栈迁移
详细见:栈迁移原理介绍与应用 - Max1z - 博客园 (cnblogs.com)
其实际上的作用就是控制esp指向已构造好的payload区(覆盖量不够的情况,可先构造好一段payload到特定的内存段上);
原理
利用栈平衡的操作;
32位调用函数时,会有如下操作发生:
1 | //第一步:操作执行流 |
第三步的即为第二部的逆运算,可以称这步为Leave,如果能够劫持在函数里的旧ebp值,就可以使得 pop ebp 到一个可控的地方,栈顶的位置是由ebp的值而控制:mov esp,ebp;从而可能影响esp,从而控制进程流;
流程
如下图所示:
意思是在Leave中,需要颠倒1,2行的内容;
如何颠倒,很简单,用gadget思想;
即首先控制旧ebp和ret地址,让ret地址返回到新的一组Leave中去,此时可以把esp的值控制为第一次的ebp的值,使得栈顶转移到另一处内存空间,达成栈迁移;
执行流程:
- 使用gadget寻找新一组的Leave地址:NewLeaveAddr,以及目标栈顶位置:AimAddr;
- 覆盖旧址ebp为 AimAddr-4 (64位-8,因为第二次执行Leave时,会再次pop ebp,使得esp下降,即往高地址走一格)
- 覆盖ret地址为 NewLeaveAddr;
执行之后,新的栈顶指向AimAddr(此时还未执行pop eip),栈底指向AimAddr-4处的数值;
运用情况
这个技术运用于栈溢出返回字节不够时的情况,此时只用覆盖ebp和ret地址就行;
能够使用该技术的情景:
- 存在leave ret gadget;
- 存在可执行 payload 的内存段;
一般而言,能执行shellcode的地方直接ret就行了,不需要这么复杂,不能执行指令的片段上,此时需要运用在栈上,使得控制的栈帧介于输入的变量缓冲区上,把可覆盖区域尽量拉长,利用已填写的 payload 再次实现经典栈溢出;
使用实例
上图为一个栈帧,此时最左侧为变量数组下标0处,也是控制esp指向的目标位置;
- 确定旧ebp与该变量的偏移,因为可以通过格式化字符串泄露旧ebp内容,从而动态地计算出此时此刻变量在栈中的地址:AimAddr;
- 找到gadget NewLeaveAddr,此时覆盖ebp处为变量地址,ret地址为gadget地址;
- 可知当执行之后,esp会减少一格到此变量下标1处,且此时(pop eip)即ret还没执行;
聪明如你,当现在的情况即是执行ret的时刻,那么后面的内容也就是传统栈溢出所需要填充的内容了;
这个时候就可以在变量上面直接地填写,不需要在变量溢出后填写;
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Second_BC の BloG!