3.03 - 3.11

These firmware versions all have the same getDiscData 0xffff * 3 * 8 buffer overflow of immediately controllable contents.

3.03 3.04J 3.04M 3.10 3.11
Symbols
getDiscData 0x243438 0x23e150 0x23e138 0x25c9f0 0x258b98
getDiscByte 0x243368 0x23e080 0x23e068 0x25c920 0x258ac8
currentDiscBytePointer 0x15f42a4 0x1273ae4 0x16ceee4 0x1411fe4 0x143b3e4
endDiscBytePointer 0x15f42a8 0x1273ae8 0x16ceee8 0x1411fe8 0x143b3e8
0xff * 3 * 8 overflow 0x241d0c 0x23cb1c 0x23cb04 0x25b3bc 0x257564
fpIndex 0x15f4b0a 0x127434a 0x16cf74a 0x141284a 0x143bc4a
fpArray 0x923d88 0x6d4e68 0x95ace8 0x5b9d40 0x3b3050
OOB call 0x0244E1C 0x23fad4 0x23faac 0x25e388 0x25ab44
getBufferInternal 0x262360 0x261560 0x261548 0x2986a0 0x2952f0
pointToIFO 0x2432c8 0x23dfe0 0x23dfc8 0x25c880 0x258a28
SifIopReboot 0x291528
SifInitRpc 0x2082a0 0x208260 0x84180 0x208d80
SifExitRpc 0x208440 0x208400 0x84310 0x208f20
SifIopReset 0x291fb8 0x291358 0x84fe0 0x20e7d8
SifIopSync 0x292138 0x2914d8 0x85110 0x20e958
Controlled memory ranges
Destination of large copy 0x15ec890 0x126d8d4 0x16c8cd4 0x140bdd4 0x14351cc
Destination + max size 0x176C878 0x12AD8D0 0x1848CBC 0x158BDBC 0x15B51B4
Exploit values
currentDiscBytePointer value at overwrite 0x015f1008 0x01273044 0x016ce444 0x01411544 0x0143a94c
Jump target 0x15ea540 0x0126b7e0 0x01800180 0x01500014 0x01500014
Address of jump target 0x928D24 0x6D9C3C 0x95CF40 0x5f1f38 0x3EA438
Intermediate jump location 0x012811E4 Not required Not required Not required
Intermediate jump target 0x01281340 Not required Not required Not required
IFO offsets
currentDiscBytePointer 0x1c6c (4 bytes) 0x2744 (2 bytes), 0x2c26 (2 bytes) 0x2744 () 0x2744 (4 bytes) 0x277c (4 bytes)
fpIndex 0x24D2 0x29ea 0x2faa 0x2faa 0x2fe2
Payload 0x0e8c 0x2880 0x2d00 0x2bb4 0x2954

3.03

3.03 has a couple of additional tricks going on. There are no jump targets which lie within our controlled range from any buffer overflows, however the jump target 0x15ea540 is very close to the beginning of our IFO file contents (0x15ea620).

The memory between the jump target and the start of the IFO (0x15ea540 - 0x15ea620) is all zeroes, so that's just a NOP-sled. Then the IFO header "DVDVIDEO-VMG" turns out to decode to a conditional relative branch which not only happens to be taken, but also jumps to fully controlled contents later in the IFO:

bnel    s2,a0,pos_015FFF34

In addition, that jump target does not fall within language data, so the 3.03 exploit supports all languages, not just English!


Testing


Conflicts

In order to merge 2 exploits into a single ISO there must be either:

It's more complicated than that, because the currentDiscBytePointer is overwritten byte-by-byte.


< 3.03

These firmwares don't use the same getDiscData stream reader API, instead they manually call getBuffer and then memcpy from that sectorBuffer somewhere else. They still contain the vulnerability, but as it occurs from memcpy of OOB memory into other OOB memory, it is not just immediately possible for the full memory range overflowed with to contain fully controlled contents.

Let's look at 3.02 specifically.

0x256668 - getBufferInternal
0x256888 - getBuffer

Searching calls to getBuffer, it's always a fixed number of sectors, 1 to 4, so as previously stated we can't just overflow straight into fpIndex with controlled contents as in > 3.02.

But, the buffer overflows definitely do still exist. The function at 0x23e560 is a nice self contained one:

long bufferOverflow(void) {
  long lVar1;
  
  lVar1 = getBuffer(s_VIDEO_TS.IFO_0090c210,(long)(int)DAT_013c7840,sectorBuffer,1,0);
  if (lVar1 == 0) {
    someLengthFromIFO = (ushort)sectorBuffer[0] * 0x100 + (ushort)sectorBuffer[1];
    DAT_013c7890 = ((long)(int)((uint)sectorBuffer[4] << 0x18) | (ulong)sectorBuffer[5] << 0x10) +
                   (ulong)sectorBuffer[6] * 0x100 + (ulong)sectorBuffer[7];
    memcpy(&PTR_DAT_013c7898,sectorBuffer + 8,(uint)someLengthFromIFO * 0xc);
    lVar1 = 0;
  }
  return lVar1;
}

