栈迁移原理深入理解以及实操
字数 1418 2025-08-20 18:17:00

栈迁移原理深入理解及实战应用

前言

栈迁移技术主要用于解决栈溢出空间受限的情况。当题目只允许覆盖ebp和ret_addr等少量字节时,传统ROP链无法完整布置,此时需要通过栈迁移将栈转移到其他可控制区域(如bss段)来执行攻击。

栈迁移核心原理

关键指令分析

栈迁移依赖于leave; ret指令组合,这两个指令的功能如下:

  1. leave指令

    • 等价于mov esp, ebp; pop ebp
    • 先将ebp值赋给esp,使esp和ebp指向同一地址
    • 然后弹出栈顶内容到ebp寄存器
  2. ret指令

    • 等价于pop eip
    • 将栈顶内容弹出到eip寄存器,作为下一条执行指令

栈迁移执行流程

  1. 第一次leave;ret

    • mov esp, ebp:esp和ebp指向同一地址
    • pop ebp:将栈顶内容(攻击者控制的迁移目标地址)存入ebp
    • ret:执行第二次leave;ret
  2. 第二次leave;ret

    • mov esp, ebp:esp迁移到目标地址
    • pop ebp:弹出无用内容
    • ret:执行目标地址处的ROP链

栈迁移实战案例

案例1:ciscn_2019_es_2(32位)

题目特点

  • 32位程序,开启NX保护
  • 存在printf格式化字符串漏洞
  • 输入缓冲区仅40字节,但允许读入48字节

利用思路

  1. 泄露ebp地址

    • 利用printf不自动补\0的特性,填满缓冲区泄露ebp
  2. 构造ROP链

    • 在bss段布置system地址和"/bin/sh"字符串
    • 计算s参数地址与ebp的偏移(0x38)
  3. 栈迁移执行

    pl2 = 'aaaa' + p32(system) + p32(1) + p32(binsh) + "/bin/sh\00"
    pl2 = pl2.ljust(0x28,'p')
    pl2 += p32(s) + p32(leave_ret)
    

关键点

  • 使用send而非sendline避免添加\0截断
  • 精确计算binsh字符串位置(ebp-0x28)

案例2:gyctf_2020_borrowstack(64位)

题目特点

  • 64位程序
  • 第一个read可溢出16字节(覆盖rbp和ret_addr)
  • 第二个read允许在bss段写入大量数据

利用思路

  1. 迁移到bss段

    • 将栈迁移到bank+0xd0位置,避开关键区域
  2. 泄露libc地址

    pl2 = 'a'*0xd0 + p64(0) + p64(rdi_addr) + p64(puts_got) + p64(puts_plt) + p64(main)
    
  3. 二次迁移执行onegadget

    pl4 = p64(0) + p64(one_gadget) + p64(0)*10
    

关键点

  • bss段布局需避开got表等关键区域
  • 需要保持栈结构完整性(填充无用数据)

案例3:[Black Watch入群题]PWN

题目特点

  • 第一个read无溢出,第二个read存在溢出
  • 需在bss段构造完整ROP链

利用思路

  1. 首次输入布置ROP

    pl = p32(0) + p32(puts_plt) + p32(main) + p32(puts_got)
    
  2. 栈迁移泄露libc

    pl2 = 'a'*0x18 + p32(s_addr) + p32(leave_ret)
    
  3. 二次迁移getshell

    pl3 = p32(0) + p32(system) + p32(0) + p32(binsh)
    

通用利用技巧

  1. 地址计算

    • 32位:参数地址通常为ebp-0x28
    • 64位:注意bss段与got表的距离
  2. 指令查找

    • leave; ret指令可通过ROPgadget工具查找
    • 32位常见地址:0x080485FD
    • 64位常见地址:0x0000000000400699
  3. 输入方式选择

    • read函数:优先使用send
    • gets/scanf函数:必须使用sendline
  4. 栈结构维护

    • 迁移后需保持栈平衡
    • 必要时填充无用数据(如p64(0)*10)

总结

栈迁移技术通过精心控制ebp和esp寄存器,将执行环境转移到攻击者可控区域。关键在于:

  1. 精确计算目标地址偏移
  2. 合理布置两次leave;ret的执行流程
  3. 根据题目特点选择适当的输入方式和栈结构维护方法

掌握栈迁移技术可有效解决栈溢出空间受限的难题,是现代pwn题中的重要攻击手段。

栈迁移原理深入理解及实战应用 前言 栈迁移技术主要用于解决栈溢出空间受限的情况。当题目只允许覆盖ebp和ret_ addr等少量字节时,传统ROP链无法完整布置,此时需要通过栈迁移将栈转移到其他可控制区域(如bss段)来执行攻击。 栈迁移核心原理 关键指令分析 栈迁移依赖于 leave; ret 指令组合,这两个指令的功能如下: leave指令 : 等价于 mov esp, ebp; pop ebp 先将ebp值赋给esp,使esp和ebp指向同一地址 然后弹出栈顶内容到ebp寄存器 ret指令 : 等价于 pop eip 将栈顶内容弹出到eip寄存器,作为下一条执行指令 栈迁移执行流程 第一次leave;ret : mov esp, ebp :esp和ebp指向同一地址 pop ebp :将栈顶内容(攻击者控制的迁移目标地址)存入ebp ret :执行第二次leave;ret 第二次leave;ret : mov esp, ebp :esp迁移到目标地址 pop ebp :弹出无用内容 ret :执行目标地址处的ROP链 栈迁移实战案例 案例1:ciscn_ 2019_ es_ 2(32位) 题目特点 32位程序,开启NX保护 存在printf格式化字符串漏洞 输入缓冲区仅40字节,但允许读入48字节 利用思路 泄露ebp地址 : 利用printf不自动补\0的特性,填满缓冲区泄露ebp 构造ROP链 : 在bss段布置system地址和"/bin/sh"字符串 计算s参数地址与ebp的偏移(0x38) 栈迁移执行 : 关键点 使用send而非sendline避免添加\0截断 精确计算binsh字符串位置(ebp-0x28) 案例2:gyctf_ 2020_ borrowstack(64位) 题目特点 64位程序 第一个read可溢出16字节(覆盖rbp和ret_ addr) 第二个read允许在bss段写入大量数据 利用思路 迁移到bss段 : 将栈迁移到bank+0xd0位置,避开关键区域 泄露libc地址 : 二次迁移执行onegadget : 关键点 bss段布局需避开got表等关键区域 需要保持栈结构完整性(填充无用数据) 案例3:[ Black Watch入群题 ]PWN 题目特点 第一个read无溢出,第二个read存在溢出 需在bss段构造完整ROP链 利用思路 首次输入布置ROP : 栈迁移泄露libc : 二次迁移getshell : 通用利用技巧 地址计算 : 32位:参数地址通常为ebp-0x28 64位:注意bss段与got表的距离 指令查找 : leave; ret 指令可通过ROPgadget工具查找 32位常见地址:0x080485FD 64位常见地址:0x0000000000400699 输入方式选择 : read函数:优先使用send gets/scanf函数:必须使用sendline 栈结构维护 : 迁移后需保持栈平衡 必要时填充无用数据(如p64(0)* 10) 总结 栈迁移技术通过精心控制ebp和esp寄存器,将执行环境转移到攻击者可控区域。关键在于: 精确计算目标地址偏移 合理布置两次leave;ret的执行流程 根据题目特点选择适当的输入方式和栈结构维护方法 掌握栈迁移技术可有效解决栈溢出空间受限的难题,是现代pwn题中的重要攻击手段。