11 Feb 2020

glibc2.29,那咋办🐎

glibc2.29,那咋办🐎

tcache double free 不好使了,👴的青春结束了

Unsortedbin Attack 没了,👴的青春结束了

House of Force 没了,👴的青春结束了

新增防护机制

free

  • tcache

    #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
    

    glibc2.29在tcache_entry结构体中加上一个8字节指针key,位置就是bk的位置,chunk进入tcache后,将chunk->key设置为tcachestruct(heap开头+0x10)

    free时,进tcache前会检查chunk->key是否为tcache,如果是tcache就会检查tcache链中是否有相同的chunk

    把chunk->key改了就可以绕过检查,但是👴寻思👴都能摸到bk了应该也能摸到fd,那👴应该也⑧需要double free了

  • unlink

        /* consolidate backward */
        if (!prev_inuse(p)) {
          prevsize = prev_size (p);
          size += prevsize;
          p = chunk_at_offset(p, -((long) prevsize));
          if (__glibc_unlikely (chunksize(p) != prevsize))
            malloc_printerr ("corrupted size vs. prev_size while consolidating");
          unlink_chunk (av, p);
        }
      
    

    unlink前判断prev_size和要合并的chunk size是否相同,👴overlapping还得改size

malloc

  • unsortedbin

          while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
            {
              bck = victim->bk;
              size = chunksize (victim);
              mchunkptr next = chunk_at_offset (victim, size);
      
              if (__glibc_unlikely (size <= 2 * SIZE_SZ)
                  || __glibc_unlikely (size > av->system_mem))
                malloc_printerr ("malloc(): invalid size (unsorted)");
              if (__glibc_unlikely (chunksize_nomask (next) < 2 * SIZE_SZ)
                  || __glibc_unlikely (chunksize_nomask (next) > av->system_mem))
                malloc_printerr ("malloc(): invalid next size (unsorted)");
              if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
                malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");
              if (__glibc_unlikely (bck->fd != victim)
                  || __glibc_unlikely (victim->fd != unsorted_chunks (av)))
                malloc_printerr ("malloc(): unsorted double linked list corrupted");
              if (__glibc_unlikely (prev_inuse (next)))
                malloc_printerr ("malloc(): invalid next->prev_inuse (unsorted)");
    

    wdnmd

    新增四项检查

    1. 下一个chunk的size
    2. 下一个chunk的prev_size
    3. unsorted bin双向链表的完整性
    4. 下一个chunk的prev_inuse位
    5. 验了unsorted bin双向链表的完整性Unsortedbin Attack直接没了,上香
  • large bin

              else
                {
                  victim_index = largebin_index (size);
                  bck = bin_at (av, victim_index);
                  fwd = bck->fd;
      
                  /* maintain large bins in sorted order */
                  if (fwd != bck)
                    {
                      /* Or with inuse bit to speed comparisons */
                      size |= PREV_INUSE;
                      /* if smaller than smallest, bypass loop below */
                      assert (chunk_main_arena (bck->bk));
                      if ((unsigned long) (size)
    		      < (unsigned long) chunksize_nomask (bck->bk))
                        {
                          fwd = bck;
                          bck = bck->bk;
      
                          victim->fd_nextsize = fwd->fd;
                          victim->bk_nextsize = fwd->fd->bk_nextsize;
                          fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
                        }
                      else
                        {
                          assert (chunk_main_arena (fwd));
                          while ((unsigned long) size < chunksize_nomask (fwd))
                            {
                              fwd = fwd->fd_nextsize;
    			  assert (chunk_main_arena (fwd));
                            }
      
                          if ((unsigned long) size
    			  == (unsigned long) chunksize_nomask (fwd))
                            /* Always insert in the second position.  */
                            fwd = fwd->fd;
                          else
                            {
                              victim->fd_nextsize = fwd;
                              victim->bk_nextsize = fwd->bk_nextsize;
                              fwd->bk_nextsize = victim;
                              victim->bk_nextsize->fd_nextsize = victim;
                            }
                          bck = fwd->bk;
                        }
                    }
                  else
                    victim->fd_nextsize = victim->bk_nextsize = victim;
                }
    

    large bin attack还好使

    large bin 的 nextsize 成环时没有检查,劫持bk_nextsize,写入一个假地址,在插入的时候,会把这个假地址当成一个真的 chunk 进行双向链表插入操作,就会把要插入的chunk地址写入假地址

    两个chunk的size不能相同,否则会进入

                          if ((unsigned long) size
    			  == (unsigned long) chunksize_nomask (fwd))
                            /* Always insert in the second position.  */
                            fwd = fwd->fd;
    

    large bin的 fd_nextsize 需要设置为0,否则会进入

              else
                {
                  size = chunksize (victim);
      
                  /*  We know the first chunk in this bin is big enough to use. */
                  assert ((unsigned long) (size) >= (unsigned long) (nb));
      
                  remainder_size = size - nb;
      
                  /* unlink */
                  unlink_chunk (av, victim);
    

    过不了unlink的检查

top chunk

      victim = av->top;
      size = chunksize (victim);

      if (__glibc_unlikely (size > av->system_mem))
        malloc_printerr ("malloc(): corrupted top size");

使用top chunk时检查size小于system_mem(0x21000)

House of Force上香


Tags:
0 comments



本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议CC BY-NC-ND 4.0)进行许可。

This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License (CC BY-NC-ND 4.0).