Skip to content

Commit 721ce20

Browse files
Initial commit
0 parents  commit 721ce20

File tree

32 files changed

+3217
-0
lines changed

32 files changed

+3217
-0
lines changed

.gitignore

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.vscode/
2+
arm_kernel_fw_launcher/build/
3+
arm_kernel_region_free/build/
4+
arm_kernel_loadfile/build
5+
arm_kernel_loadfile/mcp/build
6+
7+
bluubomb
8+
*.bin
9+
*.bin.h
10+
*.elf
11+
*_syms.h
12+
*.rpx

Makefile

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
.PHONY: all clean arm_kernel_loadfile arm_kernel_fw_launcher arm_kernel_region_free
2+
3+
all: bluubomb arm_kernel_loadfile arm_kernel_fw_launcher arm_kernel_region_free
4+
5+
clean:
6+
rm -f bluubomb
7+
@$(MAKE) --no-print-directory -C arm_kernel_loadfile clean
8+
@$(MAKE) --no-print-directory -C arm_kernel_fw_launcher clean
9+
@$(MAKE) --no-print-directory -C arm_kernel_region_free clean
10+
11+
bluubomb: bluubomb.c adapter.c bdaddr.c sdp.c
12+
gcc -std=gnu11 -Wall -o bluubomb bluubomb.c adapter.c bdaddr.c sdp.c -lbluetooth
13+
14+
arm_kernel_loadfile:
15+
@$(MAKE) -j1 --no-print-directory -C arm_kernel_loadfile
16+
17+
arm_kernel_fw_launcher:
18+
@$(MAKE) -j1 --no-print-directory -C arm_kernel_fw_launcher
19+
20+
arm_kernel_region_free:
21+
@$(MAKE) -j1 --no-print-directory -C arm_kernel_region_free

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# BluuBomb
2+
3+
Exploits the Wii U's bluetooth stack to gain IOSU kernel access via bluetooth.
4+
5+
For a more detailed write-up see [WRITEUP.md](https://github.com/GaryOderNichts/bluubomb/blob/master/WRITEUP.md).
6+
7+
Not to be confused with [BlueBomb](https://github.com/Fullmetal5/bluebomb) for the Wii and Wii Mini.
8+
9+
## Requirements
10+
- A Wii U which is able to pair a Wii Remote
11+
- A PC with bluetooth
12+
- A PC or VM running a version of Linux which is able to run the custom build of BlueZ
13+
I recommend using Debian 10, BlueZ 4 just crashes for me on Ubuntu 20.04
14+
15+
## How to use
16+
1. Run `sudo apt install build-essential libbluetooth-dev libglib2.0-dev libdbus-1-dev` to install the required dependencies.
17+
1. Clone https://github.com/rnconrad/WiimoteEmulator
18+
1. Run `source ./build-custom.sh` to build BlueZ. Don't worry if building the emulator itself fails.
19+
1. Stop the already running bluetooth service `sudo systemctl disable --now bluetooth`
20+
1. Run the custom built bluetoothd `sudo ./bluez-4.101/dist/sbin/bluetoothd -d -n`
21+
1. Download the `bluubomb` binary and the kernel binary of your choice from the [releases page](https://github.com/GaryOderNichts/bluubomb/releases).
22+
Take a look at [Kernel binaries](#kernel-binaries) for more information.
23+
1. Power on the Wii U and press the sync button.
24+
1. Run `sudo ./bluubomb arm_kernel.bin` and wait for the pairing process to complete.
25+
This might take a minute.
26+
27+
Write down the Wii U's bd address that should be displayed after the pairing is complete.
28+
You can now run `sudo ./bluubomb arm_kernel.bin <bdaddr here>` to connect directly to the Wii U and skip the pairing process.
29+
30+
## Kernel binaries
31+
32+
### arm_kernel_loadfile
33+
Launches a launch.rpx from the root of your SD card on the next application launch.
34+
35+
### arm_kernel_fw_launcher
36+
Launches a fw.img from the root of your SD card on the next OS relaunch (for example when exiting System Settings).
37+
38+
### arm_kernel_region_free
39+
Applies IOSU patches to temporarily remove region restrictions.
40+
This should be helpful if you've locked yourself out of your applications due to permanent region modifications.
41+
42+
## Building
43+
44+
To build you need to have gcc and devkitARM installed.
45+
Then run `make`.
46+
47+
## Credits
48+
- GaryOderNichts - bluubomb
49+
- rnconrad for the [WiimoteEmulator](https://github.com/rnconrad/WiimoteEmulator)
50+
- dimok789 and everyone else who made [mocha](https://github.com/dimok789/mocha) possible

WRITEUP.md

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
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

Comments
 (0)