House of Orange 的基本方法
来看看House of Orange 的用法.
0x00 概述
House of Orange 的利用比较特殊,首先需要目标漏洞是堆上的漏洞但是特殊之处在于题目中不存在 free 函数或其他释放堆块的函数。我们知道一般想要利用堆漏洞,需要对堆块进行 malloc 和 free 操作,但是在 House of Orange 利用中无法使用 free 函数,因此 House of Orange 核心就是通过漏洞利用获得 free 的效果。
0x01 原理
House of Orange 的核心在于没有free函数的情况下得到一个释放的堆块(unsorted bin). 当我们的top chunk不能慢慢组malloc 的分配需求的时候, 会将top chunk放到unsorted bin中.
我们来看一下这个过程的详细情况,我们假设目前的 top chunk 已经不满足 malloc 的分配需求。 首先我们在程序中的malloc调用会执行到 libc.so 的_int_malloc函数中,在_int_malloc函数中,会依次检验 fastbin、small bins、unsorted bin、large bins 是否可以满足分配要求,因为尺寸问题这些都不符合。接下来_int_malloc函数会试图使用 top chunk,在这里 top chunk 也不能满足分配的要求,因此会执行如下分支。1
2
3
4
5
6
7
8
9/*
Otherwise, relay to handle system-dependent cases
*/
else {
void *p = sysmalloc(nb, av);
if (p != NULL && __builtin_expect (perturb_byte, 0))
alloc_perturb (p, bytes);
return p;
}
此时 ptmalloc 已经不能满足用户申请堆内存的操作,需要执行 sysmalloc 来向系统申请更多的空间。 但是对于堆来说有 mmap 和 brk 两种分配方式,我们需要让堆以 brk 的形式拓展,之后原有的 top chunk 会被置于 unsorted bin 中。
然后我们要实现的是brk的chunk, 这里需要绕过libc中的一些check.1
if ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max))
- malloc 的尺寸不能大于mmp_.mmap_threshold
如果所需分配的 chunk 大小大于 mmap 分配阈值,默认为 128K,并且当前进程使用 mmap() 分配的内存块小于设定的最大值,将使用 mmap() 系统调用直接向操作系统申请内存。
在 sysmalloc 函数中存在对 top chunk size 的 check,如下1
2
3
4assert((old_top == initial_top(av) && old_size == 0) ||
((unsigned long) (old_size) >= MINSIZE &&
prev_inuse(old_top) &&
((unsigned long)old_end & pagemask) == 0));
这里检查了 top chunk 的合法性,如果第一次调用本函数,top chunk 可能没有初始化,所以可能 old_size 为 0。 如果 top chunk 已经初始化了,那么 top chunk 的大小必须大于等于 MINSIZE,因为 top chunk 中包含了 fencepost,所以 top chunk 的大小必须要大于 MINSIZE。其次 top chunk 必须标识前一个 chunk 处于 inuse 状态,并且 top chunk 的结束地址必定是页对齐的。此外 top chunk 除去 fencepost 的大小必定要小于所需 chunk 的大小,否则在_int_malloc() 函数中会使用 top chunk 分割出 chunk。
我们总结一下伪造的 top chunk size 的要求
- 伪造的 size 必须要对齐到内存页, 一般是4kb
- size要大于MINSIZE(0X10)
- size要小于之后申请的chunk size + MINSIZE
- size的prev inuse = 1
之后原有的 top chunk 就会执行_int_free从而顺利进入 unsorted bin 中。
- size的prev inuse = 1
内存页对齐
什么是对齐到内存页呢?我们知道现代操作系统都是以内存页为单位进行内存管理的,一般内存页的大小是 4kb。那么我们伪造的 size 就必须要对齐到这个尺寸。在覆盖之前 top chunk 的 size 大小是 20fe1,通过计算得知 0x602020+0x20fe0=0x623000 是对于 0x1000(4kb)对齐的。
因此我们伪造的 fake_size 可以是 0x0fe1、0x1fe1、0x2fe1、0x3fe1 等对 4kb 对齐的 size。而 0x40 不满足对齐,因此不能实现利用。
一个例子1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int main(void)
{
void *ptr;
ptr=malloc(0x10);
ptr=(void *)((int)ptr+24);
*((long long*)ptr)=fake_size;
malloc(0x2000);
malloc(0x60);
}