The Exploit

As the exploit that was used to dump lv0ldr/bootldr/howeveryouliketocallit is public now, let’s have a closer look at it to understand what’s going on. Here is what I have reversed from lv0 (it shares the syscon portion of the code with its SPU counterpart):

//In .data section.
static u8 tmp_pkt[0x800];

//Get size from sc packet.
#define GET_SIZE(pkt) ((pkt[4] << 8) | pkt[5])

int read_cmpl_msg(/*...*/, u8 *payload_buf /*r5*/, int min_size /*r6*/, /*...*/)
{
    u16 pkt_size;

    //Get packet header.
    memcpy_aligned_64(tmp_pkt, MMIO_SC_PKT, 0x10);

    //Check packet size.
    pkt_size = GET_SIZE(tmp_pkt);
    if(pkt_size - 4 < min_size || pkt_size + 8 > 0x800)
        return ERR;

    //Run first sc_checksum.
    if(!sc_checksum(...))
        return ERR;

    //Read packet again (plus header!).
    pkt_size = GET_SIZE(tmp_pkt);
    memcpy_aligned_64(tmp_pkt, MMIO_SC_PKT, size + 0x1B);

    //Get size again (not checked now).
    //I suspect that this is actually a compiler 'quirk' and not a
    //programmer mistake. The original source probably accesses the
    //packet size through a structure and the compiler noticed the
    //non const access of the packet and generated this read of the
    //size member because it could have changed.
    pkt_size = GET_SIZE(tmp_pkt);

    //Let's have some fun (payload_buf on caller stack).
    memcpy(payload_buf, tmp_pkt + 8, size - 4);

    //Run second sc_checksum.
    if(!sc_checksum(...))
        return ERR;

    //...
}

The syscon library implements some high level functions, e.g. to shutdown the console on panic or to read certain configuration values. Every of this functions internally uses another function to exchange packets with syscon and the exchange function uses the read_cmpl_msg one to get the answer packet. The top-level function will pass a fixed size buffer to the exchange function. So if we are able to control syscon packets, e.g. by emulating MMIO (and thanks to IBM we are), we can change the packet size between the two packet readings and overwrite the caller stack. And if we first copy a little stub to shared LS and let the return address point to it, we can easily dump the whole 256 kB.

Nothing more left to say now, let’s wait and see if this is going to be fixed in future firmware versions (we just have to check lv0 fortunately).

Advertisement

5 thoughts on “The Exploit

  1. Pingback: A Closer Look At The lv0ldr/bootldr Exploit By naehrwert | Console Gamers

  2. I understand how the size could theoretically change between two calls, and I assume this is where the magic happens, but could you describe that step a bit more? You mention MMIO emulation… does it mean you/they got that code to run on a hacked ps3 that was able to “change” the behavior of memcpy_aligned_64 ?

    • lv0ldr is exchanging packets with syscon over MMIO. So the idea was to use the fact that we can run lv0ldr on the SPU with memory translation mode enabled. Thus meaning that we can ‘point’ MMIO to our own memory. Then by a mechanism IBM intended to help developers debug MFC DMA access (and which was left enabled for isolated SPUs) we’re able to intercept all MFC access (that’s how lv0ldr read/writes from/to MMIO). And that makes it possible to emulate the behaviour of syscon and use the above exploit to gain code execution.

  3. I wonder if this could be theoretically used for making the PS3 assume it’s working with the original code while having all (or partially) custom code? More or less how the the old PlayStation hard mods used to bypass the protection code by temp-injecting stuff directly into the BIOS rom.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s