Create a gist now

Instantly share code, notes, and snippets.

@Wack0 / Secret
Created Jan 9, 2017

Cemu v1.6.4b infoleak + buffer overflow = emulator breakout (code exec on host)

For those who only care about one thing: the PoC is here.


Cemu is a closed-source Wii U emulator developed by Exzap. New versions are released to those who donate to him via his Patreon first, then to the public one week later. According to its official website, Cemu "is not intended for general use yet", however it can run some games well.

It HLEs the Wii U OS APIs. For those who don't know, the Wii U runs executables in a modified ELF format that include additional PE-like import and export sections. Basically, the HLE here means each exported function from each shared library has been reimplemented, and runs in native code. That's a pretty large attack surface! So, when looking for bugs, I decided to start there.

Finding bugs in Cemu HLE API emulation

Obviously, the first thing to do is to find where the API exports are set up so all of them can be annotated in IDA. I found a function at 0x1400AEDC0 (before relocation, cemu.exe is compiled with ASLR) that I labeled set_up_emulated_API. It takes three arguments: a pointer to a hashed (or obfuscated) shared library name, a pointer to a hashed (or obfuscated) exported function name, and a pointer to the function used for implementation. This function has a nice debug printf where it printed out the library name and exported function name, so I did things the long way and set a breakpoint there in x64dbg and labeled all the ~620(!) functions by hand. This took the better part of a day (however I did take breaks.)

Once I had all the functions labeled, I could go ahead and start looking for bugs. It was nice from my perspective that the emulated API functions did all the grunt work of endianness conversion of arguments and return values, so I didn't have to do anything of the sort myself. I first decided to check the more interestingly (for me) named functions, not long later I'd found a bug.


uint64_t _SYSGetSystemApplicationTitleId(uint32_t index);

The implementation of this function just sets up a large array of title IDs (a title ID is a 64 bit integer that identifies "something that runs on the console", like a system application, firmware component or game, this has been used by Nintendo since the Wii and DSi, on console and handheld respectively) on the stack, then indexes it using the provided argument without checking and returns the array[index] to the emulator. What a perfect infoleak, to defeat ASLR later!

Exploiting this seems easy, just get the return address from the stack (index 37), but it seems this isn't totally reliable, so instead I make a dummy call, then use index 52, which seems to return an address inside the cemu.exe .text more reliably.


uint32_t KPADSetConnectCallback(uint32_t index,uint32_t value)

With an infoleak obtained, I just needed to find some (semi-)arbitrary write, and this took annoyingly longer to find. I found a few bugs that seemed promising but ultimately turned out to be unexploitable. Finally, after checking some of the functions not related to the OS, I found this function. Basically, it writes to an array of 32-bit integers (obviously the intended use of that array is function pointers inside the emulated system), in the .data section, with no checking of the provided index. Even better, it returns the old value (although I never needed to use this functionality when exploiting).

The array is unfortunately near the end of .data, but that doesn't really matter, as it's stored before a nice array of KPAD C++ objects with vtables that I can clobber -- and if a pointer inside one of the objects happens to not be NULL, this same function makes a vtable call twice! Even better!


My PoC clobbers the first KPAD object (player 1 gamepad): it nulls out the checked pointer so no vtable calls are made while things are being overwritten, it overwrites the vtable pointers, sets up the ROP chain, sets up the stack pivot, makes that pointer non-NULL, and makes a dummy call to KPADSetConnectCallback to get ROP.

Heh, I just made that sound easy. It wasn't.

Let's see, it was annoying to find a stack pivot in the first place? But then I found the perfect pair of gadgets:

0x000000014015d404 : add rcx, 0x10 ; jmp qword ptr [rax]
0x0000000140228371 : push rcx ; pop rsp ; ret

When the first one gets called, rcx has the address of the vtable array, and rax has the address of the first element of the vtable array (which isn't actually used, so it's a perfect place to put a gadget address).

The ROP chain is written using KPADSetConnectCallback just like everything else, all this is written into a part of memory that contains UTF-16LE strings for controller mappings, that can only be seen if you open the controller settings. The ROP chain itself just grabs the address of the shellcode inside emulated RAM, memcpys it to RWX memory allocated for the dynamic recompiler, and jumps there. Sure, it doesn't work if you deliberately disable the dynamic recompiler, but firstly, who even does that?!, and secondly, I'll leave the making of a ROP chain that uses VirtualAlloc to someone else if they wish.

The shellcode itself is just metasploit windows/x64/exec running calc.exe. Nothing special.

One final thing: when testing, I noticed that the emulator crashed if controller one was set up properly. It's because I initially thought the pointer that got checked for being NULL was a boolean or something else, and I'd only zeroed out the lower 32 bits of it. Whoops.

Compiling the PoC

Linked at the top of the page is an archive including the PoC itself as calc.rpx plus source plus modified and additional import library dependencies (as source and binaries). I used wut to make the PoC which obviously depends on devkitPro/devkitPPC. After compiling wut successfully I had to make library additions, as both of the vulnerable functions were not included in the library set. Luckily enough it was very easy to make additions to the import libraries.


2016-12-30: started reversing
2016-12-31: found exploits
2017-01-01: made PoC, made initial contact with developer
2017-01-02: developer replies, said fixes have been made
2017-01-02: asked for release date
2017-01-02: reply: release date unknown, "in 1-2 weeks maybe"
2017-01-09: release to patrons, public disclosure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment