PSJailbreak Exploit Reverse Engineering
Analysis of the PSJailbreak Exploit
The PSJailbreak dongle is a modchip for the PlayStation3 that allows users to backup and play games off the harddrive. Unlike the modchips of the Previous generation, or the modchips so far for the Xbox360 and Wii, this modchip simply plugs into the USB port on the front of the PS3, avoiding the need for complex soldering and voiding of your warranty.
As the time of writing this document, the final PSJailbreak has not been released, but a number of samples were given out and at least one fell into the hands of someone who owned a USB sniffer. This analysis of the exploit is based on those USB sniffer logs, issues encountered during the development of the opensource PSGroove version of the exploit and a number of educated guesses. It will probably be updated as new information comes in.
The initial analysis by gamefreax.de suggested that it was a Stack overflow attack. After further analist it turns out that this exploit is a Heap Overflow attack. The exploit carefully manipulates the heap by plugging and unplugging fake usb devices with large device descriptors until the device on port 4 which misreports its size to overwrite one of malloc's boundary tags.
The state of the PS3
The exploit takes place while the PS3 is looking for the Jig (triggered by pressing eject within 200ms of pressing power). It is suspected that the ps3 spends around 5 seconds doing nothing but initializing devices on the USB bus, so there is little extra code running to mess the exploit up.
Setting up the heap
The PSJailbreak dongle emulates a 6 port USB hub. By attaching and detaching fake devices to the ports of the hub the dongle has control over the mallocing and freeing of various blocks of memory that hold the device and configuration descriptors.
After the hub has been initialized, a device is plugged into port one with a pid/vid of 0xAAAA/0x5555, It has 4 configurations with each one is 0xf00 bytes long. This is just under the size of 4k page, so malloc will have probably have request a new page for each one, unless it already has enough free space, but at least one will be aligned at the start of a page.
The dongle also changes the configuration the 2nd time it is read so that the configuration in the ps3 memory is only 18 bytes long.
It just so happens that that this data contains the payload that the exploit will jump to after gaining control of the execution, but that is not important for the exploit.
After the PS3 has finished reading the port one device descriptors, the dongle switches back to the address of the hub and reports that a device has been plugged into port two.
This device has a pid/vid of 0xAAAA/0xBBBB, and it has 1 configuration descriptor which is 22 bytes long. Only the first 18 bytes are real usb data and the remaining 4 bytes are:
04 21 B4 2F
With a length of 04 and an invalid type byte, anything interpreting it as USB descriptor will probably skip over it and the last 2 bytes. It is suspected that this is just here to make this descriptor take up an exact amount of heap space.
Update: I tried changed this to 04 21 11 11 and the exploit failed. For some reason, these values are important.
Update 2: Based on what was wrote by gray (lan.st/showthread.php) The first two-byte (04 21) seems to be the Dongle ID and the second two-byte (B4 2F) seems to be the Dongle Key. The authentication uses a classic hmac scheme and works as follows. The console sends the challenge which is basically 20 bytes of random values and stores it. the usb dongle sha1-hmacs the challenge with the Dongle Key and sends it to the console along with the Dongle ID. The console recovers the Dongle Key from the master key and Dongle ID and sha1-hmacs the stored challenge value. if the result is the same as the dongle response - authentication is passed. Um, No. The key is 160 bits long so that theory doesn't work. The Jig authentication fails in this exploit. -- Phire
Update 3: The class/subclass/protocol of the interface for this device is 0xFE/0x01/0x02 and according to usb.org the 0xFE/0x01/0x00 combination means it's a DFU device (Device Firmware Upgrade), so it could be that these last 4 bytes mean something for this Application-Specific device interface (I'm not sure about that protocol 0x02 though).
It could be 0x04 = size, 0x21 = type, 0xba and 0x2f could be some kind of setting or a 'code' to tell the ps3 to expect a JIG or something.
The port three device has a pid/vid of 0xAAAA/0x5555, the same as port one. Unlike the port one device it has 2 configuration descirptors, each 0xa4d bytes long The data that fills them is junk but it may or may not be relevant that if you treat the data as descriptors they will have valid lengths. These descriptors will probably be allocated to the start of a fresh 4kb page that follows the page with the last port one descriptor and port three descriptors.
Port Two Disconnect
After port three is connected, port two will be disconnected, this will cause the port two descriptors to be freed, which frees up some space between the Port One and Port Three descriptors.
The heap is now prepared for our exploit.
Port Four Connection
A device is connected to port 4, with a pid/vid of 0xAAAA/0x5555 and 3 configurations.
This is a normal configuration, 18 bytes long
This configuration is the same as Configuration A, except it changes its total length from 18 bytes to to zero bytes after the PS3 has read it the first time and allocated space for it.
This is where things get vague, this is key to the exploit and will somehow cause the the extra data at the end of Configuration C to overwrite one of malloc's boundary tag, most likely the one belonging to Port Three.
But the exact reason for this buffer overrun is hard to guess without actually seeing the exploited code.
This starts the same as configuration A, but has 14 bytes of extra data at the end.
.. .. 3e 21 00 00 00 00 fa ce b0 03 aa bb cc dd 80 00 00 00 00 46 50 00 80 00 00 00 00 3d ee 70
The first 6 are just padding (but the 3e might be important if this ever gets interpreted as a USB descriptor.) Then there are 3 u64 values, each 8 bytes long.
The first two values are stored for use by the shell code later just before malloc's boundary tag.
The 3rd value overwrites the first value of the boundary tag, which is pointer to the next free section of memory. The replacement pointer will point to a function somewhere. This will cause a malloc to allocate memory in the wrong place, sometime in the future, allowing the exploit to overwrite an existing function.
The first value is a magic number (it reads FACEBOOK AABBCCDD).
The dongle plugs the fake Jig into Port Five right after Port Four has done its job. It uses the same PID/VID that the original Sony Jig uses (0x054C/0x02EB) and probably the same configuration with the same end points.
It is suspected that because the Jig is a known device that the PS3 was waiting for, it's device and configuration descriptors will not be malloced into the heap.
The PS3 sends a 64 byte challenge to the fake Jig to authenticate it, and the dongle replies with 64 bytes of static data. The PS3 will malloc space for this response, and because the boundary tags have been modified by Port Four, malloc will return a pointer to 24 bytes before a function that has something to do with free and the 64 bytes of data will be written over top of the function.
At the point, no code has been patched yet, so the Jig's static response will fail to authenticate the jig.
Unplug Port Three
The dongle now sends a message that port 3 has been unplugged. This will cause the PS3 to free the Port Three's configuration data, the very same buffer which had its boundary tag overwritten by Port Four.
So our shellcode gets called, with R3 pointing to the boundary tag before Port Three's Configuration data.
ROM:00000018 ld %r4, -0x10(%r3) ROM:0000001C ld %r3, -8(%r3) ROM:00000020 ROM:00000020 loc_20: # CODE XREF: sub_18+14�j ROM:00000020 ld %r5, 0x18(%r3) ROM:00000024 addi %r3, %r3, 0x1000 ROM:00000028 cmpw %r4, %r5 ROM:0000002C bne loc_20 ROM:00000030 addi %r6, %r3, -0xFE0 ROM:00000034 mtctr %r6 ROM:00000038 bctr
This takes a pointer to the corrupted boundary tags in r3.
r4 is loaded with the 0xFACEB003AABBCCDD tag, then r3 is loaded with 0x8000000000465000, both of these values are stored just before the boundary tag.
The shell code then scans every 4KB block (0x1000 bytes) starting at 0x8000000000465000, checking for 0xFACEB003AABBCCDD tag in the u64 at 0x18 in each page.
When it finds it, the shellcode will jump to offset 0x20 in the payload.
After the exploit
The exploit is now completed: Port Five, Port Four then Port One will be unplugged.
Hopefully the Payload will have copied itself out of the heap before Port One is unplugged.
The device that gets plugged into Port Six has nothing to do with the exploit. It has a vid/pid of 0xAAAA/0xDEC0 (on the PPC, which is big endian, the pid is 0xC0DE).
The payload sends it a single byte (0xAA) control transfer so that the dongle will know that the exploit was successful so it can turn the green LED on to signal the user.
A function in the original PSJailbreak Payload will make sure that this device stays plugged in. If it is ever unplugged then it will call LV1_Panic and your PS3 will shutdown. PSGroove has removed this 'feature'.
The actual payload is outside the scope of this document (There might be a 2nd document discussing the original PSJailbreak payload), but we will discuss the environment.
The payload will start in an unknown position, aligned to a 4KB boundary, it should either use position independent code, or copy itself to a known location. The payload has full control over the lv2 (aka gameos) kernel and anything below it. It doesn't have any control over lv1 (aka the hypervisor) without a 2nd exploit (the original Geohot exploit should still work.)
The Jig authentication code is most likely running in lv1 or an isolated SPU so it is not possible to patch it with this exploit.
The lv2 kernel is loaded at the time of the exploit, perfect for patching or you could replace it with something better like a linux kernel. A linux kernel running in this environment would have all the privilege of the regular gameos kernel.
Written up by phire (aka phiren on EFnet). Thanks to Matt_P, subdub and others for helping develop this theory.
Commented descriptor.h to help out while reading this epic tutorial by phire and many others.
If you have any information to add, feel free to edit this document or use the discussion page.