Hunting for iOS Kernel Symbols
Last week, Ian Beer of Google’s Project Zero announced on Twitter that he’d be releasing something to help anyone looking to bootstrap iOS 11 kernel security research by providing read/write access to kernel memory via
While a lot of the excitement around this announcement was for the potential for a new jailbreak, I wanted to use this as an opportunity to spend time with mobile security again. Most of my experience in the past has been with app level testing, but I wanted to get down into the the nitty gritty of the OS itself.
With the promise of a toolkit from Jonathan Levin “for those people who end up with a send right to the kernel_task port (a.k.a tfp0) in their process, but don’t know what to do next” as well, this seemed like a great chance to dive in.
Mr. Beer’s vulnerability announcement and PoC code was released yesterday, and it was beautiful. Extremely well documented (ASCII art stack frame example included!), clean, and relatively easy to start reading and reasoning through. I was ready to get started with my testing, but ran into one quick snag. Mr. Beer was gracious enough to include kernel symbols for several devices, but not the one I was planning to use. Luckily as part of that well documented code, he included detailed hints for how to find the symbols, so I was ready for the challenge.
The rest of this post walks through my process for finding the symbols, in the hope that it may also be useful for you to add support for your device. Outside of the symbols that weren’t stripped, I did not find any other documentation for how to do this, so I wanted to document this process for others (and myself in the future). Depending on what’s in Jonathan Levin’s toolkit, this may not be necessary, but in my opinion is a fun learning exercise regardless.
The first step for finding the symbols is getting the
kernelcache for the target iOS version. I hopped over to https://ipsw.me/ and downloaded the
11.1.2 firmware for both my iPad Mini 2, as well as for one of the devices that already had symbols defined in
symbols.c. Having an already defined firmware on hand made it extremely helpful to confirm that the processes I used to find each symbol was correct, since we already knew what the right symbol should be for that device.
After un-zipping the
.ipsw files, I grabbed Mr. Levin’s tool Joker. After playing around with the options for a bit, I pointed it at the
kernelcache file with the
-m options to dump out all the available symbols. The output contained the addresses for the following symbols I needed:
9 down, 10 to go. The rest must have been stripped (or never labeled in the first place, like the JOP gadget) so it was time to load up the
kernelcache files in IDA Pro. You can most certainly use another disassembler like Binary Ninja, Hopper, or radare2 for this as well.
Before we could do that, we need to decode the
kernelcache file. I found this guide from Joshua Wright to get the file ready.
* open kernelcache in a hex editor and look for 0xFFCFFAEDFE, note the offset (435)
* wget -q http://nah6.com/%7Eitsme/cvs-xdadevtools/iphone/tools/lzssdec.cpp
* g++ -o lzssdec lzssdec.cpp
* ./lzssdec -o 435 < kernelcache >kernelcache.dec # 435 is offset byte count to 0xFFCFFAEDFE header
First I loaded up IDA with the kernelcache for the firmware with known symbols so I could get an idea of what the assembly looked like for the symbols I still had to find.
The easiest to find next was
KSYMBOL_RET. Jumping to the address for the known symbol, I saw it was the
RETinstruction from the
_kalloc_external function. So it was easy enough to load up my target device kernelcache in IDA and find
RET in the same function. For the rest, we’d need to rely heavily on the hints provided in the comments.
Next up was
KSYMBOL_CPU_DATA_ENTRIES, which the hint says is
0x6000 into the data segment. In IDA, I went to “Jump to Segment” and went to the start of
_data segment, noted the address and did some hex math to add
0x6000 and get the address I needed.
The next two I found were actually the last two in the list,
KSYMBOL_SLEH_SYNC_EPILOG. I opened the Strings Window in IDA (Shift+F12) and searched for the strings. Double clicking them lead me to the reference.
For the first one, I scrolled down to find switch case 49 and grabbed the address.
For the second one, the address for the known symbol file was the first of several
LDP instructions below the XREF’d string.
One last easy one was the
KSYMBOL_X21_JOP_GADGET. After seeing that the instruction needed was
MOV X21, X0, I did a text search in IDA to find the gadget for my device.
The last five were the trickiest for me to find. My searches weren’t directly turning up the references mentioned in the comments, so I started to look at the addresses I found and how they compared to the addresses that were already known. Doing some basic subtraction showed they were all somewhat the same distance apart, give or take, which would at least help me narrow down the search. I also compared the distances between close known addresses. For example,
X21_JOP_GADGET were only
0x28 bytes apart.
So I looked up and recorded the assembly instructions at the known addresses, and then loaded up my device’s
kernelcache and jumped to
KNOWN_ADDRESS + my offset guess, and started looking nearby for those same instructions.
Once I was done spelunking in assembly, it was just a matter of adding to the if block to get support for my device.
And finally, plug my iPad into my laptop, open the Xcode project, and run!
The patch I made for the iPad Mini 2 WiFi is available here.
This whole process was entirely new to me (but a lot of fun!) so I welcome any feedback on things I may have gotten wrong or misinterpreted, or if you know of an easier method to find any or most of these addresses (please let there be an easier way). I love learning new tools and techniques and would be glad to hear about them and update this post.
(Edit — I had the KSYMBOL_WRITE_SYSCALL_ENTRYPOINT wrong so my previous idea of “success” wasn’t quite there! Thanks to my awesome coworker Etienne for his help fixing things!)