House_of_Orange
House of Orange
方法适用于glibc2.23
house of orange主要分为两个部分,首先是创造出unsorted bin,然后利用unsorted bin attack修改_IO_list_all指向可控的heap,最后伪造数据FSOP getshell
以下以how2heap中的house of orange为例
创造unsorted bin
malloc(0x400-16)
假设存在溢出,修改top chunk的size
需要满足两个条件:Top chunk + size页对齐和pre_inuse位为1
示例中top chunk大小为0x20c01,修改为0xc01
接下来malloc(0x1000)
由于此时top chunk的大小仅为0xc01
会调用sysmalooc,效果就是旧的top chunk被放入unsorted bin中,heap也进行了brk拓展,且紧跟在原本的top chunk之后
1 | 0x602000 0x623000 rw-p 21000 0 |
拓展后如下1
0x602000 0x645000 rw-p 43000 0
unsorted bin attack
修改unsorted bin的bk指向_IO_list_all-0x10
并修改old top chunk的size为0x61(small bin,为了与之后对_IO_FILE利用中的偏移对应)
1 | Free chunk (unsortedbin) | PREV_INUSE |
1 | pwndbg> x/10gx 0x7ffff7dd2510 |
这样一来,最后malloc时会遍历到unsorted bin,发现该chunk不符合大小,于是将该chunk从unsorted bin链中取出,此时执行1
2
3/* remove from unsorted list */
unsorted_chunks (av)->bk = bck;
bck->fd = unsorted_chunks (av);
进行解链操作,自然就会在bck的fd处填上unsorted_chunks (av),所以这一步后_IO_list_all会写上0x7ffff7dd1b78(main_arena+88)1
2
3pwndbg> x/20gx 0x7ffff7dd2510
0x7ffff7dd2510: 0x0000000000000000 0x0000000000000000
0x7ffff7dd2520 <_IO_list_all>: 0x00007ffff7dd1b78 0x0000000000000000
并且old top chunk从unsorted bin中取出,因为size被改为0x61,介于90~98,被放入small bin[4]
另外在old top chunk数据区开始处写上”/bin/sh”,方便接下来_IO_FILE的利用
FSOP
伪造_IO_FILE_plus
现在_IO_list_all已经指向了0x00007ffff7dd1b78
作为结构体查看此处内存
这里与上文中修改old top chunk的size为0x61对应,由于old top chunk被放入smallbin[4]中,从而对应_IO_FILE_plus结构体中_chain的偏移,知道_chain链接着FILE,可以看到_chain指向0x602400,正是old top chunk。所以程序会把old top chunk当作下一个_IO_FILE_plus结构体,在这里布置数据即可。
1 | FILE *fp = (FILE *) top; |
修改_mode为0,_IO_write_base为2,_IO_write_ptr为3绕过检查
并在对应vtable处(偏移0xd8)写入可控内存的地址(这里为&top[12])
再在vtable中偏移0x18处的_overflow处写入winner函数地址
布置好后如下图所示


触发_IO_overflow
malloc(10)
首先遍历到unsorted bin,执行上文所述的unsorted bin解链造成unsorted bin attack,并将原本位于unsorted bin中的old top chunk放入对应大小的small bin中(smallbin[4])
然后malloc还会检查size域,检测到size小于MINSIZEsize <= 2 * SIZE_SZ,触发终止调用链执行最终getshell
1 | pwndbg> backtrace |
malloc_printerr函数调用链如下1
_int_malloc->malloc_printerr->__libc_message->__GI_abort->_IO_flush_all_lockp->_IO_OVERFLOW
最终跳转进入winnner函数,调用system(“/bin/sh”)getshell
总结
house of orange整体由三部分组成
- 没有free的情况下通过修改top chunk的size位(需要注意页对齐和pre_inuse位为1),利用sysmalloc创造出unsorted bin
- 利用unsorted bin attack修改_IO_list_all指向main_arena+88,并修改old top chunk的size为0x61(97),从而放入smallbin[4](90~98)。则造成在main_arena+88+0x68处,对应_IO_FILE_plus中的_chain指向old top chunk
- FSOP,布置old top chunk(fake _IO_FILE_plus)中的数据和指针,布置fake _IO_FILE中的_mode,_IO_write_base,_IO_write_ptr。写入fake vtable指向可控的内存区域,在对应偏移0x18写入fake _overflow指向winner函数最终执行了system(“/bin/sh”)。
如果在fake _overflow处写入system函数地址,则最终会执行_IO_flush_all_lockp在调用_overflow(fp, EOF)时,fp指向”/bin/sh”,也能getshell
例题
houseoforange_hitcon_2016
这道题也是这个方法的来源
buu上能够复现
题目
build
结构
每个house大小固定,存两个指针,一个指向name,一个指向color和price
name最大可以申请0x1000
color和price没什么意义
每次build后,只会保留最新的house的指针,且计数加1,最多build三次
see
打印出信息
用来泄露地址
upgrade

更新name的信息时输多少就能写多少,存在堆溢出
只能更新最新的house内容
分析
程序没有free,需要利用house of orange中的方法创造出一个unsorted bin(修改topchunk的size,malloc(0x1000))
然后需要申请一个large bin大小(>512字节)的chunk作为name
该large bin会用fd_nextsize和bk_nextsize会存放自身的地址,fd和bk会存放libc地址,从而分别泄露出libc地址和堆地址

接下来构造payload,伪造
_IO_FILE_plus:unsorted bin头部写入/bin/sh,size位修改为0x61,覆盖unsorted bin的bk指针指向_IO_list_all-0x10,伪造数据通过检查_mode=0;_IO_write_base=2;_IO_write_ptr=3,同时写入fake vtable的地址,并相应的在_IO_overflow处写上system函数地址。最后再申请一个house,触发调用链,最终getshell
exp
1 | from pwn import* |
远程的时候发现vtable向下布置才能成功,猜测可能是vtable中其他函数会影响到结果,让无关函数都为0比较稳。