The memcpy call can overwrite memory from 0x013c7898 to 0x148788C (0x013c7898 + 0xffff * 0xc). The buffer overflow we are triggering in all other exploits because it gives biggest size is at 0x240284:


      length2 = (ushort)sectorBuffer[uVar33 + 2] * 0x100 + (ushort)sectorBuffer[uVar33 + 3];
      length1 = (ushort)sectorBuffer[uVar33] * 0x100 + (ushort)sectorBuffer[uVar33 + 1];
      length3 = (ushort)sectorBuffer[uVar33 + 4] * 0x100 + (ushort)sectorBuffer[uVar33 + 5];
      DAT_013c9a2e = (ushort)sectorBuffer[uVar33 + 6] * 0x100 + (ushort)sectorBuffer[uVar33 + 7];
      memcpy(&DAT_013c9a30,sectorBuffer + uVar33 + 8,
             ((uint)length1 + (uint)length2 + (uint)length3) * 8);

fpIndex is at 0x13cfaca (leading to OOB call at 0x242f6c), and if we can set that to a controlled value we potentially have an exploit (if there's a good jump target).

fpIndex can be overwritten by either of the memcpy buffer overflows shown with a large enough size, but we're not corrupting it with data coming straight from disc; we only read at most 4 sectors (0x800 * 4) = 0x2000 into sectorBuffer, however we need to memcpy 0x609A bytes from sectorBuffer into 0x13c9a30 to overwrite fpIndex (0x13cfaca-0x13c9a30), so we'll be copying from uncontrolled OOB memory into fpIndex.

So, can we make that OOB memory contain controlled contents? Well, by making use of that buffer overflow, we can shift the question from "can we control fpIndex (0x13cfaca)", to "can we control sectorBuffer + 0x609A = 0x13D331A", since if we control that the memcpy will then copy into fpIndex from an address we can control the contents of.

Looking at all of the copies - maybe you will be lucky and find that it happens to line up that after a series of copies - some value you control ends up in fpIndex. Will need more time on it.


UDF vulnerabilities

The IFO buffer overflows are really easy to find as the IFO parsing is the first thing the DVD player does on EE side. We'll probably want to reverse engineer deeper into things like the actual video decoding, etc, in order to see if more easily exploitable bugs are available; for that, I hope others will help collaborate and share notes.


readPartitionTables stack buffer overflow - Found by ElReino

This is a stack buffer overflow occuring in UDFIO IOP processor module. From 2.10E, at 0xb37e4:

    memcpy(&lengthOfExtendedAttributes,DescriptorBuf + 0xa8,4);
    memcpy(&lengthOfAllocationDescriptors,DescriptorBuf + 0xac,4);
    memset(&AllocationDescriptors,0,8);
    memcpy(&AllocationDescriptors,DescriptorBuf + lengthOfExtendedAttributes + 0xb0,
           lengthOfAllocationDescriptors);

Interestingly, this was actually patched by Sony in firmware 2.14! We see it just uses fixed size of 8 bytes:

  memcpy(local_20,0x5ab8,4);
  memcpy(auStack36,0x5abc,4);
  memset(&local_18,0,8);
  memcpy(&local_18,(int)&PTR_DAT_00005ac0 + local_20[0],8);

To find the vulnerability in an IOP memory dump, search for this instruction sequence:

08 00 06 24       _li        param_3,0x8

Until you get one that matches the memcpy/memset pattern shown above in the decompiler view.

If you are paranoid about cache, there's a really nice ROP gadget in the sound module. To find where the FlushDCache function is in your BIOS, run an IOP RAM dump through this code. Then look for calls to that function to find a nice ROP gadget, for 2.10 it's at 0x57f1c:

        00057f1c 3e 67 01 0c       jal        FlushDCacheWrapper
        00057f20 00 00 00 00       _nop
        00057f24 18 00 bf 8f       lw         ra,local_8(sp)
        00057f28 14 00 b1 8f       lw         s1,local_c(sp)
        00057f2c 10 00 b0 8f       lw         s0,local_10(sp)
        00057f30 01 00 02 24       li         v0,0x1
        00057f34 08 00 e0 03       jr         ra
        00057f38 20 00 bd 27       _addiu     sp,sp,0x20

This will be our initial corrupted return address, then we'll jump to the uncached virtual address of the actual IOP payload entry-point, and first thing will be undoing the corruption from this ROP gadget (sub 0x20 from sp and restore s0/s1). The IOP payload loads a second IOP payload, which loads an ELF into EE RAM, then redirects return address on EE stack and IOP returns gracefully (which resumes EE and triggers EE payload).

Table of addresses for completed ports below. It should be possible to port to all versions from 1.00 - 2.13:

Symbol 2.10 2.12
memcpy overflow 0xb37e4 0xb37e8
copy destination 0x01F6268
return address location 0x1f62ac
jump to return address 0xB3BF0
stage 1 address 0x1f62b0
Flush D Cache IOP 0x0003044
Flush I Cache IOP 0x00002f40
Flush D Cache IOP caller (initial jump address) 0x57f1c
Return address in ISO 0x00818f4
Second return address in ISO 0x0081910

readSectors buffer overflow

In 2.14, Sony removed the bounds check on sizes passed to readSectors, so we can reach the following with controlled sectorCount:

0xb31bc:

iVar1 = readSectors(sectorCount,sectorNumber,0xb6c40);

In PCSX2 emulator, we can exploit this bug by overflowing into the stack (you can just spam payload addresses like 0xa00c0000 to that massive range of controlled memory and it will jump to it).

I was super excited by this, and started writing a nice exploit for 2.14+, but then krHacken burned the disc and found out that it isn't accepted by mechacon as a valid DVD Video, so we can't trigger this bug on the hardware. This makes sense; the change was too random to have been a security regression, especially as 2.14 was a release that fixed the readPartitionTables bug, otherwise it would have seemed too much like a backdoor lol

2.13 bug 0xb33fc ret 0xb37c4