|
| 1 | +# BluuBomb write-up |
| 2 | + |
| 3 | +BluuBomb allows running an IOSU kernel binary by sending data from an emulated Wii Remote. |
| 4 | + |
| 5 | +## Overview |
| 6 | + |
| 7 | +The Wii U's operating system consists of CafeOS, which runs on the PPC and the IOSU, which itself runs on a chip called the Starbuck. Yeah, for some reason the names of everything on the Wii U are coffee-related. |
| 8 | +While the Wii U has several exploits and entrypoints on the PPC side, the IOSU only has a few exploits and no direct entrypoints. |
| 9 | +To get IOSU code execution you always have to go through the PPC side. |
| 10 | +The IOSU consists of several modules. I was working on reverse engineering some IOSU modules to gain some additional knowledge for a project I was working on. |
| 11 | +The most interesting modules for this write-up are IOS-PAD and IOS-KERNEL. |
| 12 | +IOS-PAD handles most of the controller-related things. It communicates with 2 libraries on the PPC side: vpad.rpl and padscore.rpl. |
| 13 | +I was curious to see how the communication between padscore and IOS-PAD is handled, so I decided to take a look at IOS-PAD and padscore. |
| 14 | +IOS-KERNEL is the kernel of the IOSU (obviously). It has the most permissions in the IOSU and can write to executable memory that is usually not writable. |
| 15 | +The main goal of an IOSU exploit would be to load a custom binary into the kernel. |
| 16 | + |
| 17 | +## Communication between padscore and IOS-PAD |
| 18 | + |
| 19 | +IOS-PAD uses a resource manager named `/dev/usb/btrm`. |
| 20 | +Padscore will then use ioctl/ioctlv calls to communicate with it. |
| 21 | +Once padscore is loaded it will set up something called `SMD`, which I assume stands for `Simple Message Deque` (?). It uses a shared buffer between the PPC and IOSU and is able to send and receive messages between each other. |
| 22 | +SMD on the PPC side is called `smdPpc` and is part of [coreinit](https://github.com/devkitPro/wut/blob/master/cafe/coreinit.def#L1217), while on the IOSU it's called `smdIop`. |
| 23 | +The buffer is allocated in padscore and its' address is passed to the IOSU using btrm's ioctl call 1. |
| 24 | +Once an HID-report is received via Bluetooth it's copied to the stack and passed to the PPC via a `smdIopSendMessage` call. |
| 25 | + |
| 26 | +## The bug |
| 27 | + |
| 28 | +When the HID-report is received `bta_hh_co_data` is called. |
| 29 | +The function looks something like this: |
| 30 | +```c |
| 31 | +void bta_hh_co_data(uint8_t dev_handle, void *p_rpt, uint16_t len, uint8_t mode, uint8_t sub_class, uint8_t ctry_code, void *peer_addr, uint8_t app_id) |
| 32 | +{ |
| 33 | + HidBuffer hid; |
| 34 | +... |
| 35 | + if (len == 0) { |
| 36 | + log_printf("BT: [Err] received invalid HID len==0 \n"); |
| 37 | + } |
| 38 | + else { |
| 39 | + hid.len = len; |
| 40 | + hid.sub_class = sub_class; |
| 41 | + hid.app_id = app_id; |
| 42 | + hid.dev_handle = dev_handle; |
| 43 | + hid.mode = mode; |
| 44 | + memcpy(hid.data, p_rpt, len); |
| 45 | +... |
| 46 | + res = smdIopSendMessage(smdIopIndex, &hid, 0x40); |
| 47 | + if (res == 0) { |
| 48 | + numSucessfulSmdMessages = numSucessfulSmdMessages + 1; |
| 49 | + } |
| 50 | + else { |
| 51 | +... |
| 52 | + pad_printf("BT: [Err] SMD send message failed with status %d:%d\n", res); |
| 53 | + numFailedSmdMessages = numFailedSmdMessages + 1; |
| 54 | + } |
| 55 | + } |
| 56 | +... |
| 57 | +} |
| 58 | +``` |
| 59 | +The vulnerability is really easy to spot here. |
| 60 | +It checks that len isn't 0 and then copies the report data to the buffer on the stack. |
| 61 | +This buffer can store up to 58 bytes. We can send as many bytes as the MTU allows though. |
| 62 | +This allows writing to the stack and we can easily overwrite the LR stored on the stack, which allows us to jump anywhere in IOS-PAD. |
| 63 | +
|
| 64 | +## Exploiting the bug |
| 65 | +At first, I needed a fake controller that can send malicious data to the Wii U. |
| 66 | +I found a repository called [WiimoteEmulator](https://github.com/rnconrad/WiimoteEmulator) on GitHub. Sadly I wasn't able to pair the emulated Wii Remote to my Wii U. |
| 67 | +After hooking into some functions on the IOSU, I figured out the Wii U will try to use something called "Secure Simple Pairing". If the "Secure Simple Pairing" Mode (SSP) is enabled on the client, pairing on the Wii U will fail. |
| 68 | +The fix was relatively simple. All I had to do was to disable SSP on the client while BluuBomb was running. |
| 69 | +
|
| 70 | +We can now send our own data using the WiimoteEmulator. |
| 71 | +All data packets start with `0xa1` if they're sent from the Wii Remote to the Wii U and with `0xa2` if sent from the Wii U to the Wii remote. |
| 72 | +The Wii U will copy all the data behind this byte to the earlier mentioned buffer. |
| 73 | +
|
| 74 | +We can't write to any executable memory without kernel access, so we'll have to use existing instructions stored in executable memory. |
| 75 | +On ARM the function address returned to after calling a function will be stored in the Link Register. |
| 76 | +When a function needs to call another function this Link Register is pushed to the stack and popped back into the program counter when returning from the original function. |
| 77 | +Since we now control the stack we can modify the return addresses stored in it. This allows us to create a so-called ROP chain. |
| 78 | +We can use existing useful instructions, so-called "gadgets", and jump to them. |
| 79 | +When the gadget returns it will read the return address from our controlled stack. |
| 80 | +I've mostly used [ROPgadget](https://github.com/JonathanSalwan/ROPgadget) or Ghidra itself to find useful gadgets. |
| 81 | +We can also execute ARM instructions as Thumb instructions. Thumb is a special mode in ARM processors that allows using 2-byte instructions instead of 4 bytes. |
| 82 | +By loading an address with the last bit set to 1 into the program counter the following instructions will be interpreted as Thumb. |
| 83 | +
|
| 84 | +The stack at the location of the HID buffer looks something like this: |
| 85 | +|Stack| |
| 86 | +|---- | |
| 87 | +|HID buffer | |
| 88 | +| r4 - r10 | |
| 89 | +| Return address | |
| 90 | +
|
| 91 | +We can now create a payload which overwrites our return address. |
| 92 | +This would look something like this: |
| 93 | +| Data | Size | Note |
| 94 | +|----------------|------|------ |
| 95 | +|0xa1 |4 |Indicates that this data is from the Wii Remote |
| 96 | +|Can be anything |58 |The data copied to the actual HID buffer |
| 97 | +|Other registers |24 |Other registers |
| 98 | +|0x11f831f8 |4 |The return address to overwrite (This would jump to IOS_Shutdown and power off the system) |
| 99 | +
|
| 100 | +
|
| 101 | +The MTU is around 512 bytes and we only have around 130 bytes until we reached the top of our stack. |
| 102 | +This won't be enough to exploit the kernel or load a kernel binary, so we need a way to load more data into memory. |
| 103 | +
|
| 104 | +I started by making a simple payload which will upload data. |
| 105 | +It uses the 58 bytes of the HID buffer and calls memcpy to copy the data to a location we've specified. |
| 106 | +It then continues with normal execution. To achieve this we only overwrite a specific amount of the stack and jump to a location that would expect the stack pointer at the location it's currently at. |
| 107 | +
|
| 108 | +We can now upload 58 bytes at a time! |
| 109 | +This allows us to upload a bigger ROP chain and a kernel binary. |
| 110 | +
|
| 111 | +Unfortunately, we overwrite the address of the report buffer packet and can no longer free it. |
| 112 | +That means with all the ROP chains we only have 871 bytes for the kernel binary. Ouch, that's small. |
| 113 | +
|
| 114 | +Let's start with the big ROP chain we'll use to gain kernel access. |
| 115 | +We can use a flaw in the IOS_CreateThread call which will clear parts of the specified stack with zeroes. Since this is cleared with kernel permissions we can use this anywhere in memory. |
| 116 | +Zeroes are interpreted as NOPs on ARM. This allows us to patch parts of the `set_panic_behavior` syscall which makes it possible to use it for arbitrary write. |
| 117 | +We can now use this syscall to write bytes anywhere in memory. |
| 118 | +This allows us to write our own instructions and turn the `set_fault_behavior` syscall into a function which copies our kernel binary from memory and executes it with kernel permissions. |
| 119 | +This is highly based on the ROP chain used in Mocha CFW, but adjusted to work in IOS-PAD. |
| 120 | +
|
| 121 | +Now all that was left is a payload that pivots the stack into our bigger ROP chain which is placed into memory. |
| 122 | +We need to pivot the stack or else we'll write over the top of our stack. The IOSU will prevent use from using syscalls if the stack pointer is invalid. |
| 123 | +There only seems to be a single instruction in IOS-PAD which makes it possible to pivot the stack. |
| 124 | +This instruction was `add sp, sp, r2`, which adds the value in register 2 to our stack pointer. |
| 125 | +To properly return from this gadget, without executing instructions we don't want, we use `IOS_CreateThread` again to nop out some instructions. |
| 126 | +We can now offset the stack by any amount we want. The thread we're running on has a stack size of `0x1000`. |
| 127 | +We'll now set register 2 to `-0x600` and run the stack pivot ROP chain. |
| 128 | +This will give us enough space for the big ROP chain. |
| 129 | +
|
| 130 | +And that's basically it. We can now: |
| 131 | +- Use the upload payload to upload our kernel binary into unused memory |
| 132 | +- Use the upload payload to upload our bigger ROP chain to the stack |
| 133 | +- Use the stack pivot payload to pivot the stack into the bigger ROP chain |
| 134 | +- Copy the kernel binary into the kernel and run it |
| 135 | +
|
| 136 | +The 871 bytes are luckily enough for loading a custom .rpx or a custom fw.img. |
| 137 | +
|
| 138 | +## Conclusion |
| 139 | +
|
| 140 | +This is the first fully implemented Wii U exploit that directly exploits the IOSU. |
| 141 | +The only thing you need for BluuBomb is a Wii U that is in a state able to pair a Wii Remote and a PC with Bluetooth support. |
| 142 | +For the fw.img loader you also need to be able to access and exit System Settings on the Wii U. |
| 143 | +While the browser is still the more convenient entrypoint, this should be able to repair a few soft bricks. |
| 144 | +
|
| 145 | +Hope this is useful for someone :) |
| 146 | +
|
| 147 | +Funnily enough, at the same time as I was finishing implementing this exploit, the Switch got an [update that fixes](https://switchbrew.org/wiki/Switch_System_Flaws#System_Modules) this exact issue. |
0 commit comments