1) We need to find a buffer overflow, and
2) figure out how to exploit it.
I think the best candidate so far is this thread.
(I think. Anyone else know of anything that looks like an obvious buffer overrun?)
Some resources on buffer overflows:
http://www.phrack.org/show.php?p=49&a=14 - Stack smashing/buffer over runs in general
http://www.phrack.org/phrack/56/p56-0x0f - MIPS shellcode construction
3) We need MIPS assembly code that will load something and run it.
I cannot supply #1 or #2. It will take a lot more intelligence, experience and time than I presently have. (The fact that I don't currently own a PSP doesn't help things either.)
But I've been looking for ways to construct #3, and I think I can do it. nem's post in this thread
contains the memory addresses of two functions that can allow us to load code: sceIoOpen and sceIoRead. These are both C style functions, and more importantly, both of them take four arguments or less. Which means that they can be called using only the normal MIPS call registers, no pushing stuff onto the stack. (Though that isn't too hard, I guess.)
I'm not sure if these are firmware routines or not. If they're not, they might not be in the same location in memory every time, which would shoot this all to hell. Also, these routines may need to be called via the MIPS syscall instruction instead of via jump. I hope nem can tell me if I'm doing the calling wrong.
So anyway, here is my first attempt at a program loader in MIPS assembly. NOTE: I have not tried to assemble this yet. If someone wants to try that and tell me the results, fine. I expect this still needs some tweaking. After it assembles, then I'll have to go back through it with a hex editor and make sure there are no zero bytes. This code must be made to avoid having any byte in it be equal to 0. And code with a 0 byte in it will generally not work as buffer overflow code, since the buffer overflowing will stop at a zero byte. (See Phrack article(s).)
The code is pretty simple. It just (tries to) load up the raw MIPS machine code from the file "PSP_GAME/HOMEBREW/HOMEBREW.MIPS" on the memory stick. It loads it into memory, and then transfers control to it. The code is intentionally crafted to avoid having any byte in it be equal to 0. And code with a 0 byte in it will generally not work as buffer overflow code, since the buffer overflowing will stop at a zero byte. This is why I'm using weird instructions like "addiu" instead of the usual "li". That turns into an "ori", which contains a zero byte.
This code alone is not enough. We also need some way to smash the stack on PSP 1.5, which is how we will insert and run this code. Once we can do that, we should have enough leverage to load an ELF file from the memory stick. Baby steps...
Code: Select all
main: addiu $a0, $zero, filen ; 1st arg to open() - filename.
slti $a1, $zero, -0xFFFF ; 2nd rg to open() - operation = 1 = reading.
jal 0x109f50bc ; Call sceIoOpen().
; If we were smart, here we'd check $v0 (the return value from open()) to make
; sure it's not -1, which would indicate failure.
; But we're just going to assume that the open() worked and run with it.
andi $a0, $v0, 0xFFFF ; First arg to read() - copy fd from $v0 to $a0.
addiu $a1, $zero, 0x09FFF001 ; Second arg to read(), where to store read data.
addiu $a2, $zero, 0xFFE ; Third arg to read(), how much to load. 4094 bytes.
jal 0x6a638d83 ; Call sceIoRead()
; Again, if we were smart, we'd check to see if $v0 contained the proper value.
; In this case, it should be 0x0EEE, which is the number of bytes read.
; But again, we're just gonna hope it all worked...
j 0x09FFF001 ; Jump to loaded code.
filen: .ascii "ms0:/PSP_GAME/HOMEBREW/HOMEBREW.MIPS"
The code is available as loader.s at the URL in my sig.
Tomorrow I'll try and assemble this and run it through the SPIM MIPS emulator. Right now, bedtime...