house of orange:一种不需要free的堆利用方式
IO_FILE知识
[toc]
House of orange(glibc 2.23)🍊
前言
一句话描述该漏洞:将top chunk置入unsortedbin并打unsortedbin attack对_IO_list_all写入main_arena+88,将该chunk置入smallbin,使其_IO_list_all的_chain刚好指向该chunk,便可劫持vtable来触发FSOP。
先看一下总体流程:
- 通过漏洞修改
top chunk的size,使得系统以brk的方式来扩展空间,此时top chunk会被置入到unsorted bin中去 - 利用
unsortedbin attack,将_IO_list_all指针的值改写为unsortedbin的头chunk地址,即main_arena+88 - 通过再次修改该
unsorted chunk的size为0x61,使其被置入smallbin[4]中,如此以来main_arena+88+0x68(也就是_IO_list_all的_chain)将指向这个chunk - 再次
malloc的时候由于unsortedbin中指针被修改,因此会触发调用链malloc() -> malloc_printerr() -> __libc_message() -> abort() -> fflush() -> _IO_flush_all_lockp() -> _IO_new_file_overflow()。而最终调用的函数实际上是调用了_IO_FILE_plus.vtable中的_IO_OVERFLOW函数,函数原型为_IO_OVERFLOW(fp, EOF)。通过上面的chunk覆盖函数指针,将_IO_FILE结构体0字节偏移处的fp写为/bin/sh,即可获得shell。
以上就是House of orange的简要流程,整个流程在我第一次见的时候是相当震撼的,因此只能慢慢嚼碎再咽下去。
0x00: 将top chunk置入unsortedbin
需要利用的漏洞:控制top chunk的size
当程序调用malloc时,系统会以此检查fastbin、small bins、unsorted bin、large bins是否满足分配要求。若都不满足,那么_init_malloc函数会试图使用top chunk。若top_chunk也不满足分配要求,那么ptmalloc将会执行sysmalloc来申请更多的空间。此时有两种分配方式,一种是brk,而另一种是mmap。我们需要让sysmalloc以brk的方式扩展内存,因为brk方式扩展内存后,会将原有的top chunk置入到unsorted bin中。到这里,我们已经明白如何让top chunk被置入到unsortedbin:一是top chunk不满足分配要求,二是让系统以brk方式扩展内存。要满足这些条件,我们要使得:
- 伪造的
top chunk的size对齐到内存页(自己计算,要使得其top chunk addr+size对齐0x1000) size大于MINSIZE(0X10)top chunk size小于之后申请的chunk size + MINSIZE(使其使用brk扩展)top chunk size的prev_inuse位为1- 申请的内存小于
mmap阈值,即0x20000
满足上述条件后我们即可以在malloc一个chunk的时候将top chunk置入到unsorted bin中,这段代码如下:
1 | |
运行这段代码后,效果如下,其中第一个chunk是申请的大小为0x400的chunk,第二个是已经位于unsorted bin中的以前的top chunk,另外两个chunk是在这个过程中产生的chunk,暂时不清楚原因。

bins中如下:

0X01: 使用Unsortedbin attack改写_IO_list_all指针
需要利用的漏洞:unsortedbin attack,需要你能够控制刚刚的top chunk的bk指针
由于最开始的top chunk已经位于unsortedbin中,我们可由其fd和bk泄露出libc,也就可以获得libc具有固定偏移的io_list_all的地址。我们将top chunk的bk写为_IO_list_all-0x10,在下一次malloc时便将会触发unsortedbin attack,往_IO_list_all中写入main_arena+88。我们知道_IO_list_all本来是指向_IO_2_1_stderr_的,即指向一个_IO_FILE_plus结构,那么main_arena+88地址开始处也会被当做一个_IO_FILE_plus结构体。然而,此处空间我们并不是可控的,该怎么办呢?先不管,我们先看这一段的代码:
1 | |
0x02: 将top chunk置入smallbin使得_chain指向该chunk
需要利用的漏洞:仍然是控制top chunk即可
根据上面的问题,很显然,我们需要能够控制_IO_FILE_plus结构体的空间才可以继续。目前我们已经使得_IO_list_all指向了main_arena+88,那么main_arena+88处的地址空间将会被当做一个IO_FILE_plus结构体,而该结构体里面含有一个_chain域即_IO_FILE_plus.file._chain,它指向下一个IO_FILE_plus结构。若我们能够控制该_chain指向一个我们可以控制的chunk,这样一来当前的IO_FILE_plus指向的下一个IO_FILE_plus就完全受我们控制了。此时问题就回到,如何控制该_chain?_chain在_IO_FILE_plus结构体中的偏移为0x68,即main_arena+88+0x68,即main_arena+192。实际上,main_arena附近的内存空间相对复杂,笔者通过mallopt(M_MXFAST,0)的方式禁用fastbin,得到如下结果:

从图上可以看到,main_arena+192指向的区域是大小为0x60的smallbin中的最后一个chunk,也就是smallbin(0x60)->bk。那么,接下来我们就将top chunk挂入大小为0x60的smallbin,挂入后_IO_FILE_plus的_chain就将指向这个top chunk,也就是top chunk会成为下一个_IO_FILE_plus。此外,最终我们执行的代码是_IO_OVERFLOW(fp, EOF),而其中的fp实际上也就是top chunk的地址,因此若我们在top chunk的地址处写下/bin/sh\x00,那么相当于执行了_IO_OVERFLOW('/bin/sh\x00'),覆盖函数指针后就是system('/bin/sh\x00')。
这部分的代码如下:
1 | |
0x03: 满足利用条件,触发FSOP调用链获得shell
需要利用的漏洞:控制top chunk即可
有的读者可能注意到我们将top chunk置入unsortedbin后,一直没有调用malloc来触发unsortedbin attack,也没有将top chunk置入small bin中。实际上在最后调用malloc时,这个申请内存的操作会先后触发unsortedbin attack,然后将其置入small bin;并且由于unsortedbin attack时已经破坏了其链表结构,因此会触发malloc() -> malloc_printerr() -> __libc_message() -> abort() -> fflush() -> _IO_flush_all_lockp() -> _IO_new_file_overflow()函数的调用链。因此,在malloc之前,我们需要检查剩下的安全机制,来保证我们的攻击可以成功。在_IO_flush_all_lockp()函数中,要满足要求fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base。因此,可以设置如下条件:
fp->_mode=0,其中_mode对于起始位置的偏移是0xc0fp->_IO_write_base = (char*)2;,其中_IO_write_base偏移为0x20fp->_IO_wirte_ptr = (char*)3;,其中_IO_write_ptr偏移为0x28
满足以上条件后,我们便可以覆盖掉_IO_new_file_overflow函数的函数指针了。vtable中的函数如下:
1 | |
这里我们需要覆盖overflow函数,也就是vtable[3]。而另外vtable相对于_IO_FILE_plus起始地址处的偏移是0xd8。top chunk的IO_FILE_plus结构体中的vtable是可以任意构造值的,我们将其设置到top chunk处的任意地方即可,只需要注意fake_vtable->overflow处不要被占用。这部分代码如下:
1 | |
其中winner函数就是咱们的后门函数。
1 | |
2024年补充
有时候还是不能太死板。例如,今天做到一道house of orange,能够delete一次,但是申请chunk大小在0xf0-0x3f0之间,这就导致最后难以通过malloc一个小chunk来触发漏洞。
最终想了一会,只需要将chunk大小改为0x61的时候,再通过其打一个unsortedbin attack即可!
参考链接: