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
对于起始位置的偏移是0xc0
fp->_IO_write_base = (char*)2;
,其中_IO_write_base
偏移为0x20
fp->_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
即可!
参考链接: