Discussion in 'General PS4 Discussion.' started by kozarovv, Oct 20, 2017.
Team fail0verflow known for various hacking adventures on the PS3 & PS4 has an OLD Exploit they have detailed for firmware v4.06 and below called Adieu. As put by team fail0verflow "And so we say goodbye to a nice exploit, I hope you enjoyed this blast from the past" shows us this is not the exploit that will define the PlayStation 4 in the same way exploits for the PS3 and PS Vita had. So while this exploit is exciting to see it won't be 3.41 & also 3.55 FW on the PS3 where every console at that time had the ability to exploit thier system until Sony updated FW which cand take several weeks like in the case of 3.60 FW for the PS Vita / PsTV where HENkaku could be installed by any device until Sony updated firmware (3.61) and patched. Even with every device having the ability to exploit the community is still small, so expecting this to really kick off the PS4 Homebrew Community is a bit much to ask because even you are willing to buy a 2nd console, there is 10 people who are not willing to do so. Where as those same 10 people would of installed a hack that was on current firmware. This exploit is good for developer's or even the curious but its not going to be the hack that we will use in future. Great development for the community but again an exploit that came too late to have any comparsion to the exploits present on other devices. . Personally i would not suggest buying a 2nd PS4 at this time. At very least i would see how this exploit shakes out to see if it gives you a better experience then OFW..
Finding a UAF Target
Putting it Together
The First PS4 Kernel Exploit: Adieu
Plenty of time has passed since we first demonstrated Linux running on the PS4. Now we will step back a bit and explain how we managed to jump from the browser process into the kernel such that ps4-kexec et al. are usable.
Over time, ps4 firmware revisions have progressively added many mitigations and in general tried to lock down the system. This post will mainly touch on vulnerabilities and issues which are not present on the latest releases, but should still be useful for people wanting to investigate ps4 security.
The namedobj exploit was present and exploitable (albeit using a slightly different method than described here) until it was fixed in firmware version 4.06. This vulnerability was also found and exploited by (at least) Chaitin Tech, so props to them! Taking a quick look at the 4.07 kernel, we can see a straightforward fix (4.06 is assumed to be identical - only had 4.07 on hand while writing this post):
And so we say goodbye to a nice exploit.
I hope you enjoyed this blast from the past
As previously explained, we were able to get a dump of the ps4 firmware 1.01 kernel via a PCIe man-in-the-middle attack. Like all FreeBSD kernels, this image included “export symbols” - symbols which are required to perform kernel and module initialization processes. However, the ps4 1.01 kernel also included full ELF symbols (obviously an oversight as they have been removed in later firmware versions). This oversight was beneficial to the reverse engineering process, although of course not a true prerequisite. Indeed, we began exploring the kernel by examining built-in metadata in the form of the syscall handler table - focusing on the ps4-specific entries.
After some recovering of structures, we discovered that a large portion of the ps4-specific syscalls are little more than wrappers to what is essentially a hash table API. The API exposes the following interface:
Each process object in the kernel contains its own “idt” (ID Table) object. As can be inferred from the snippet above, the hash table essentially just stores pointers to opaque data blobs, along with a given kind and name. Entries may be accessed (and thus “locked”) with either read or write intent.
Note that IDT_TYPE is not a bitfield consisting of only unique powers of 2. This means that if we can control the kind of an id_entry, we may be able to cause a type confusion to occur (it is assumed that we may control name). Sure enough, kind may be set from usermode via the namedobj_create syscall:
Now we need to find a way to have the kernel access and improperly use an object from our process’ (i.e. the browser process) idt which has a kind of 0x1000 plus any other number of bits set. This was found in the following code:
Using the combination of these syscalls, we can induce a type confusion. First, calling namedobj_create(name = "haxplz", kind = 0x1000 | 0x4000, ...) will cause the kernel to set a pointer of type namedobj_usr_t into the idt. Then, calling namedobj_create_ex(name = "haxplz", ...) will cause the kernel to access the same pointer, but cast it to type namedobj_dbg_t!
To an exploiter without ps4 background, it might seem that the easiest way to exploit this bug would be to take advantage of the write off the end of the malloc’d namedobj_usr_t object. However, this turns out to be impossible (as far as I know) because of a side effect of the ps4 page size being changed to 0x4000 bytes (from the normal of 0x1000). It appears that in order to change the page size globally, the ps4 kernel developers opted to directly change the related macros. One of the many changes resulting from this is that the smallest actual amount of memory which malloc may give back to a caller becomes 0x40 bytes. While this also results in tons of memory being completely wasted, it does serve to nullify certain exploitation techniques (likely completely by accident…).
The way chosen to exploit this type confusion was actually to convert it into a use-after-free scenario. This was done with the help of the namedobj_delete syscall:
Note that the type confusion allows us to cast a namedobj_usr_t object to a namedobj_dbg_t one, and then update all of the namedobj_dbg_t fields. Not only does this allow us to write off the end of the actual namedobj_usr_t object, it also allows writing to the lower 32bits of the namedobj_usr_t.name pointer, as well all the other namedobj_usr_t fields. The fact that we may only update the lower 32bits of namedobj_usr_t.name is actually a blessing in disguise (although it doesn’t matter so much for this post).
So, the use-after-free primitive we have allows us to free() any kernel address which happens to share the top 32bits with no->name. This means we can have our choice of any malloc’d pointer to free - we just need to somehow find such a pointer Obviously, such a pointer should be able to be used in a nice way after we free it and reallocate the backing memory.
Finding a UAF Target
Since this was my first time working with FreeBSD, I just looked for some kernel object containing some function pointers which I could somehow derive the address of from the browser process. It turns out that on firmware 1.01 this is incredibly easy:
will happily give you various kernel addresses relating to the file objects which the kernel uses to manage userspace file descriptors. From the exploit code:
Putting it Together
Another exploit excerpt:
Above is shown the creation of a kqueue object in userspace, which then gets sprayed into the kernel after performing our UAF primitive (via kernelFree()) simply by calling ioctl() with it. After the spray, executing the syscall kevent() with the fd relating to our corrupted file object will cause the kernel to call the kqueue object’s kl_unlock function pointer, which will kick off execution of the ROP chain.
Since this exploit leaves a corrupted file object in the browser’s file descriptor table, the first thing for the kernel payload to do is actually to remove that corruption. Otherwise, the kernel will eventually panic (normally while iterating the process’ file descriptor table in an attempt to close() all of them). This can easily be done with the following:
Separate names with a comma.