House Of Orange + Unsorted Bin Attack
House Of Orange + Unsorted Bin Attack
什么是 House Of Orange?
Make something out of nothing
首先 假设我们的堆布局如下:
1 | graph TB |
而且 我们知道 如果此时的Chunk存在堆溢出状况 我们就可以覆盖Top chunk 的 Prev-Size 和 Size 位
知道了这些,我们就可以堆Top-chunk的源码进行分析了
Sources
有关Top-chunk的分割 在glibc源码中有说明(glibc 2.29) 使用Top-chunk的前提是bin中没用合适使用的bin的情况下
1 | ....... |
需要注意的是,在glibc2.29或更早的use_top中新添加了对于另一个size的判断,我们暂时忽略
可以看到在分割 Top_chunk 时,会首先对于Top-chunk的大小,也就是size
和用户需要的的大小也就是nb
进行判断,如果用户申请的空间小于等于Top-chunk size 会进行接下来的操作:
- 重新计算Top-chunk的大小 也就是
remainder_size = size - nb
- 获取一个
Topchunk + nb
的的chunk 其中nb
为的用户申请的大小, 赋值为Remainder
- 把Top-chunk赋值为
Remainder
- 申请用户内存
但是 如果 nb (用户申请大小) 不满足要求 那么会使用 sysmalloc
申请内存
Sysmalloc()
在这里 我们遇到第一个条件判断
1 | if (av == NULL |
av *(arena)* == NULL
,arena为空,或者- 申请内存大小大于
mmap_threshold
时
在这两种情况下,会调用mmap
申请内存 如果 不满足条件 则会转到else分支:
else 分支首先会对 Top-chunk进行检查:
1 | assert ((old_top == initial_top (av) && old_size == 0) || |
在这里 Top-chunk必须满足以下的条件:
- Topchunk 的
prev-inuse
必须为 1 Topchunk size >= MINSIZE
old_end
为页对齐 old_end = Topchunk address + Top-chunk-size
如果这些条件都满足,那么会尝试扩展Topchunk (不放源码了,无聊) 一般来说分两种情况:arena 不是 主线程 和 arena 是 主线程
其中的具体操作不说明了 反正两种分支都会free掉源Topchunk
那么好玩的就来了:如果我们可以通过修改Topchunk,绕过前面的检查,我们就可以修改size,导致最后被free的是一个unsorted bin
1 | /* If possible, release the rest. */ |
how to exploit?
需要绕过检查的条件,稍微总结一下:
nb > Topchunk->size
Topchunk -> prev-inuse
为 1Topchunk size >= MINSIZE
old_end
为页对齐
也就是说我们只要在溢出时满足了这些条件 那么我们申请出来的必定是Unsorted Bin
至此,我们已经成功Make something out of nothing了 (在CTF-WIKI中,对于H-O-O的介绍只有这些。以下的内容是比较偏IO_FILE
攻击的,可以选看)
Make Something into Everything
我们先留个悬念,不讲什么,先从一个叫做malloc_printerr
函数说起
1 |
|
malloc_printerr
是一个错误提示函数,主要会在malloc
发生错误时被调用
可以看到,在我们熟悉的unlink中也有他的身影
1 | static void |
其中 它调用了一个__libc_message
函数 其中在这个函数的80-90行,调用了abort
函数
1 | if ((action & do_abort)) |
其中,它还调用了fflush()
函数 而这个函数,就很有趣了。
首先,我们如果阅读源码,是可以发现fflush()
本质上就是一个调用链
1 | graph LR |
这个时候,我们去最后调用的_IO_flush_all_lockp
函数看看
1 |
|
这个函数,简单来说就是以list_all_lock
为链表头进行遍历,而且对一些条件进行判断。最后会调用一个_IO_OVERFLOW
函数,而这个函数其实就是jump到了__overflow
函数 其中 这个函数调用一些和IO_FILE有关的函数 我们看看IO_file链式结构
IO_FILE Structure
1 | struct _IO_FILE { |
可以看到 在这个结构中,出现了一个*_chain
成员, 这个成员的作用是连接下一个IO_FILE
。
同时,我们在_IO_flush_all_lockp
函数中,可以看到我们作为遍历头指针的_IO_list_all
,他的作用不想而之,就像如下
1 | struct _IO_FILE_plus *_IO_list_all = &_IO_2_1_stderr_; |
会形成一个这样的链表:
1 | graph LR |
现在,我知道我们可以通过Make Something out of nothing
部分的方法,通过Top chunk
的方法“分割“出来一个Unsorted Bin
那么,也就说明我们可以通过尝试Unsorted Bin attack
的方法, 去修改_IO_list_all
指针。Unsorted Bin attack
的结果也不用多说,可以修改任意地址变成一个大数字,也就是unsorted bin
的地址。
算了。想一想还是讲一下Unsorted Bin Attack
把
Unsorted Bin Attack
这一切都要从一段源码说起;
1 | * remove from unsorted list */ |
在
_int_malloc
有这么一段代码,当将一个 unsorted bin 取出的时候,会将bck->fd
的位置写入本 Unsorted Bin 的位置。
那么也就是说,如果我们可以控制到当前堆块的bk
,那么我们就可以把地址为bk
(不准确)的地址修改成unsorted_chunks (av);
这里,我希望大家可以在How2Heap看一看Unsorted Bin attack
的例子,也推荐大家动态调试一下。
接下来,我们回到我们之前的思路,可以知道,我们可以通过Unsorted Bin Attack
把 _IO_list_all
指针修改成Main Arena + 88 (unsorted_chunks (av);)
的地址,那么,如果我们把Main Arena + 88
当作一个IO_FILE
结构体,那么根据星梦大佬的说法来说,IO_File
结构体中的*_chain
指针就等于Main Arena + 88 + 0x68
。也就等于Main Arena + 0xc0
,而这个地址就正好就是0x60大小的Small chunk
的地址。