一、第一种类型的原理
House of Orange 的利用比较特殊,首先需要目标漏洞是堆上的漏洞但是特殊之处在于题目中不存在 free 函数或其他释放堆块的函数。我们知道一般想要利用堆漏洞,需要对堆块进行 malloc 和 free 操作,但是在 House of Orange 利用中无法使用 free 函数,因此 House of Orange 核心就是通过漏洞利用获得 free 的效果。如我们前面所述,House of Orange 的核心在于在没有 free 函数的情况下得到一个释放的堆块 (unsorted bin)。 这种操作的原理简单来说是当前堆的 top chunk 尺寸不足以满足申请分配的大小的时候,原来的 top chunk 会被释放并被置入 unsorted bin 中,通过这一点可以在没有 free 函数情况下获取到 unsorted bins。
这里用代码解释下:
1 | malloc(size) |
然后unsorted bin就可以拿来切割了,后面的操作看具体情况而定。
一般来说,topchunk都是很大的,通过malloc去消耗topchunk,显然不现实。但是呢,我们可以通过溢出修改topchunk的大小,但是topchunk的size有挺多检查的,所以要小心点改,要满足以下要求:
- 伪造的 size 必须要对齐到内存页
2.size 要大于 MINSIZE(0x10)
3.size 要小于之后申请的 chunk size + MINSIZE(0x10)
4.size 的 prev inuse 位必须为 1
之后原有的 top chunk 就会执行_int_free
从而顺利进入 unsorted bin 中,即:
1 | assert((old_top == initial_top(av) && old_size == 0) || |
topchunk内存页对齐,size > topchunk_size > 0x10, pre_used位为1
重点是这个页对齐,一般来说操作系统都是对于4kb对齐的,
例如,topchunk的地址是0x602020,topchunksize为0x20fe0,通过计算得知 0x602020+0x20fe0=0x623000 是对于 0x1000(4kb)对齐的,因此我们伪造的 fake_size 可以是 0x0fe1、0x1fe1、0x2fe1、0x3fe1 等对 4kb 对齐的 size,一般选取的是0xfe1。
看一道题:巅峰极客一道pwn题:
漏洞很明显,一个UAF,一个溢出7字节的edit,而且申请的size为小于0x1000,有打印函数,但是只有一个chunk可以操作,想要泄露地址,一般情况下是要堆去阻隔,但是house of orange真好,可以实现放入unsorted bin中,而且还有残留地址,所以这题直接可以打了,先分析下:
首先页对齐要求,先看下tpochunk大小:0x20f91
根据惯例:
所以取后三位得到fake_topchunksize: 0xf91
修改并释放出来:malloc(0xf98)
house of orange 成功了,接着就是UAF漏洞利用了,申请0x68,泄露地址+写入fake_chunk,改写malloc_hook即可。
1 | #coding=utf8 |
所以以后可以想想,一个堆块泄露地址,没人帮你阻隔时,house of orange!
二、第二种类型的原理
来看pwnable.tw的一道题:
典型的没有free的情况,这里我们需要通过house of orange实现getshell操作,分析下漏洞点:
这里根据0截断的知识,我们可以知道,通过下一个chunk的size紧密相连,我们能更新当前堆块的size,再次edit时就是溢出了,这里我们第一个块是接近topchunk的,所以可以实现把topchunk给free出来。
再看看malloc
会发现检查条件是2者需要同时成立时,即i<=8&&chunk[i]==0才能申请出堆块,但是通过看其他函数,发现只能操作8个堆块,也就是i的极限是7,而不是8,意味着可能可以申请出第9个堆块,看下布局:
由于是一一对应的,当我们把chunk[0]的size利用edit更新为0时,那么我们就可以绕过那个条件,申请出第九个堆块,放到chunk[0]的size位置,这样就是相当于超级溢出了,直接覆盖unsorted bin实现house of orange操作。
伪造整个结构体!下面介绍这一种方法,由上面的方法,我们可以通过改topchunk实现free操作把topchunk给放到unsortedbin中,然后再次申请堆块就可以得到真实地址,这里只要再次申请的堆块大于0x20,就可以同时得到堆地址,比如我再次申请是0x68大小:
所以一步操作即可泄露真实地址,也可以泄漏堆地址。
接着我们通过刚刚的超级溢出,实现覆盖我们的house of orange代码:
这里讲下原理:
File Stream Oriented Programming
我们知道有rop
即retn Oriented Programming
,那么其实File Stream Oriented Programming
是一个道理的。也是一种劫持程序流程的方法,只不过方式是通过攻击File Stream
来实现罢了。
我们先要了解malloc
对错误信息的处理过程,malloc_printerr
是malloc
中用来打印错误的函数。
malloc_printerr
其实是调用__libc_message
函数之后调用abort
函数,abort
函数其中调用了_IO_flush_all_lockp
,这个函数调用涉及到io_stderr结构体,也就是说一定会用到io_stderr里面的vtable跳转到想要执行的函数,即采用的是虚表调用的方式。
通过unsorted bin的attack,我们的io_file_all会被修改成指向top_chunk的main arena。
但是我们是无法控制main_arena
的内容的,至少全部控制是不行的,那么怎么处理呢?
这里还是要牵扯到io_file
的使用,IO_FILE
结构中有一个字段是chian
字段,它位于0x60偏移处,他指向的是下一个IO_FILE
结构体,我们如果可以控制这个字段,就再次指定io_file
的位置,它相当于是一个链表的结构
这样的话又联系到smallchunk
的问题,在拆卸unsort_bin时候对属于small_bin
的chunk进行了记录操作。
这个时候IO_FILE_all
指向的正是main_arena
的bins里面unsortbin
的位置,那么偏移0x60
处正好是,smallchunk
的index为6的地方,也就是满足大小为16*6
(0x61)的chunk,所以我们需要把unsortbin的大小设置为0x60
大小,这样就会放到我们的small bin中。
1 | while (fp != NULL) |
因为第一个分配在main_arena
的IO_FILE_plus
结构的fp->mode
等值不符合要求,就会通过chains
跳转到就下一个IO_FILE_plus
就是我们之前设置的unsortbin
,然后需要满足一下条件
- fp->mode>0
- _IO_vtable_offset (fp) ==0
- fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base
这里的话我就是把wide_data的IO_wirte_ptr
就指向read_end
就可以,然后就会调用虚表vtable+0x18偏移处的函数了。
一个实际跟踪的例子:
1 | f 0 7f2af059f390 system |
这里其实有点不太懂system是怎么被调用到的,以后再看看吧~同时,这里house of orange其实是有概率会失败的,因为不能保证那个约束条件一定是成立的~
1 | # -*- coding: utf-8 -*- |
放个re脚本专区:
1 | # -*- coding: utf-8 -*- |
放个pwn脚本专区:
1 | # -*- coding: utf-8 -*- |