house of force:

发布于 2024-05-17  152 次阅读


参考学长博客:House Of Force-原理 | Pwn进你的心 (ywhkkx.github.io)

1.利用:

这一个利用是因为libc 在管理的时候默认了top chunk 的size的大小是合法的,所以不会去检查top chunk的size,因此如果一个程序存在top chunk可修改的漏洞的情况,那么我们就可以把这个chunk的大小设置为0xffffffff(x86)

假如这个时候我们的top_chunk = 0x601200,然后malloc(0xffe00020)然后就对malloc申请的size进行检查,0xffe00030 < top_chunk_size,所以top chunk的新地址就为:0xffe00030+0x601200=0x100401230,因为是x86的环境,所以最高位溢出,所以top_chunk = 0x401230。

因此这个时候我们再一次malloc的时候我们的top_chunk就变成了0x401230。

2.分割机制和利用点:

top chunk的作用是作为后备空间来使用的,也就是当我们的bin里面没有chunk可提供的时候 ,就会从top chunk里面分割出来

victim = av->top; /* 获取addr of top chunk */
size   = chunksize(victim); /* 获取top chunk size */
if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))
{
   remainder_size = size - nb; /* 计算剩下的size */
   remainder      = chunk_at_offset(victim, nb);
   av->top        = remainder; /* 修改top chunk */
   set_head(victim, nb | PREV_INUSE |
          (av != &main_arena ? NON_MAIN_ARENA : 0)); /* 设置top chunk的头 */
   set_head(remainder, remainder_size | PREV_INUSE); /* 设置剩下chunk的头 */

   check_malloced_chunk(av, victim, nb);
   void *p = chunk2mem(victim);
   alloc_perturb(p, bytes);
   return p;
}

首先libc会检测用户申请的大小,分析top chunk能不能够承担得起,如果能承担就会从top chunk的head的位置,以用户申请所匹配的chunk大小为偏移量,把top chunk向后移到新的位置,原来的top chunk head就作为新的堆被分给用户了。

所以如果我们能够控制用户申请多大小为任意值,我们就可以把top chunk移到内存的任意位置,然后就可以控制用户的内存了。if ((unsigned long) (size) >= (unsigned long) (nb + MINSIZE))

保护的检查:只有top chunk的size大小等于申请的size,才会有后续的操作了,而pwn中我们劫持的是malloc_hook、got表等指针,到那时这些指针和我们的top chunk相差很远,不可能直接就能将top_chunk给移到这些指针上。

破解的方法:大小检查时如果其的数据类型是unsigned long,如果能够通过某些漏洞(如溢出)把top chunk的size字段给改为-1,那么在做这个检查时,size 就变成了无符号整数中最大的值,那么无论我们申请多大的chunk都能够满足条件。

利用条件:1.用户能够篡改top chuink的size字段;2.用户能够申请任意大小的堆内存(包括负数)

3.版本:

libc-2.23:

 /* Try to use top chunk */
 /* Require that there be a remainder, ensuring top always exists */
 if ( (remainder_size = chunksize(top(ar_ptr)) - nb) < (long)MINSIZE)
{


   /* If the request is big and there are not yet too many regions,
      and we would otherwise need to extend, try to use mmap instead. */
   if ((unsigned long)nb >= (unsigned long)mmap_threshold &&
       n_mmaps < n_mmaps_max &&
      (victim = mmap_chunk(nb)) != 0)
     return victim;
     /* 如果申请字节超过“topchunk->size”,调用mmap_chunk */


   /* Try to extend */
   malloc_extend_top(ar_ptr, nb);
   if ((remainder_size = chunksize(top(ar_ptr)) - nb) < (long)MINSIZE)
  {

     /* A last attempt: when we are out of address space in a
        non-main arena, try mmap anyway, as long as it is allowed at
        all. */
     if (ar_ptr != &main_arena &&
         n_mmaps_max > 0 &&
        (victim = mmap_chunk(nb)) != 0)
       return victim;
       /* 如果,第一次调用mmap_chunk没有成功,则再调用一次 */

     return 0; /* propagate failure */
  }
}
 victim = top(ar_ptr);
 set_head(victim, nb | PREV_INUSE); /* 设置top chunk的头 */
 top(ar_ptr) = chunk_at_offset(victim, nb);
 set_head(top(ar_ptr), remainder_size | PREV_INUSE); /* 设置剩下chunk的头 */
 check_malloced_chunk(ar_ptr, victim, nb); /* 这个检查几乎没有影响 */
 return victim;

