wInd3x, the iPod Bootrom exploit 10 years too late
This can be a write-up of the wInd3x bootrom exploit affecting iPod Nanos from third gen to fifth gen. A ready-made software to make use of this exploit is obtainable at github.com/freemyipod/wInd3x.
Background
The 12 months is 2020. The Finish Instances are nigh. I uncover a humorous Australian man on YouTube by the title of Dankpods.
“Hey, iPods are kinda neat! I wanna run Doom on them.”, I mentioned to myself.
I rapidly realized that whereas rather a lot these outdated iPods had been ‘liberated”https://q3k.org/”hacked’ (that means: you may run your personal software program on them), plenty of them nonetheless weren’t. Which means, on the time, there was no public technique to get code execution on any system newer than the iPod Nano 4G.
I discovered this a bit shocking. Unacceptable, even. I imagined the safety on these units should have been totally damaged, much like older iPhones (see: checkm8 vuln).
I imply, might I perhaps simply port some outdated iPhone exploits to the iPods? Aren’t they like some form of proto-XNU or one thing? Even when not, there should be tremendous hackable, proper?
iPod Tech Stack
Nicely, not fairly. Let’s evaluation:
Working system: as an alternative of working iOS/XNU, the iPods run a customized embedded RTOS that does not also have a well-documented title. It is primarily based across the RTXC microkernel, with an UI stack supplied by ‘Pixo’, an organization Apple acquired quickly within the product lifecycle. The nice factor is that this isn’t a security-oriented OS and it is more likely to be filled with bugs. The dangerous new is that there is mainly no analysis into it, no recognized bugs, nothing.
Boot chain: Every part’s encrypted and signed (we’ll get into that later). The on-die BootROM (much like the BootROM present in early iPhones and iPod touches) chains right into a second stage bootloader in NOR/NAND, which in flip units up drivers for units and boots a remaining payload, be it the primary OS or disk mode or diagnostics. Curiously, the second stage bootloader (and among the payloads, just like the diag software) are primarily based round EFI firmware volumes / drivers. however that is a subject for one more time.
{Hardware}: Early units used PortalPlayer SoCs. The units I am interested by (Nano 3G+) use Samsung S5L87xx SoCs, that are an in depth relative of the S5L89xx early iPhone SoCs. The S5L8720 is used each within the iPod Nano 4G and the iPod Contact 2G, however has a distinct BootROM. There exist no publicly obtainable datasheets of any of those SoCs.
Boot Safety Chain
The standard boot circulation of the system is as follows:
.---------. | BootROM | '---------' | | Verifies V .-------------------------. | Second stage bootloader | | (NAND/NOR) | '-------------------------' | | Selects and Verifies |'-------.-------------. V V V .--------. .-----------. .--------. | OS | | Disk Mode | | Diags | | (NAND) | | (NAND) | | (NAND) | '--------' '-----------' '--------'
The BootROM performs sufficient {hardware} deliver up to have the ability to load a second stage from a plain sector of NAND/NOR (relying on the technology of the system), then the second stage bootloader brings up a bunch of different peripherals just like the LCD/DMA/DRAM/VICs…, shows the apple brand, and permits the person to pick out the firmware to be loaded (most important firmware, disk mode, or diagnostics) primarily based on keypresses. The firmware is loaded from NAND, however this time going by a full-blown Flash Translation Layer.
Every one of many levels being loaded (besides the BootROM itself) is wrapped in an Image1, which was additionally utilized in early iPhone units. This format has the next safety properties:
- The header (excluding its signature discipline) is hashed with SHA1 and the result’s encrypted with a fused per-device-generation key. The result’s in contrast in opposition to the signature discipline. Thus, the header is signed.
- One other discipline within the header determines whether or not the physique is decrypted utilizing the identical key. That is true for all firmware payloads noticed within the wild.
- One other discipline within the header determines whether or not the physique is signed utilizing an X509 PKI system, with the fingerprint hardcoded within the earlier boot stage. That is true for all firmware payloads noticed within the wild, and is obligatory on units newer than the Nano 3G.
Issues look barely totally different while you need to carry out restoration on the system:
.---------. | BootROM | '---------' | | Verifies V .-----------. | WTF | | (USB DFU) | '-----------' | | Verifies V .--------------------. | Restoration Disk Mode | | (USB DFU) | '--------------------'
As you’ll be able to see, when doing restoration, we have now an analogous break up into secondary bootloader stage and firmware, however as an alternative of loading the following stage from native storage, we load it over USB, utilizing the DFU protocol. The second stage bootloader can also be known as ‘WTF’, for unknown causes (its most important distinction from the usual second stage bootloader is that it defaults to loading the following stage over USB DFU, too).
Now, you would possibly already spot an issue right here. We’re working a full-blown USB stack and DFU protocol implementation in early bootloader levels, together with the bootrom. That is Typically A Dangerous Concept, with many programs’ safety falling to shoddy USB implementations working on the highest privilege ranges. Put a pin in that.
Prior Artwork
In fact, I wasn’t the primary particular person trying to liberate these units.
First, there was iPodLinux, a port of uClinux (now Linux nommu) to outdated PortalPlayer-based iPods. These people paved the best way in the direction of the preliminary analysis on the file codecs concerned within the iPod firmware. Nevertheless, issues obtained kinda caught when iPods moved over to Samsung-based chips (with the discharge of the iPod Nano 2 and iPod ‘Traditional’).
Then there was Linux4Nano, now Freemyipod, a collective of individuals trying to get code exec on the S5L-based units. In collaboration with the model new iPhone hacking scene, they managed to search out and exploit a however known as ‘Pwnage 2.0’, one other bootrom bug, however within the X509 parsing stack. They then reverse-engineered sufficient of the units to permit for a Rockbox port to occur for the iPod Nano 2, and the iPod basic. The Pwnage 2.0 bug labored all the best way as much as the iPod Nano 4G, then it obtained patched by Apple. One other bug, within the Notes utility, additionally allowed for code exec as much as the iPod Nano 4G, this time on the firmware stage.
And now, me. To summarize, I had entry to the next:
- Code execution on the Nano 2G (Samsung S5L8701), Nano 3G / iPod Traditional (Samsung S5L8702), Nano 4G (Samsung S5L8720) because of the Pwnage 2.0 bug.
- BootROM dumps from the above units
- Decrypted firmware for the above units
- Some reverse engineering notes left behind by earlier generations of hackers.
Every part from the Nano 5G up was mainly totally sealed. No firmware dumps, no code execution. Nothing to actually reverse engineer. I made a decision the perfect plan of action can be to discover a new vulnerability within the Nano 4G, and port it to later units.
So I made a decision to search out new bugs within the Nano 4G BootROM
Reverse-engineering the BootROM
The simple half was getting began: simply take a binary dump, load it at handle 0x2000_0000, and begin disassembling on the first bytes.
Making sense of what was occurring was a bit extra difficult. The BootROM is sort of small with mainly no human-readable strings to make use of as steerage. Fortunately, I had some reverse-engineered register/reminiscence space notes from the earlier generations of hackers, so I might rapidly discover out that early reminiscence pokes had been issues like configuring the PLL and clock tree. A number of different ‘anchors’ was code to allow/disable clock gates, schedule AES decryption utilizing the built-in AES peripheral, mainly any MMIO register pokes was a very good place to begin documenting issues.
Quickly sufficient, I recovered many of the most important performance of the BootROM. Naturally, all of the image/struct names under are my very own, because the BootROM binary was simply that, a binary stripped of all its authentic symbols.
void boot(void) { undefined4 spino; char *cnCA; DFUBoot dfuBoot; State *state = g_State; g_State->vtable = &StateVTable; /* Unknown a part of CHIPINFO, bits [3:0]. */ CHIPINFO chipinfo = read_volatile_4(CHIPID_INFO); uint chipinfo_unk = (uint)((int)chipinfo << 0x1c) >> 0x1e; state->chipinfo_unk = chipinfo_unk; if (chipinfo_unk == 1) { cnCA = "/CN=Apple Safe Boot Certification Authority"; } else { /* That is in prod certs. */ cnCA = "/CN=Apple iPod Certification Authority"; } state->cnS5L8720SecureBoot = "/CN=S5L8720 Safe Boot"; state->cnCertificationAuthority = cnCA; gstatus_set(0,0); int disconnected = boot_otg_try_connect_dfu(); if (disconnected != 0) goto dfu; gpio_configure_input(3,5,0); gpio_configure_input(3,6,0); gpio_configure_input(3,7,0); int gpio5 = gpio_read(3,5); int gpio6 = gpio_read(3,6); int gpio7 = gpio_read(3,7); change(gpio7 | gpio5 << 2 | gpio6 << 1) { case 0: case 2: spino = 0; break; case 1: case 3: spino = 1; break; case 4: case 5: case 6: boot_nand(); default: goto dfu; } boot_spi(spino); dfu: gpio_configure_unk(0xc,3,1); gpio_set_bit(0xc,3,1); (*g_State->vtable->DFUBootDFUBoot)(&dfuBoot); /* setup dfuBoot... */ (*g_State->vtable->DFUBootSetup)(&dfuBoot); (*g_State->vtable->DFUBootSetupUSB)(); (*g_State->vtable->DFUBootRun)(); return; }
You possibly can see how the BootROM decides, primarily based on some GPIO straps (and sure GPIO comms from some PMIC or the clickwheel) besides over NAND, NOR(SPI) or over USB.
Inside every boot methodology handler, there can be some calls to confirm the integrity of the loaded IMG1. For instance, in boot_nand:
undefined4 boot_nand(void) { clkgen_enable_gate(5); clkgen_enable_gate(9); nand_power_up_maybe(); nand_reset(0); nand_read_maybe(0,0,(int)&hdr); bool bVar1 = (*g_State->vtable->verify_img_header)(&hdr,AES_KEY_TYPE_GLOBAL); if (((bVar1 != false) && (hdr.field2_0x7 == ASYMMETRIC || hdr.field2_0x7 == ASYMMETRIC_ENCRYPTED)) && (hdr.bodySize int iVar2 = (*g_State->vtable->verify_decrypt_image)(&hdr,&g_IMG_Payload,2); if (iVar2 != 0) { offset = g_State->img_header_jump_offset; gstatus_set(3,0); prepare_and_jump((int)&g_IMG_Payload + offset); } } return 0; }
You possibly can see a NAND web page (?) learn, adopted by a name to verify_img_header which ensures the header passes the AES(SHA(header), fused_key) == header.signal verify. After that, we load a bunch of extra NAND pages of the physique of the IMG1, and eventually we name verify_decrypt_image on the whole thing of the loaded picture. That in flip performs a signature checking of the physique, this time utilizing X509. The loaded payload is predicted to have a sound certificates chain appended to its finish.
That X509 codepath above is the place ‘Pwnage 2.0’ lives. It is a particularly dumb vulnerability in a fairly intelligent ASN.1/DER parser. See the linked Wiki article for extra particulars. For sure, I did look across the X509 parsing code for extra bugs, however I did not actually discover something. Among the cert chain logic is sort of bushy although, so perhaps another person may have extra luck :).
There’s a lot extra code within the BootROM, and this was largely simply an instance of how such a codebase can appear to be while you spend dozens of hours pouring over it. This nonetheless is not excellent, but it surely’s sufficient to really search for bugs.
The bug: USB wIndex == magic
Let’s get on with it. What is the bug I ended up discovering? A particularly trivial vulnerability within the USB stack, after all. It was so trivial in actual fact that I missed it after I was doing a primary move over the codebase.
Deep inside a forest of callbacks, ‘ops’ buildings and magical register pokes to a Synopsys USB OTG peripheral, we discover a operate which truly parses a acquired SETUP packet:
void USB::HandlePendingSetup(void) { usb_device_request *req; USBHandler *fptr; uint index; byte bmRequestType; USBState state; if (g_State->ep0state != SETUP) { return; } req = (usb_device_request *)g_State->ep0_dma; state = g_State->usbState; /* INIT or CONFIGURED */ if (stateThe necessary half is that g_state->ep0_dma is the reminiscence buffer configured to be populated with a USB packet acquired on Endpoint 0. That's, these are straight managed by the attacker. The code assumes that is USB SETUP packet, xtracts the bmRequestType discipline from it and dispatches on it:
bmRequestType = req->bmRequestType; if ((bmRequestType & 0x60) == 0) { /* Sort == Customary */ EP0OutSetupStandard(req); PrepareRecvBuf(); return; } if ((bmRequestType & 0x60) == 0x20) { /* Sort == Class */ [...] } else if ((bmRequestType & 0x60) == 0x40) { /* Sort == Vendor */ [...] } else { goto LAB_20004e18; }Fairly regular stuff. Test if it is a Customary request, and in that case, deal with that by a name to EP0OutSetupStandard. If it is a Class or Vendor request sort, deal with that too. In any other case, stall and fail. Let's zoom into that Class handler:
index = (uint)req->wIndex[0]; if (state usbHandlers[index].handlerClass; goto joined_r0x20004d9c; } if ((bmRequestType & 3) == 1) { if (index == 0) { fptr = (USBHandler *)g_State->usbHandlers[0].handlerClass; goto joined_r0x20004d9c; } } [...]There it's. The bug's proper there. Are you able to see it?
That is proper. If (bmRequestType & 0x3) == 0, the user-controlled 'index' (populated from the decrease byte of the wIndex discipline of the SETUP packet) is used as an index into g_State->usbHandlers with none boundary checks. Then, the results of that index is executed as a operate (not proven right here). Oops. The identical buggy codepath exists within the Vendor handler, too.
And certainly, if we ship a crafted USB SETUP packet to the BootROM in DFU mode with bmRequestType set to 0x20 (passing the 2 checks above) and wIndex set to 0xff00, we crash the BootROM on the Nano 4G and... Nano 5G! That is a brand new system on which we by no means had code exec earlier than! However let's first attempt to exploit this on the Nano 4G, as we have now the BootROM dump for that.
Exploiting the Nano 4G
At this level we have to familiarize ourselves a bit extra with the State construction and its usbHandlers member.
typedef struct { uint stuff[0x15]; USBInterfaceHandlers usbHandlers[1]; // different issues afterwards... } State; typedef struct { USBInterfaceDescriptor *interfaceDescriptor; void *onSetConfiguration; void *unk; void *onSynchFrame; void *handlerClass; void *handlerVendor; } USBInterfaceHandlers;So successfully, what we will do, is deal with among the fields after usbHandlers in State as a code pointer to leap to. Doing a little math, these are offsets 0x64 + (0x1c * n) and 0x68 + (0x1c * n) for N in 0..255.
I am going to spare you the main points, however what I discovered was the next: for those who set wIndex to three and ship a Class request, we'll deal with the uint at offset 184 within the State construction as a code pointer and execute it. And in that offset in State, there is a counter I known as ep0_txbuf_offs.
The BootROM DFU cannot solely obtain a picture to run on the system, but in addition ship what it at present has in its buffer. The implementation of it's truly damaged, however for those who first request N bytes of the picture, then day trip the learn from the host aspect, ep0_txbuf_offs will include N-0x40 (ie. the quantity of knowledge left to ship, minus the primary packet dimension).
There's some dimension limits in place, however by scheduling a firmware ship from the system, after which sending bmRequest=0x20 and wIndex=3, we will get the system to execute at any handle from 0x000 to 0x600. If you understand your ARM units, you will additionally know that these low addresses should include an interrupt vector for the CPU. That often means most units will mirror their present execution medium (on this case the BootROM) into these low addresses, and that is additionally the case right here.
So, we will execute addresses 0x000 to 0x600 from the BootROM... what now? Nicely, underneath handle 0x3b0 we discover the next:
200003b0 30 ff 2f e1 blx r0This good little gadget is successfully a trampoline that can proceed executing from no matter handle is in r0, the primary register of the ARM CPU. This register can also be used, in the usual ARM ABI, to carry the primary argument passsed to a operate. And fortuitously, in USB::HandlePendingSetup, the corrupt fptr is definitely known as with an argument: ep0_dma.
Nicely that is enjoyable! By scheduling a learn of 0x3b0+0x40, then performing the wInd3x bug, we'll begin executing the USB SETUP packet as ARM code!
This implies it is time for me to introduce to you to my favorite a part of the exploit chain: a polyglot ARM shellcode and USB packet:
0x20 0xfe 0xff 0xea 0x03 0x00 0x00 0x00When parsed as a USB SETUP packet, it has a bmRequsetType of 0x20, a bRequest of 0xfe, a wValue of 0xffea, and a wIndex of three. This implies it triggers the wInd3x bug above.
When parsed as ARM code executing from 0x2202e300 (the place ep0_dma lives), it is:
$ rasm2 -a arm -b 32 -o 0x2202e300 -D "20feffea03000000" 0x2202e300 4 20feffea b 0x2202db88 0x2202e304 4 03000000 andeq r0, r0, r3By the way, 0x2202db88 is 136 bytes into the DFU buffer, which is just about 'limitless' in dimension and really simply managed. We now have steady code exec!
With this, we will begin sending the system some 'shell'code. On of the earliest issues I wrote was proof of idea 'send-memory-region-over-USB'. In a future article, we'll talk about extra sensible payloads, however for now let's give attention to the explanation we're even right here:
Exploiting the Nano 5G
We're now in The Cool Zone. We experimentally confirmed the Nano 5G BootROM is susceptible to wInd3x as a consequence of a crash, however we have now completely no BootROM dumps to really craft our exploit as simply as for the Nano 4G. Simply making an attempt the identical payload would not work both (as anticipated, the offsets are in all probability all totally different as a consequence of a recompilation of the BootROM).
First I needed to verify if the primary a part of the exploit, leaping into the 0x000..0x600 reminiscence by controlling ep0_dma nonetheless labored. I used to be capable of provide you with a easy 'oracle' check: if ep0_dma is 0, then we must always soar to handle 0x000 which is the reset vector, which ought to mainly restart DFU mode. Nevertheless, if I set it to 4, then it ought to soar to 0x004 which is the invalid instruction handler, which I do know the Nano 4G BootROM carried out as an infinite loop. That labored, so I knew I might management that a part of the execution.
Now, my objective was to discover a working 'blx r0' gadget someplace within the first 0x600 bytes. So I bruteforced that, and saved notes:
# 374: caught # 378: returns # 37c: restartOne handle popped out as having a distinct behaviour: 0x37c. Every part else both crashed the system (and prompted it to be caught in an infloop) or had no impact (possible hitting a block of code ending with a mov laptop, lr). I might now work on my USB/ARM polyglot payload. To my shock, it labored out of the field: relying on whether or not I populated the DFU buffer with 'loop: b loop' or 'bl #0x0' directions, I obtained totally different behaviour.
Now, I might execute code, however I had actually no technique to get any outcome out from it, as I did not know the handle of the USB ship buffer to inject information into. Nicely, nearly no approach. I might in any case, both get the system caught (by doing an infloop), or restart it (by leaping to 0). Meaning I might leak, one bit at a time, some information. So I wrote some janky ARM payloads that will calculate a bit from somwhere (eg. searching for some reminiscence sample, then leaking the Nth little bit of the discovered handle) that allowed me to very slowly leak some details concerning the BootROM, like the place the buffers lived.
... however ultimately, it seems the buffer addresses for the Nano 5G had been precisely the identical as for the Nano 4G. So I might just about simply re-use my outdated code as was, the one distinction being 0x37c as an alternative of 0x3b0 because the 'blx r0' trampoline handle :).
What have we realized?
Do not put a C implementation of a USB stack in your BootROM, lol.
What's subsequent?
Nicely, the exploit is fairly steady. I've construct up some extra tooling on prime of the bug (topic to a future article right here) that permits me besides custom-made firmware over USB.
I've additionally made some progress on porting Linux and U-Boot to the Nano 5G:
U-Boot 2023.01-rc4-q3k-00055-g7de7e65add (Jan 01 1980 - 00:00:00 +0000) CPU: Samsung/Apple S5L8730 Mannequin: Apple iPod Nano 5G DRAM: 64 MiB Core: 5 units, 5 uclasses, devicetree: separate MMC: Loading Surroundings from nowhere... OK In: serial@3cc00000 Out: serial@3cc00000 Err: serial@3cc00000 Web: No ethernet discovered. => dfu 0 ram 0 s5l87xx_lcd_init: detected LCD sort 38f7 (2) s5l87xx_otgphy: turning on s5l87xx: ungating usb-otg s5l87xx: ungating usb2-phy #DOWNLOAD ... OK Ctrl+C to exit ... s5l87xx_otgphy: turning off => bootm ## Booting kernel from Legacy Picture at 08000000 ... Picture Identify: Picture Sort: ARM Linux Multi-File Picture (uncompressed) Information Dimension: 7189312 Bytes = 6.9 MiB Load Handle: 08000000 Entry Level: 08000000 Contents: Picture 0: 5722624 Bytes = 5.5 MiB Picture 1: 1465230 Bytes = 1.4 MiB Picture 2: 1440 Bytes = 1.4 KiB Verifying Checksum ... OK ## Loading init Ramdisk from multi element Legacy Picture at 08000000 ... ## Flattened Gadget Tree from multi element Picture at 08000000 Booting utilizing the fdt at 0x086dade0 Working FDT set to 86dade0 Loading Multi-File Picture WARNING: legacy format multi element picture overwritten Loading Ramdisk to 0ae1c000, finish 0af81b8e ... OK Loading Gadget Tree to 0ae18000, finish 0ae1b59f ... OK Working FDT set to ae18000 Beginning kernel ... [ 0.000000] Booting Linux on bodily CPU 0x0 [ 0.000000] Linux model 6.2.0-rc3-00024-ge2e3252e9e4e (q3k@mimeomia) (arm-none-eabi-gcc (GNU Arm Embedded Toolchain 10.3-2021.10) 10.3.1 20210824 (launch), GNU ld (GNU Arm Embedded Toolchain 10.3-2021.10) 2.36.1.20210621) #15 Thu Jan 12 23:37:12 CET 2023 [ 0.000000] CPU: ARMv6-compatible processor [410fb764] revision 4 (ARMv7), cr=00c5387d [ 0.000000] CPU: PIPT / VIPT nonaliasing information cache, VIPT nonaliasing instruction cache [ 0.000000] OF: fdt: Machine mannequin: Apple iPod Nano 5G [...] [ 1.520000] Liberating unused kernel picture (initmem) reminiscence: 1024K [ 1.530000] Run /init as init course of Beginning syslogd: OK Beginning klogd: OK Operating sysctl: OK Saving random seed: [ 2.160000] random: dd: uninitialized urandom learn (32 bytes learn) OK Beginning community: OK Welcome to Buildroot buildroot login: #There's nonetheless some issues to determine although for a Totally Liberated iPod Expertise:
- Untethering code execution on the Nano 5G: at present we at all times need to run our personal code over USB.
- Getting code execution on the Nano 6G and 7G, for instance by discovering a bug within the firmware. They don't seem to be prone to wInd3x.
- Reverse-engineering extra peripherals for the Nano 5G, and ending the Linux port.
- Writing a Good implementation of the Whimory FTL used within the iPod Nano 3G+.
- A Rockbox port for the Nano 3G, 4G and 5G.
Should you're curious in seeing common updates (and even need to assist!), be part of us on the #freemyipod channel on Libera.chat, or on #freemyipod:hackerspace.pl on Matrix. You may also try the (largely not lifeless) Freemyipod Wiki, or check out the wInd3x tool itself.
And as a prize for making it all through this drivel, I am going to depart you with a remaining curiosity: wInd3x additionally impacts the iPhone 3G (and possibly the unique iPhone), however I wasn't but capable of chain collectively an exploit for them. Wanna be part of the Ineffective Outdated Gadget 0dayz Scene? 🙂
Copyright 2023 Serge Bazanski. This work is licensed underneath a Creative Commons Attribution 4.0 International License.