Wii hacked it!
After bushing had shown the first homebrew exploit, a lot of stuff has happened in the Wii-world. The exploit was based on a hole in the disc hashing&verification, but the original finder (segher) decided that he doesn’t want the bug to be published. While this caused some controversy, the reason behind this was that the hole could be patched very easily in a future firmware version, as no original function relies on it. The next goal was to find a bug which could not be patched so easily, for example a savegame exploit. Patching such game exploits is considerable harder. Of course you could patch the game code when it is loaded (like some gamecube games are fixed in compatibility mode by the “gamecube compatibility IPL”), but we could just move on to another game. We wouldn’t lose that much power if a game bug is fixed, vs. a critical system bug. I can totally understand that people are annoyed by us not doing full disclosure. Nevertheless we try our best to balance our different interests. It’s not always easy, even inside a team. Still, the rule is: If you find a bug, it’s your choice what you do with it. If you don’t like that, find your own bug.
I’ve concentrated less on the high-level things, I’m generally more interested in the system design and security architecture. So I’ve digged into the bootloader.
What we knew before was that there is a fixed block of code called “boot1”, which is supposed to be the first code executed from flash. It’s ARM (“Starlet”) code, btw, the powerpc (Broadway) is booted much later. We didn’t knew how boot1 is encrypted (rumours ranged from an LFSR-based streamcipher to AES), nor if and how it was hashed. But what we had was a program called “BC” (title id: 1-100), extracted out of a system update. We are absolutely not sure why BC does even exist (it might be used to return from GC mode to Wii mode, but why would you want to do so?), but what BC is doing matches what boot1 could be doing: Reading a bunch of sectors from flash, decrypting them, and checking a signature against a previously decoded cert chain, then jumping there. Once we re-coded the algorithm, it was clear that this in fact decrypts boot2. Encrypting a new boot2 requires signing the new hash. Now it turned out that “BC” also contains “the bug” (well, a similar one), so chances were big that boot1 also does. But flashing a new boot2 is dangerous if you have no return - there is a backup mechanism to boot another copy of boot2, but we cannot count on that for several reasons (for example, if our new boot2 code hangs, the backup would not be tried, as boot1 thinks that everything is right).
It also became clear that once we are able to execute starlet code, it will be a lot of trial&error. So what I did was to revive my old FPGA-based NAND flash emulator, which I once built for the Xbox 360. I wired the Wii’s flash pins to the FPGA. Now the Wii flash has different properties (large block, larger size, different ECC algorithm used), but I could adapt it in a matter of hours. I had to fix the RESET handling (the Wii is waiting for R/#B to go low for a short moment of time), and some minor things, but then it worked! I could boot from my FPGA instead of the original Flashrom. So I could do code changes in a matter of seconds, instead of always reprogramming the flashrom (potentially external). Because my FPGA board has “only” 512MB of RAM, I couldn’t fit the whole flash contents into the RAM. As part of the NAND emulation happens on the embedded PowerPC core in the FPGA (a Virtex 2 Pro), I just added an ethernet MAC, and used lwip to fetch the flash pages from a TCP server. That made the development cycle even easier, as I could now just modify the virtual NAND content on my PC!
bushing prepared a fixed boot2 for me (he just changed a dummy byte), and hey, it worked! I could now run code on the Starlet! First thing would obviously a “hello world”, but where to output? I’ve wanted a software UART, like I had done in GC mode before using the EXI_CS line, but I wasn’t be able to find any useful GPIO. Finally I was able to blink the sensorbar!Unfortunately, all those IOs aren’t TTL, and I didn’t wanted any level shifting.
An hour later I finally got the idea which I should have got in the first minute: Using the already existing NAND interface. I would just issue a non-standard command (the Starlet NAND controller is simple enough to be able to issue any command), and use the address bytes as payload. I’ve added the proper code on the FPGA side (luckily I hadn’t even to change my VHDL code…) to output the address bytes of the private command to the FPGA’s UART.
Some hours later I’ve dumped boot1 (it still was in memory), and some hour later I’ve dumped a new piece of code, which turned out to be decrypting something from the beginning of the NAND flash into memory, then calculating a SHA-1 hash over it, and compares that to a hash read from some “internal memory” (which we believe is an OTP area). It turned out that this was - the bootrom! Hooray! I decided to call this piece of code “boot0”.
So, to sum things up:
- First thing which ever executes on the Wii is the “boot0” code, which is probably stored inside the hollywood in a mask rom.
- boot0 loads the first 0x2F pages (“boot1”) from flash, decrypts them with a fixed aes key, calculates a SHA-1 hash (with some obscure
bugsspecialities, I still couldn’t calculate it by hand), and checks that versus the expected values, read from some internal memory.
- If the hash bytes in the “internal memory” is all-zero, the hash check is skipped. This is probably used for production, and maybe for devkits.
- boot1 then searches a certain header in flash, where it extracts specific information where to find boot2.
- At that position, some certificate chain is checked, and finally the boot2 “tmd” is verified, and the hash extracted.
- The boot2 payload is load from flash, decrypted, and hash-checked (against the hash from the boot2 tmd).
- boot2 will then load the firmware, or whatever. That’s not my region of interest at the moment. :)
However, there is no revocation mechanism. boot1 cannot be updated on retail machines, as the hash is OTP. And boot1 is buggy. New produced Wiis could contain a fixed boot1, of course, even without modifying the mask rom, thanks to the flexible approach with the OTP hash. boot1 looks everything but nice to me. The whole decrypt function is completely chaotic (and while this might be just an optimizing compiler, I would bet the source looks similar..), doesn’t do nearly as much header sanity checks as required, and contains, as said, at least two completely critical bugs. Given that in the early stage of booting a real chain of trust is used (each code inherits all security priviledges from the caller), this is what breaks the security.
On the hardware side, what I would have liked is a possibility to turn off the bootrom (and not just mask it behind an MMU), and PEOPLE: You designed a perfectly cool AES engine into the hardware. You also have a secure key store (the OTP). WHY didn’t you JUST added the possibility to use the OTP area as key material for the AES engine, and disallow reading of these areas by the CPU? You could have hidden the boot1 key in hardware, for example. Or even the boot2 key. Or even really important stuff. But everything is exposed to software. Once you’re in, you own it. That IS a security problem. Security by obscurity doesn’t work, but obscurity while doing the security (without sacrifying any real security, that’s the important part) often helps to gain time. It isn’t important whether your product is hacked in 10 years, it’s important if it’s hacked TODAY. By doing some tricks here and there, you can make it much harder to hack your product, gaining time. Still, and that’s very important, you need real security. Obscurity is no replacement for security!)
I have to add that I really liked our teamwork, even if it wasn’t easy all the time. It really helps if more than one person is working on something, even (or especially) if everybody is working in another direction, but all on the same topic. I would also like to thank d. for sharing his NAND logs with me (he also built a nand sniffer&emulator, long before mine was ready), it helped a great deal in determining what went wrong. And of course I have to say that I’m happy about everyone who is taking part of this discoveries, be it public or non-public, be it in an overcrowded or three user channel. There is a lot to be learned about the Wii, and I’ve always liked being part of the gamecube development community (at least up to a certain point, but that’s another story). It amazed me how much good work came from a relatively small group of people. Let’s continue on that!
(Hey, it’s about functionality, not about the visuals!)