Here’s a write-up of the wInd3x bootrom exploit affecting iPod Nanos from third gen to fifth gen. A willing-made instrument to make employ of this exploit is on hand at github.com/freemyipod/wInd3x.
Background
The three hundred and sixty five days is 2020. The End Conditions are nigh. I look a comical Australian man on YouTube by the name of Dankpods.
“Hello, iPods are kinda orderly! I wanna bolt Doom on them.”, I mentioned to myself.
I quick realized that while plenty these worn iPods were ‘liberated”http://q3k.org/”hacked’ (that methodology: that you just may maybe well presumably bolt your occupy tool on them), a range of them silent weren’t. Which methodology, at the time, there became no public way to salvage code execution on any design more moderen than the iPod Nano 4G.
I came upon this just a shrimp just. Unacceptable, even. I imagined the protection on these devices must get hang of been completely damaged, identical to older iPhones (discover: checkm8 vuln).
I mean, may maybe well well I presumably factual port some worn iPhone exploits to the iPods? Will no longer be they fancy some invent of proto-XNU or something? Even if no longer, there ought to be suitable hackable, generous?
iPod Tech Stack
Effectively, no longer quite. Let’s review:
Working design: rather than working iOS/XNU, the iPods bolt a personalized embedded RTOS that would no longer even get hang of a smartly-documented name. It is based mostly around the RTXC microkernel, with an UI stack equipped by ‘Pixo’, a firm Apple obtained quickly within the product lifecycle. The positive thing is that right here is no longer a security-oriented OS and it’s seemingly to be plump of bugs. The unsuitable contemporary is that there’s genuinely no research into it, no known bugs, nothing.
Boot chain: All the pieces’s encrypted and signed (we will salvage into that later). The on-die BootROM (identical to the BootROM show camouflage in early iPhones and iPod touches) chains precise into a 2nd stage bootloader in NOR/NAND, which in turn devices up drivers for devices and boots a closing payload, be it the fundamental OS or disk mode or diagnostics. Curiously, the 2nd stage bootloader (and one of the major payloads, fancy the diag instrument) are based mostly around EFI firmware volumes / drivers. nonetheless that is a topic matter for one other time.
Hardware: Early devices worn PortalPlayer SoCs. The devices I am focused on (Nano 3G+) employ Samsung S5L87xx SoCs, which may maybe well be a conclude relative of the S5L89xx early iPhone SoCs. The S5L8720 is worn both within the iPod Nano 4G and the iPod Touch 2G, nonetheless has a special BootROM. There exist no publicly on hand datasheets of any of these SoCs.
Boot Security Chain
The identical earlier boot drift of the design is as follows:
.---------. | BootROM | '---------' | | Verifies V .-------------------------. | 2nd stage bootloader | | (NAND/NOR) | '-------------------------' | | Selects and Verifies |'-------.-------------. V V V .--------. .-----------. .--------. | OS | | Disk Mode | | Diags | | (NAND) | | (NAND) | | (NAND) | '--------' '-----------' '--------'
The BootROM performs sufficient hardware bring up to be able to load a 2nd stage from a undeniable sector of NAND/NOR (reckoning on the generation of the design), then the 2nd stage bootloader brings up a bunch of completely different peripherals fancy the LCD/DMA/DRAM/VICs…, shows the apple logo, and permits the patron to to find the firmware to be loaded (predominant firmware, disk mode, or diagnostics) in step with keypresses. The firmware is loaded from NAND, nonetheless this time going thru a plump-blown Flash Translation Layer.
One and all in all the phases being loaded (with the exception of the BootROM itself) is wrapped in an Image1, which became additionally worn in early iPhone devices. This format has the next security properties:
- The header (with the exception of its signature field) is hashed with SHA1 and the conclude result is encrypted with a fused per-design-generation key. The conclude result is in comparison in opposition to the signature field. Thus, the header is signed.
- One other field within the header determines whether or no longer the physique is decrypted the employ of the identical key. Here’s genuine for all firmware payloads seen within the wild.
- One other field within the header determines whether or no longer the physique is signed the employ of an X509 PKI design, with the fingerprint hardcoded within the earlier boot stage. Here’s genuine for all firmware payloads seen within the wild, and is fundamental on devices more moderen than the Nano 3G.
Issues behold just a shrimp of completely different whenever you happen to must create recovery on the design:
.---------. | BootROM | '---------' | | Verifies V .-----------. | WTF | | (USB DFU) | '-----------' | | Verifies V .--------------------. | Recovery Disk Mode | | (USB DFU) | '--------------------'
As you are going to even discover, when doing recovery, now we get hang of a identical cleave up into secondary bootloader stage and firmware, nonetheless rather than loading the next stage from native storage, we load it over USB, the employ of the DFU protocol. The 2nd stage bootloader is additionally called ‘WTF’, for unknown causes (its predominant distinction from the regular 2nd stage bootloader is that it defaults to loading the next stage over USB DFU, too).
Now, you are going to already build a instruct right here. We’re working a plump-blown USB stack and DFU protocol implementation in early bootloader phases, along with the bootrom. That is Basically A Low Concept, with many methods’ security falling to shoddy USB implementations working at the very top privilege phases. Put a pin in that.
Prior Artwork
Of path, I wasn’t the first person taking a glance to liberate these devices.
First, there became iPodLinux, a port of uClinux (now Linux nommu) to worn PortalPlayer-based mostly iPods. These other folks cleared the path in direction of the initial research on the file formats focused on the iPod firmware. However, issues purchased kinda caught when iPods moved over to Samsung-based mostly chips (with the starting up of the iPod Nano 2 and iPod ‘Traditional’).
Then there became Linux4Nano, now Freemyipod, a collective of of us taking a glance to salvage code exec on the S5L-based mostly devices. In collaboration with the emblem contemporary iPhone hacking scene, they managed to search out and exploit a nonetheless called ‘Pwnage 2.0’, one other bootrom computer virus, nonetheless within the X509 parsing stack. They then reverse-engineered sufficient of the devices to permit for a Rockbox port to happen for the iPod Nano 2, and the iPod classic. The Pwnage 2.0 computer virus labored your whole way as much as the iPod Nano 4G, then it purchased patched by Apple. One other computer virus, within the Notes application, additionally allowed for code exec as much as the iPod Nano 4G, this time at the firmware level.
And now, me. To summarize, I had salvage entry to to the next:
- Code execution on the Nano 2G (Samsung S5L8701), Nano 3G / iPod Traditional (Samsung S5L8702), Nano 4G (Samsung S5L8720) attributable to of the Pwnage 2.0 computer virus.
- BootROM dumps from the above devices
- Decrypted firmware for the above devices
- Some reverse engineering notes left within the help of by earlier generations of hackers.
All the pieces from the Nano 5G up became infrequently fully sealed. No firmware dumps, no code execution. Nothing to genuinely reverse engineer. I made a decision basically the most positive path of action would be to rep a contemporary vulnerability within the Nano 4G, and port it to later devices.
So I made a decision to search out contemporary bugs within the Nano 4G BootROM
Reverse-engineering the BootROM
The easy fragment became getting started: factual spend a binary dump, load it at address 0x2000_0000, and originate disassembling at the first bytes.
Making sense of what became going on became just a shrimp extra subtle. The BootROM is reasonably little with infrequently no human-readable strings to make employ of as guidance. Fortuitously, I had some reverse-engineered register/memory house notes from the earlier generations of hackers, so I could maybe well well quick discover that early memory pokes were issues fancy configuring the PLL and clock tree. A few completely different ‘anchors’ became code to permit/disable clock gates, agenda AES decryption the employ of the built-in AES peripheral, infrequently any MMIO register pokes became an even build to originate up documenting issues.
Soon sufficient, I recovered many of the fundamental functionality of the BootROM. Naturally, your whole image/struct names beneath are my occupy, as the BootROM binary became factual that, a binary stripped of all its usual symbols.
void boot(void) { undefined4 spino; char *cnCA; DFUBoot dfuBoot; Direct *teach=g_State; g_State->vtable=&StateVTable; /Unknown fragment of CHIPINFO, bits [3:0]. */ CHIPINFO chipinfo=read_volatile_4(CHIPID_INFO); uint chipinfo_unk=(uint)((int)chipinfo <<0x1c)>> 0x1e; teach->chipinfo_unk=chipinfo_unk; if (chipinfo_unk==1) { cnCA="/CN=Apple Stable Boot Certification Authority"; } else { /Here's in prod certs. */ cnCA="/CN=Apple iPod Certification Authority"; } teach->cnS5L8720SecureBoot="/CN=S5L8720 Stable Boot"; teach->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); swap(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; }
That you can be ready to search out how the BootROM decides, in step with some GPIO straps (and certain GPIO comms from some PMIC or the clickwheel) to boot over NAND, NOR(SPI) or over USB.
Internal every boot way handler, there would be some calls to verify the integrity of the loaded IMG1. As an 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 !=wrong) && (hdr.field2_0x7==ASYMMETRIC || hdr.field2_0x7==ASYMMETRIC_ENCRYPTED)) && (hdr.bodySize <0x24c01)) { uint i=0; uint depend=hdr.bodySize / 0x600; if (hdr.bodySize % 0x600 !=0) { depend=depend + 1; } if (depend !=0) { attain { nand_read_maybe(0,i + 1,(int)(&g_IMG_Payload + i 0x180)); i=i + 1; } while (iint 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; }
That you can be ready to search out a NAND web state (?) read, followed by a call to verify_img_header which ensures the header passes the AES(SHA(header), fused_key)==header.stamp verify. After that, we load a bunch of additional NAND pages of the physique of the IMG1, and sooner or later we call verify_decrypt_image on the whole lot of the loaded image. That in turn performs a signature checking of the physique, this time the employ of X509. The loaded payload is predicted to get hang of a sound certificates chain appended to its conclude.
That X509 codepath above is the build ‘Pwnage 2.0’ lives. It is an especially dull vulnerability in a quite artful ASN.1/DER parser. Learn about the linked Wiki article for extra particulars. Needless to utter, I did behold around the X509 parsing code for extra bugs, nonetheless I did no longer genuinely rep something else. A few of the cert chain good judgment is reasonably furry even when, so presumably somebody else may maybe well get hang of extra generous fortune :).
There’s plenty extra code within the BootROM, and this became mostly factual an instance of how this sort of codebase can behold fancy whenever you happen to spend dozens of hours pouring over it. This silent is rarely always genuinely expedient, nonetheless it completely’s sufficient to genuinely behold for bugs.
The computer virus: USB wIndex==magic
Let’s salvage on with it. What’s the computer virus I accomplished up finding? A namely trivial vulnerability within the USB stack, pointless to utter. It became so trivial genuinely that I overlooked it after I became doing a first circulation over the codebase.
Deep inner a forest of callbacks, ‘ops’ structures and magical register pokes to a Synopsys USB OTG peripheral, we discover a characteristic which genuinely parses a obtained SETUP packet:
void USB::HandlePendingSetup(void) { usb_device_request *req; USBHandler *fptr; uint index; byte bmRequestType; USBState teach; if (g_State->ep0state !=SETUP) { return; } req=(usb_device_request *)g_State->ep0_dma; teach=g_State->usbState; /INIT or CONFIGURED */ if (teach <3) { LAB_20004e18: EP0Stall(); PrepareRecvBuf(); return; } [...]
The important part is that g_state->ep0_dma is the memory buffer configured to be populated with a USB packet obtained on Endpoint 0. That is, these are immediately managed by the attacker. The code assumes right here is USB SETUP packet, xtracts the bmRequestType field from it and dispatches on it:
bmRequestType=req->bmRequestType; if ((bmRequestType & 0x60)==0) { /Form==Same earlier */ EP0OutSetupStandard(req); PrepareRecvBuf(); return; } if ((bmRequestType & 0x60)==0x20) { /Form==Class */ [...] } else if ((bmRequestType & 0x60)==0x40) { /Form==Seller */ [...] } else { goto LAB_20004e18; }
Pretty regular stuff. Test whether it’s miles a Same earlier expect, and if that is so, handle that by a call to EP0OutSetupStandard. Whether it’s miles a Class or Seller expect form, handle that too. In any other case, stall and fail. Let’s zoom into that Class handler:
index=(uint)req->wIndex[0]; if (teachusbHandlers[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 computer virus’s generous there. Are you able to discover it?
That is generous. If (bmRequestType & 0x3)==0, the patron-managed ‘index’ (populated from the lower byte of the wIndex field of the SETUP packet) is worn as an index into g_State->usbHandlers with out any boundary tests. Then, the conclude result of that index is accomplished as a characteristic (no longer confirmed right here). Oops. The identical buggy codepath exists within the Seller handler, too.
And certainly, if we ship a crafted USB SETUP packet to the BootROM in DFU mode with bmRequestType region to 0x20 (passing the 2 tests above) and wIndex region to 0xff00, we crash the BootROM on the Nano 4G and… Nano 5G! That is a contemporary design on which we by no methodology had code exec before! But let’s first attempt to milk this on the Nano 4G, as now we get hang of the BootROM dump for that.
Exploiting the Nano 4G
At this level now we want to familiarize ourselves just a shrimp extra with the Direct structure and its usbHandlers member.
typedef struct { uint stuff[0x15]; USBInterfaceHandlers usbHandlers[1]; // completely different issues afterwards... } Direct; typedef struct { USBInterfaceDescriptor *interfaceDescriptor; void *onSetConfiguration; void *unk; void *onSynchFrame; void *handlerClass; void *handlerVendor; } USBInterfaceHandlers;
So effectively, what we can attain, is address one of the major fields after usbHandlers in Direct as a code pointer to leap to. Doing some math, these are offsets 0x64 + (0x1c n) and 0x68 + (0x1c n) for N in 0..255.
I will spare you the particulars, nonetheless what I came upon became the next: whenever you region wIndex to some and ship a Class expect, we will address the uint at offset 184 within the Direct structure as a code pointer and elevate out it. And in that offset in Direct, there is a counter I called ep0_txbuf_offs.
The BootROM DFU can no longer most positive get a image to bolt on the design, nonetheless additionally ship what it currently has in its buffer. The implementation of it’s genuinely damaged, nonetheless whenever you first expect N bytes of the image, then day outing the read from the host aspect, ep0_txbuf_offs will occupy N-0x40 (ie. the quantity of files left to ship, minus the first packet dimension).
There’s some dimension limits in build, nonetheless by scheduling a firmware ship from the design, and then sending bmRequest=0x20 and wIndex=3, we can salvage the design to elevate out at any address from 0x000 to 0x600. While you happen to know your ARM devices, you are going to additionally know that these low addresses must occupy an interrupt vector for the CPU. That on the final methodology most devices will deem their newest execution medium (in this case the BootROM) into these low addresses, and that’s the reason additionally the case right here.
So, we can elevate out addresses 0x000 to 0x600 from the BootROM… what now? Effectively, beneath address 0x3b0 we discover the next:
200003b0 30 ff 2f e1 blx r0
This positive shrimp system is effectively a trampoline that can continue executing from with out reference to address is in r0, the first register of the ARM CPU. This register is additionally worn, within the regular ARM ABI, to defend up the first argument passsed to a characteristic. And fortuitously, in USB::HandlePendingSetup, the sinful fptr is infrequently called with an argument: ep0_dma.
Effectively that is stress-free! By scheduling a read of 0x3b0+0x40, then performing the wInd3x computer virus, we will originate up executing the USB SETUP packet as ARM code!
This means it’s time for me to introduce to you to my favourite fragment of the exploit chain: a polyglot ARM shellcode and USB packet:
0x20 0xfe 0xff 0xea 0x03 0x00 0x00 0x00
When 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 means it triggers the wInd3x computer virus above.
When parsed as ARM code executing from 0x2202e300 (the build ep0_dma lives), it’s:
$ rasm2 -a arm -b 32 -o 0x2202e300 -D "20feffea03000000" 0x2202e300 4 20feffea b 0x2202db88 0x2202e304 4 03000000 andeq r0, r0, r3
By the way in which, 0x2202db88 is 136 bytes into the DFU buffer, which is shapely grand ‘limitless’ in dimension and very with out problems managed. Now we get hang of stable code exec!
With this, we can originate up sending the design some ‘shell’code. On of the earliest issues I wrote became proof of thought ‘ship-memory-teach-over-USB’. In a future article, we will talk about extra radiant payloads, nonetheless for now let’s focal level on the reason we’re even right here:
Exploiting the Nano 5G
We’re now in The Cool Zone. We experimentally confirmed the Nano 5G BootROM is inclined to wInd3x attributable to a crash, nonetheless now we get hang of fully no BootROM dumps to genuinely craft our exploit as with out problems as for the Nano 4G. Valid attempting the identical payload would no longer work both (as anticipated, the offsets are doubtlessly all completely different attributable to a recompilation of the BootROM).
First I desired to verify if the first fragment of the exploit, leaping into the 0x000..0x600 memory by controlling ep0_dma silent labored. I became ready to approach up with a easy ‘oracle’ test: if ep0_dma is 0, then we must leap to address 0x000 which is the reset vector, which must infrequently restart DFU mode. However, if I region it to 4, then it may maybe well well well leap to 0x004 which is the invalid instruction handler, which I do know the Nano 4G BootROM utilized as a wide loop. That labored, so I knew I could maybe well well defend an eye on that fragment of the execution.
Now, my goal became to search out a working ‘blx r0’ system someplace within the first 0x600 bytes. So I bruteforced that, and kept notes:
# 374: caught # 378: returns # 37c: restart <- uncommon # 380: returns # 384: caught # 388: returns # 38c: returns # 390: caught # 394: returns # 398: returns # 39c: caught # 3a0: caught # 3b0: caught # 3b4: caught # 3b8-3fc: return
One address popped out as having a special behaviour: 0x37c. All the pieces else both crashed the design (and introduced on it to be caught in an infloop) or had no elevate out (seemingly hitting a block of code ending with a mov private computer, lr). I could maybe well well now work on my USB/ARM polyglot payload. To my surprise, it labored out of the box: reckoning on whether or no longer I populated the DFU buffer with ‘loop: b loop’ or ‘bl #0x0’ instructions, I purchased completely different behaviour.
Now, I could maybe well well exec
-pre>
Fantastic! I was involved in the freemyipod project back when the 4G was still a current product. In fact, the freemyipod.org wiki ran from a server I kept downstairs in my parent's home. My favorite memory was when we all built robots to brute force a return address (https://freemyipod.org/wiki/Nanotron_3000). Glad to see the project is still alive!