Finding offsets for cicuta_virosa
Contents
10 Feb 2021 @ModernPwner dropped a surprise
on the iOS jailbreak community by sharing an
open source PoC1 of a kernel-level LPE exploit, dubbed cicuta_virosa
,
based on a vulnerability patched only recently in iOS 14.4.
Here I’ll share how I adapted the PoC to work iPhone X on iOS 14.0.1.
Credits
First, I’d like to thank @ModernPwner for sharing the exploit PoC, @_simo36 for releasing great Ghidra tools and information on reverse engineering iOS kernelcache2, and @Morpheus for jtool23 and the incredible *OS Internals book series4.
Update 02-14 - prior work
Offsets I found as a part of this exercise had also been previously discovered by
@XsF1re.
@halo_michael opened a
pull request to
add those and more to the PoC, so you can try it directly thanks to their
contribution. I wasn’t aware of that at the time of writing, and would like to
also thank
@janseredynski for letting me know!
Trying out the PoC
When the exploit dropped, people in my Twitter timeline started sharing logs of
gaining root access using the PoC, so I was eager to try it out myself.
I grabbed the sources from github
and as I browsed through some of the code in cicuta_virosa.c
I noticed
the following part:
|
|
My testing device is iPhone X with the A11 chip, so it seemed like I would need to
adapt parts of the code to make it work. I still went ahead and tried running
it, but as expected, the device has restarted, and I didn’t get to see the
satisfying whoami: root
log entry.
There were only a few more lines of code following the offsets comment, and further stripping some implementation details, it was relatively straightforward to figure out what was going on.
|
|
The snippet above is accessing internal kernel structures to overwrite the
effective process privileges. The offsets are 0x3A0
and 0xF0
, and as
they point to fields of internal kernel structures, they can differ between
devices and iOS versions.
What offsets?
I’m not intimately familiar with iOS exploits, but knowing XNU has multiple
personalities with the Mach and BSD layers, I intuitively connected task
and proc
identifiers from the PoC sources with the Mach task abstraction and BSD processes.
It became clear what I had to do was figure out what are the offsets of
the relevant kernel structure fields for my device. My first step in doing that
was to get XNU sources for macOS5. I knew that even if I find the exact
structures, they will be most likely different from iOS, but I wanted to use it
as a starting point. So I just went straight in started grepping for some
keywords related to task
, proc
and cred
, and after a bit of searching,
found the information I was looking for:
|
|
It wasn’t immediately evident that bsd_info
field was the proc structure, but
that quickly became clear after further jumping through some XNU code and
finding tons of places with
(struct proc*)task->bsd_info
type of casts. As I grepped through the code,
I also tried to take notice of some APIs that were accessing the specific fields
as those could be useful later on trying to find the offsets.
Matching source with iOS kernel
Earlier I mentioned I used macOS XNU sources as an entry point and didn’t expect to use them out of the box. That became even more apparent after seeing how the structures were defined, where some fields were configurable with macros. The next step was to match what I found so far in XNU sources with actual kernel code on iOS.
I knew already iOS uses a single kernelcache file where the core kernel code
and kernel extensions are located, so I started looking online for some resources
on how to get my hands on those and how they can be reversed.
I quickly came across an awesome Ghidra framework2 for reverse engineering
iOS kernel cache. The framework provides a straightforward usage guide and
integrates jtool2
3 kernelcache symbolication feature so that some kernel
symbols are directly visible in Ghidra.
Now that I had the kernelcache disassembled with some symbol locations, my plan of
attack was to look into macOS XNU sources for the symbols that jtool2
located
and find which ones are accessing directly or are using APIs that access
the task.bsd_info
and proc_p_ucred
fields. Some of my entry points
were getter-like APIs such as get_bsdtask_info
or kauth_cred_proc_ref
,
and larger functions that directly accessed the fields.
It took a bit of time to find my way around all the different APIs and effectively
guessing at what to look at next, and I stumbled into a trap. Some symbols marked
by jtool2
were, what it seemed like, located incorrectly as Ghidra control flow
analysis didn’t match the number of function arguments that I saw in XNU sources,
and functions logic looked entirely different in several places. One such tricky
example was that I had two marked symbols, task_for_pid_posix_check
and mac_execv
,
where the latter called the former, but that didn’t match the macOS source code.
Initially, I thought maybe there were some larger differences between iOS and macOS
implementation, but then I noticed the symbolication wasn’t totally right.
The disassembled code in Ghidra was:
|
|
While in XNU source code the extended virtual addressing entitlement was used as
an argument of a call to IOTaskHasEntitlement
, called indirectly
by __mac_execve
, like this:
|
|
In the end, however, I was on the right track. IOTaskHasEntitlement
first argument
was the task pointer, so I followed in XNU sources what exactly it was doing, and
I ended up quickly finding IOUserClient::copyClientEntitlements(task_t task)
function, which at the start used the get_bsdtask_info
API.
I followed the same path in Ghidra starting from the incorrectly marked symbol
task_for_pid_posix_check
and found the bsd_info
offset to be 0x390
.
mac_execv
also called the other symbol often used through the XNU codebase
kauth_cred_proc_ref
, and while it wasn’t symbolicated by jtool2
it was the first
function called in mac_execv
. In Ghidra I saw what I suspected to be
kauth_cred_proc_ref
return a value at offset 0xf0
from the first
(struct proc*
) parameter, which is also the offset used in the PoC.
Last step was to try newly found bsd_info
offset, and it was a success. 🎉
|
|
The only change required to the PoC was the bsd_info
field offset:
|
|
The idea of exploits and their general complexity can be intimidating, but it is a fun exercise, and there’s no need to fully grasp them to make some additions and learn new things. In the future, I hope to write more about iOS vulnerabilities and jailbreak related subjects.
Thanks for reading! If you’ve got any questions, comments, or general feedback, you can find all my social links at the bottom of the page.