这一个版本可以完美利用,通过“topchunk -> size”判断是否调用“mmap_chunk”

libc-2.27:

if (av != &main_arena)
  {
     heap_info *old_heap, *heap;
     size_t old_heap_size;

     /* First try to extend the current heap. */
     old_heap = heap_for_ptr (old_top);
     old_heap_size = old_heap->size;
     if ((long) (MINSIZE + nb - old_size) > 0
         /* top chunk不够用,grow_heap扩展top chunk的空间 */
         /* 要打House Of Force,这个if一定不成立(old_size非常大) */
         && grow_heap (old_heap, MINSIZE + nb - old_size) == 0)
      {
         av->system_mem += old_heap->size - old_heap_size;
         set_head (old_top, (((char *) old_heap + old_heap->size) - (char *) old_top)
                   | PREV_INUSE);
      }
     else if ((heap = new_heap (nb + (MINSIZE + sizeof (*heap)), mp_.top_pad)))
      {
         /* Use a newly allocated heap. */
         heap->ar_ptr = av;
         heap->prev = old_heap;
         av->system_mem += heap->size;
         /* Set up the new top. */
         top (av) = chunk_at_offset (heap, sizeof (*heap));
         set_head (top (av), (heap->size - sizeof (*heap)) | PREV_INUSE);

         /* Setup fencepost and free the old top chunk with a multiple of
            MALLOC_ALIGNMENT in size. */
         /* The fencepost takes at least MINSIZE bytes, because it might
            become the top chunk again later. Note that a footer is set
            up, too, although the chunk is marked in use. */
         old_size = (old_size - MINSIZE) & ~MALLOC_ALIGN_MASK;
         set_head (chunk_at_offset (old_top, old_size + 2 * SIZE_SZ), 0 | PREV_INUSE);
         if (old_size >= MINSIZE) /* 需要分割 */
          {
             set_head (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ) | PREV_INUSE);
             set_foot (chunk_at_offset (old_top, old_size), (2 * SIZE_SZ));
             set_head (old_top, old_size | PREV_INUSE | NON_MAIN_ARENA);
             _int_free (av, old_top, 1);
          }
         else /* 不需要分割 */
          {
             set_head (old_top, (old_size + 2 * SIZE_SZ) | PREV_INUSE);
             set_foot (old_top, (old_size + 2 * SIZE_SZ));
          }
      }
     else if (!tried_mmap)
       /* We can at least try to use to mmap memory. */
       goto try_mmap;
  }
................

部分“av == &main_arena”的代码,程序里面多了很多的检查:

/* top chunk is OK */
check_chunk (av, av->top);
​/* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;static void
do_check_chunk (mstate av, mchunkptr p)
{
 unsigned long sz = chunksize (p);
 /* min and max possible addresses assuming contiguous allocation */
 char *max_address = (char *) (av->top) + chunksize (av->top);
 char *min_address = max_address - av->system_mem;
   /* 这里就是问题的关键 */
   /* 因为“topchunk->size”被设置得非常大,所以max_address和min_address也非常大 */
   /* 这个设置范围的操作打死了House Of Force */

 if (!chunk_is_mmapped (p))
  {
     /* Has legal address ... */
     if (p != av->top)
      {
         if (contiguous (av))
          {
             assert (((char *) p) >= min_address);
             /* 因为min_address非常大,重新申请的chunk地址不可能大于它 */
             assert (((char *) p + sz) <= ((char *) (av->top)));
          }
      }
     else
      {
         /* top size is always at least MINSIZE */
         assert ((unsigned long) (sz) >= MINSIZE);
         /* top predecessor always marked inuse */
         assert (prev_inuse (p));
      }
  }
 else if (!DUMPED_MAIN_ARENA_CHUNK (p))
  {
     /* address is outside main heap */
     if (contiguous (av) && av->top != initial_top (av))
      {
         assert (((char *) p) < min_address || ((char *) p) >= max_address);
      }
     /* chunk is page-aligned */
     assert (((prev_size (p) + sz) & (GLRO (dl_pagesize) - 1)) == 0);
     /* mem is aligned */
     assert (aligned_OK (chunk2mem (p)));
  }
}

这里在检查的时候添加了一个“范围”(min_address & max_address),这里想要大Hounse of Force,就需要把“topchunk->size“设置很大,导致这个”范围“很极端,不可能成立

4.总结:

所以这个漏洞只能在libc-2.23连使用


The world's full of lonely people afraid to make the first move.