参考学长博客: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连使用
Comments | NOTHING