栈平衡和栈转移(Stack-Pivot)
栈平衡
为什么要堆栈平衡
因为要保持栈的大小,使ESP始终指向栈顶
概念
- 函数如果要返回父程序,则在堆栈中进行操作的时候,一定要在RET指令之前,将ESP指向
函数压入栈中时的地址
- 如果通过堆栈传递参数了,那么在函数执行完毕后,要平衡参数导致的堆栈变化
代码示意
1 | //ESP 为 NN |
总结
也就是说当函数在栈中操作时,需要先把ESP
转交给EBP
,然后继续操作,当操作完后,在ret
之前,要先将ESP
恢复成进入栈前的状态(进入栈后每次压入参数都会使栈空间一点点变小,而因为ESP要始终指向栈顶的原因,所以要把它恢复成原来的大小。)最后再将EBP
移除栈,这样就是一个简单的栈平衡。
栈转移
- 在栈空间不够存放paylaod的情况下,需要一个新的地址空间来存放payload。
- 开启了 PIE保护,栈地址未知,我们可以将栈劫持到已知的区域。
概念
劫持栈的rsp(esp),使其指向其他位置,形成一个伪造的栈。这样栈也就被劫持到攻击者控制的内存上去,然后在该位置做ROP。
必要的Gadget
pop EBP;ret
: 释放EBP,并链接伪造的栈
leave;ret
: 更改ESP,指向后续的payload
- 等价于
mov ESP,EBP; pop EBP; ret;
原理
栈转移的原理就是以 pop ebp;ret + 伪造的栈
让程序直接跳转到伪造的栈里面,然后为了保持栈平衡,从而执行leave; ret
,最后继续执行伪造的栈内的payload
过程
- 使用输入函数(如read)将后续的Payload加载到bss段内,也就是伪造的栈
- 通过
pop ebp;ret
来调整EBP寄存器pop ebp;ret
可以使用pop EBX;ret
来代替
- 通过
leave;ret
来更改ESP
,使其指向伪造的栈(bss) - 然后在伪造的栈中执行下一段ROP
注意
- 使用bss作为stack发动ROP攻击可能会失败
- 这是因为(后续ROP使用的GOT等)必要的变量被破坏,以及跳到stack等原因
- 因为
read/write
时,系统内的dl_fixup函数对stack做了很好的保护
- 因为
- 使用的bss段建议在中间区域,如
bss+0x800
左右
参考链接:
https://darkwing.moe/2019/04/15/Pwn学习笔记14-stack-pivot与Off-by-one/
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 偏有宸机!
评论