【栈迁移例题解析】【Write-up】BUUCTF ciscn_2019_es_2
checksec查看程序架构
$ checksec --file ciscn_2019_es_2
[*] '/home/peterl/security/workspace/ciscn_2019_es_2/ciscn_2019_es_2'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
没啥特殊的,注意一下32位就可
ida查看程序伪代码
看起来像是栈溢出,但是read函数限制了读入的字节数,我们看一下栈:
我们发现这时候到return的位置只能溢出一个字,如果这时候程序有一个backdoor函数那就万事大吉。
我们可以找一下,发现了一个hack函数,但这个hack函数完全就是糊弄人的,从中唯一得到的有用信息是system确乎存在于plt表中,这样就不用ret2libc了:
看一下源程序也没有格式化字符串漏洞,我们可以考虑栈迁移
我们只要泄露出ebp,然后再通过第二次leave;ret
就可以将esp更改到我们希望的地方
构建exp
栈迁移的基本思路
我们知道,在call
一个函数的时候,我们会先将下一条指令的地址进栈,再将当前的ebp进栈,然后将当前esp的值赋给ebp,这样就实现了备份下一条指令的地址、ebp原本的值、esp原本的值的作用
然后等到函数结束时,程序会执行leave
指令和ret
指令。leave
指令相当于mov esp, ebp; pop ebp
,意思就是从备份中先恢复esp再恢复ebp;ret
指令相当于pop eip
,意思就是恢复备份的指令地址,这样就能执行函数调用的下一条语句了
那么如果我们更改栈中备份的ebp数据的同时,在程序执行过一次leave; ret
后再执行一遍,那么因为ebp的备份已被更改,所以ebp恢复的就是我们希望的数据,而再执行一遍leave; ret
时,程序是假定ebp中保存的是esp的备份,那么通过这种方法,我们就可以成功地更改esp。
这时我们已经让栈顶指针指向了我们希望的位置,这时由于leave
指令,程序会将我们伪造的栈顶的第一个数据pop
进ebp,然后由于ret
指令再将第二个数据弹进eip中,那么我们就应该在伪造的栈的第二个数据放入system
函数的地址
然后我们就可以当普通的栈溢出构造栈了。
system
后面跟个0,再跟/bin/sh
的地址就