堆栈平衡问题
pwn的坑之堆栈平衡
今天做了两道最基本的溢出题,就是劫持控制流到后门函数,思路和方法理论上是一样的,但是一道能打通一道打不通,至今百思不得其解,因此记录一下以待来日
题目说明
ctfshow-pwn02
第一题,是ctfshow上的题目,给了一个32位程序stack,拖到IDA里看一下,main调用了pwnme函数,溢出点在pwnme,还有一个stack函数调用的system


然后checksec看了下只开了NX,但无所谓了,又不执行shellcode,管它NX呢
接下来就是一些算偏移,找返回地址的操作
一系列操作以后得到偏移offset=13,stack函数的地址=0x804850f
然后写脚本,填充好垃圾数据,没开PIE的话把stack函数的地址写死就行,写出如下一个简单的脚本:
1 | |
运行以后就可以直接拿到shell了
BUUCTF-Pwn-rip
然后来到第二题,也就作妖的这题,这是BUUCTF上的题目,Pwn下的第二题rip,给了一个pwn1的64位ELF文件(从下图的file输出可以看到)
拖到IDA里可以看到溢出点发生在main函数的gets
然后有一个fun函数,直接调用了system
按道理直接劫持到这个fun就可以
那就算一下偏移,看到main里面的缓冲区在rbp-0xf,+8,offset=23
再checksec,看到没开PIE
那就可以直接在脚本里写死fun函数的地址,objdump看下fun函数在哪,如下图
于是按道理,学着第一题的思路,填充好offset,把返回地址写死成0x401186就肯定可以拿到shell,脚本也是这么写的
1 | |
但是,离谱的事情出现了,每次在interactive以后都会收到EOF
然后,如果改成下面这样的利用脚本,就没有问题
1 | |
是的,仅仅是返回地址+1,后面又陆续尝试了别的返回地址,发现只要跳过fun函数的第一条指令,即push rbp,就可以
博主大感震撼并表示不理解,遂尝试gdb单步调试,看看到底怎么个事儿
从下面这张图可以明显看到rsp指向了我们要的0x401186,按道理ret指令结束后一定是去执行fun函数
并且接着si调试,可以看到,甚至它真的进到fun函数里面了
然后call system这里下个断点,可以正常执行完call指令前的所有指令
但是,最离谱的是,现在,在call system这里按下c键,让它继续执行,直接就崩溃
目前的理解
在网上搜索一番后,得知是堆栈平衡问题,在64位程序下,需要堆栈进行16位对齐,可以采用填充ret指令的方法
对于上述第二题,为什么必须要跳过push rbp指令,是因为push操作会破坏已经构造好的堆栈寄存器,所以不能劫持到0x401186
而由于第一题是32位,不存在堆栈平衡问题,所以可以按照上述方法打通
总结一下,做题时遇到莫名其妙的EOF,考虑避免劫持到带有push rbp指令的地址,如果还不行,考虑用ret指令填充到16的倍数