STACK
本文最后更新于 2025年11月9日 下午
Stack pivoting
核心汇编
- leave :mov esp,ebp pop ebp;
- ret :pop eip;
原理实现
正常leave,ret还原现场

劫持栈空间(劫持rsp)

例题
ctfshowpwn75
read只允许我们读入0x30,存在后门system,但是没有/bin/sh,架构是i386,那么意味着我们只能读入两个地址(两个值)一个值覆盖old’s ebp,一个值会覆盖ret
程序通过leave,ret还原现场

leave 本质就是
mov esp,ebp; pop ebp那么我们的思路只能是往栈上写/bin/sh然后计算它在栈上的地址,把它的地址传递给backdoor
栈上地址?ebp不就是在栈上的吗?不管是哪个个函数的ebp,都是始终在栈上的
那么我们可以通过leak ctfshow()这个函数保存的old’s ebp来表示我们写入栈上的/bin/sh
具体做法是利用printf不遇到\x00不终止的特性,我们利用第一次read填写junk data(垃圾数据不能存在0这个数据)到ebp的位置,通过printf解析出old’s ebp
1 | |
通过调试,查看old’s ebp到我们读入数据的地址的差值,利用ebp来表示其地址:sAddr = ebp-0x38

然后我们想想怎么实现
- 它的ret—-> system.plt
- system的参数为Addr—->’/bin/sh’
这里有一点要注意,i386架构下的程序通过栈传参,也就是说通过esp或者ebp传参
通过这个,要想将我们写的’/bin/sh’传递给system,就需要控制esp
- 通过ROPgadget
- 通过leave
因为只能写一个地址,那么ROPgadget完全无法实现,那么我们就只能考虑leave(mov esp,ebp;pop ebp)
通过修改old’s ebp的值,在退出ctfshow()这个函数的时候,esp就会因为old’s ebp的值而被我们操控
EXP
1 | |
ctfshowpwn76
先输入一个字符串,要求不能超过30的长度,
接着对这个字符串进行base64解码,得到解码后的长度,检查长度是否大于0xC
最后将小于0xC的解码后的字符串复制到input中,进行hash验证,如果通过,则进入backdoor
这里则出现危险函数,mmcpy,v4的空间只到了ebp-8h,但是a1的值可以到0xC,那么就会修改ebp所指向的栈基地址
那么就可以往input上写入payload,长度只能为0xC,也就是说只能玩里边写入4个值(p32()),如果大于则程序会结束,将栈基址修改成input的地址,劫持栈空间,控制程序流
这里要注意这里是通过leave,retn来还原现场的,那么就会出现pop rbp,所以payload的前四个字节用垃圾数据填充**(具体参看:原理-劫持栈空间)**

1 | |