ctfshow 萌新赛pwn签退题
明明是叫萌新赛,整个签退题我以为很简单,拿到程序checksec一看,32位,竟然只开了NX,以为很简单;然后特么的写了几个月,从学栈开始,到格式化字符串、整数溢出,后面到堆,每次都会回来看一眼这题,丫的每次都不会,每次都™怀疑我学了个寂寞。
完全参照大佬的博客完成,在最后会附上大佬的博客;本WP仅仅为了梳理本人对该题的理解
题目分析
程序几乎没有漏洞,可见的漏洞点存在于当进入选项3的流程时,会询问你是否对你的输入进行0-2的操作,然后判断你的选项是否小于等于2,如果是,就执行(0x804B048+choice)函数,此处存在漏洞,只进行了choice小于等于2的判断,而choice是整型,即可以输入负数,也就是说,我们几乎可以执行任何小于(0x804B048)的函数
这题需要利用setbuf函数,大部分pwn题当中,setbuf用来对标准输入输出流清零。仔细观察还会发现,stdin并不是0,而是在stdio库中设置的一个文件流,所以也是作用在stdio库中的函数,类似fwrite、fread、gets等函数。下面举个例子
1 |
|
上述程序运行后buf1最后会直接输出用户输入buf中的内容。
同时setbuf并没有设置长度的参数,设置长度需要使用setvbuf,所以默认情况下setbuf设置的缓冲区长度为默认的4096。
解题思路
通过输入操作选项,使得我们执行setbuf(fd=fopen(“/dev/null”), buf)函数,然后在0x08048742函数中调用了fwrite(ptr, 1u, n, s)往buf里面写入数据,这里我们选择最后一次输入的buf作为setbuf的第二个参数,然后构造栈溢出以及栈迁移。参考大佬的栈溢出构造逻辑如下:
add(rop) -> add(buf1) -> buf(buf2) -> add(buf3) -> add(buf4) -> setbuf(fd, buf4) -> post(buf1) -> post(rop) -> 栈溢出,利用ROP链
然后构造第一个ROP,这里利用printf泄露printf_got获得libc地址,找到system以及binsh的地址,同时构造fread读入向.bss段读入system以及binsh,再把栈迁移到写入了system以及binsh的.bss段。然后构造第二个ROP去执行system(“/bin/sh”)。
最后会出现的问题
.bss段是从0x804b000开始的,当把栈迁移到0x804b100时,会出现system执行binsh失败;大佬给出了两个原因
- 当执行system时,system函数会获取系统的环境变量envp,而全局变量_environ指向了栈上的envp,当我们的rop链过长时,就有可能会覆盖了_environ的值,如果该值被覆盖成了无效地址,system就无法执行,但在这题中,因为第一个ROP链并不过长,所以并不会覆盖到该全局变量。
- system栈地址空间不足,程序的可读可写地址空间是从0x804b000-0x804c000,当我改栈地址为0x804b100时,可用空间只有0x100;所以将栈地址改为0x804b300以后都可以(这是试出来的)。
附上大佬exp
1 | #! /usr/bin/env python |
最后附上参考大佬的文章的地址
- 本文标题:ctfshow签退题
- 本文作者:LOLOLO
- 创建时间:2021-11-26 18:54:30
- 本文链接:https://lololo-pwn.github.io/2021/11/26/ctfshow签退题/
- 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!