堆溢出-Housese_Of_XXX
House Of XXX 是 2004 年《The Malloc Maleficarum-Glibc Malloc Exploitation Techniques》中提出的一系列针对 glibc 堆分配器的利用方法。 由于年代久远《The Malloc Maleficarum》中提出的大多数方法今天都不能奏效,我们现在所指的 House Of XXX 利用相比 2004 年文章中写的已有较大的不同。
但是《The Malloc Maleficarum》依然是一篇推荐阅读的文章,可以在这里读到它的原文: https://dl.packetstormsecurity.net/papers/attack/MallocMaleficarum.txt
House Of Einherjar
house of einherjar 是一种堆利用技术,由 Hiroki Matsukuma
提出。该堆利用技术可以强制使得 malloc
返回一个几乎任意地址的 chunk 。其主要在于滥用 free
中的后向合并操作(合并低地址的 chunk),从而使得尽可能避免碎片化。
此外,需要注意的是,在一些特殊大小的堆块中,off by one 不仅可以修改下一个堆块的 prev_size,还可以修改下一个堆块的 PREV_INUSE 比特位。
漏洞原理
后向合并操作
free
函数中的后向合并核心操作如下:
/* consolidate backward */ |
具体过程如下:
利用原理
首先,在之前的堆的介绍中,我们可以知道以下的知识:
- 两个物理相邻的 chunk 会共享
prev_size
字段,尤其是当低地址的 chunk 处于使用状态时,高地址的 chunk 的该字段便可以被低地址的 chunk 使用。因此,我们有希望可以通过写低地址 chunk 覆盖高地址 chunk 的prev_size
字段。 - 一个 chunk PREV_INUSE 位标记了其物理相邻的低地址 chunk 的使用状态,而且该位是和 prev_size 物理相邻的。
- 后向合并时,新的 chunk 的位置取决于
chunk_at_offset(p, -((long) prevsize))
。
那么如果我们可以同时控制一个 chunk prev_size 与 PREV_INUSE 字段,那么我们就可以将新的 chunk 指向几乎任何位置。
利用过程
溢出前:假设溢出前的状态如下图所示:
溢出:这里我们假设p0
堆块一方面可以写prev_size
字段,另一方面,存在off by one
的漏洞,可以写下一个 chunk 的PREV_INUSE
部分,那么:
溢出后:假设我们将p1
的 prev_size
字段设置为我们想要的目的 chunk 位置与 p1
的差值。在溢出后,我们释放 p1
,则我们所得到的新的 chunk 的位置 chunk_at_offset(p1, -((long) prevsize))
就是我们想要的 chunk 位置了。
攻击过程示例
可以进行 House Of Einherjar 攻击的代码:
|
攻击代码如下:
from pwn import * |
利用 unlink 漏洞的时候:
p->fd = &p-3*4 |
在这里利用时,因为没有办法找到 &p
, 所以直接让:
p->fd = p |
这里需要注意一个点:
payload = p64(0) + p64(0x101) + p64(address) * 2 + "A"*0xe0 |
其实修改为下面这样也是可以的:
payload = p64(0) + p64(0x221) + p64(address) * 2 + "A"*0xe0 |
按照道理来讲 fake chunk 的 size 是 0x221
才合理,但是为什么 0x101
也可以呢?这是因为对 size 和 prev_size 的验证只发生在 unlink 里面,而 unlink 里面是这样验证的:
if (__builtin_expect (chunksize(P) != prev_size (next_chunk(P)), 0)) \ |
所以只需要再伪造 fake chunk 的 next chunk 的 prev_size 字段就好了。
总结
这里我们总结下这个利用技术需要注意的地方:
- 需要有溢出漏洞可以写物理相邻的高地址的 prev_size 与 PREV_INUSE 部分
- 我们需要计算目的 chunk 与 p1 地址之间的差,所以需要泄漏地址
- 我们需要在目的 chunk 附近构造相应的 fake chunk,从而绕过 unlink 的检测
其实,该技术与 chunk extend/shrink 技术比较类似。
House of Lore
House of Lore 攻击与 Glibc 堆管理中的 Small Bin 的机制紧密相关。
House of Lore 可以实现分配任意指定位置的 chunk,从而修改任意地址的内存。
House of Lore 利用的前提是需要控制 Small Bin Chunk 的 bk 指针,并且控制指定位置 chunk 的 fd 指针。
原理
如果在 malloc 的时候,申请的内存块在 small bin 范围内,那么执行的流程如下:
/* |
从中可以看出,如果我们可以修改 small bin 的最后一个 chunk 的 bk 为我们指定内存地址的 fake chunk,并且同时满足之后的 bck->fd != victim 的检测,那么我们就可以使得 small bin 的 bk 恰好为我们构造的 fake chunk。也就是说,当下一次申请 small bin 的时候,我们就会分配到指定位置的 fake chunk。
攻击过程代码
示例代码如下:
|
需要注意的是:
1、
void *p5 = malloc(1000);
是为了防止和 victim_chunk 之后和 top_chunk 合并。2、
free((void*)victim)
,victim 会被放入到 unsort bin 中去,然后下一次分配的大小如果比它大,那么将从 top chunk 上分配相应大小,而该 chunk 会被取下 link 到相应的 bin 中。如果比它小 (相等则直接返回),则从该 chunk 上切除相应大小,并返回相应 chunk,剩下的成为 last reminder chunk , 还是存在 unsorted bin 中。
House of Orange
House of Orange 与其他的 House of XX 利用方法不同,这种利用方法来自于 Hitcon CTF 2016 中的一道同名题目。由于这种利用方法在此前的 CTF 题目中没有出现过,因此之后出现的一系列衍生题目的利用方法我们称之为 House of Orange。
概述
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。
详细过程:假设目前的 top chunk 已经不满足 malloc 的分配需求。 首先我们在程序中的malloc
调用会执行到 libc.so 的_int_malloc
函数中,在_int_malloc
函数中,会依次检验 fastbin、small bins、unsorted bin、large bins 是否可以满足分配要求,因为尺寸问题这些都不符合。接下来_int_malloc
函数会试图使用 top chunk,在这里 top chunk 也不能满足分配的要求,因此会执行如下分支:
/* |
此时 ptmalloc 已经不能满足用户申请堆内存的操作,需要执行 sysmalloc 来向系统申请更多的空间。 但是对于堆来说有 mmap 和 brk 两种分配方式,我们需要让堆以 brk 的形式拓展,之后原有的 top chunk 会被置于 unsorted bin 中。
综上,我们要实现 brk 拓展 top chunk,但是要实现这个目的需要绕过一些 libc 中的 check。 首先,malloc 的尺寸不能大于mmp_.mmap_threshold
if ((unsigned long)(nb) >= (unsigned long)(mp_.mmap_threshold) && (mp_.n_mmaps < mp_.n_mmaps_max)) |
如果所需分配的 chunk 大小大于 mmap 分配阈值,默认为 128K,并且当前进程使用 mmap() 分配的内存块小于设定的最大值,将使用 mmap() 系统调用直接向操作系统申请内存。
在 sysmalloc 函数中存在对 top chunk size 的 check,如下:
assert((old_top == initial_top(av) && old_size == 0) || |
这里检查了 top chunk 的合法性,如果第一次调用本函数,top chunk 可能没有初始化,所以可能 old_size 为 0。 如果 top chunk 已经初始化了,那么 top chunk 的大小必须大于等于 MINSIZE,因为 top chunk 中包含了 fencepost,所以 top chunk 的大小必须要大于 MINSIZE。其次 top chunk 必须标识前一个 chunk 处于 inuse 状态,并且 top chunk 的结束地址必定是页对齐的。此外 top chunk 除去 fencepost 的大小必定要小于所需 chunk 的大小,否则在_int_malloc() 函数中会使用 top chunk 分割出 chunk。
我们总结一下伪造的 top chunk size 的要求:
伪造的 size 必须要对齐到内存页
size 要大于 MINSIZE(0x10)
size 要小于之后申请的 chunk size + MINSIZE(0x10)
size 的 prev inuse 位必须为 1
之后原有的 top chunk 就会执行_int_free
从而顺利进入 unsorted bin 中。
示例
这里给出了一个示例程序,程序模拟了一个溢出覆盖到 top chunk 的 size 域。我们试图把 size 改小从而实现 brk 扩展,并把原有的 top chunk 放入 unsorted bin 中。
|
这里我们把 top chunk 的 size 覆盖为 0x41。之后申请大于这个尺寸的堆块,即 0x60。 但是当我们执行这个示例时会发现,这个程序并不能利用成功,原因在于 assert 并没有被满足从而抛出了异常。
[#0] 0x7ffff7a42428 → Name: __GI_raise(sig=0x6) |
我们回头来看一下 assert 的条件,可以发现之前列出的条目都被满足了除了第一条:
1.伪造的size必须要对齐到内存页 |
什么是对齐到内存页呢?我们知道现代操作系统都是以内存页为单位进行内存管理的,一般内存页的大小是 4kb。那么我们伪造的 size 就必须要对齐到这个尺寸。在覆盖之前 top chunk 的 size 大小是 20fe1,通过计算得知 0x602020+0x20fe0=0x623000 是对于 0x1000(4kb)对齐的。
0x602000: 0x0000000000000000 0x0000000000000021 |
因此我们伪造的 fake_size 可以是 0x0fe1、0x1fe1、0x2fe1、0x3fe1 等对 4kb 对齐的 size。而 0x40 不满足对齐,因此不能实现利用。
|
进行分配之后我们可以观察到原来的堆经过了 brk 扩展:
//原有的堆 |
我们的申请被分配到 0x623010 的位置,同时原有的堆被置入 unsorted bin:
[+] unsorted_bins[0]: fw=0x602020, bk=0x602020 |
因为 unsorted bin 中存在块,所以我们下次的分配会切割这个块:
malloc(0x60); |
可以看到分配的内存是从 unsorted bin 中切割的,内存布局如下:
0x602030: 0x00007ffff7dd2208 0x00007ffff7dd2208 <== 未被清零的unsorted bin链表 |
其实 house of orange 的要点正在于此,之后的利用因为涉及到_IO_FILE 的知识,放到 IO_FILE 部分进行学习记录。
House of Rabbit
House of rabbit 是一种伪造堆块的技术,一般运用在 fastbin attack 中,因为 unsorted bin 等其它的 bin 有更好的利用手段。
原理
我们知道,fastbin 中会把相同的 size 的被释放的堆块用一个单向链表管理,分配的时候会检查 size 是否合理,如果不合理程序就会异常退出。而 house of rabbit 就利用了在 malloc consolidate 的时候 fastbin 中的堆块进行合并时 size 没有进行检查从而伪造一个假的堆块,为进一步的利用做准备。
原作者POC
前提条件:
- 1、可以修改 fastbin 的 fd 指针或 size
- 2、可以触发 malloc consolidate(merge top 或 malloc big chunk 等等)
下面来看一下 POC:
POC 1: modify the size of fastbin chunk
unsigned long* chunk1=malloc(0x40); //0x602000 |
POC 2: modify FD pointer
unsigned long* chunk1=malloc(0x40); //0x602000 |
原理很简单,就是通过修改 fastbin chunk 的 size(如上面的 POC 1 所示) 直接构造 overlap chunk,或者修改 fd(如面的 POC 2 所示),让它指向一个 fake chunk,触发 malloc consolidate 之后让这个 fake chunk 成为一个合法的 chunk。
总结
House of rabbit 的优点是容易构造 overlap chunk,由于可以基于 fastbin attack,甚至不需要 leak 就可以完成攻击。大家可以通过例题的练习加深对这个攻击的理解。
例题
- HITB-GSEC-XCTF 2018 mutepig
House of Roman
House of Roman 这个技巧说简单点其实就是 fastbin attack 和 Unsortbin attack 结合的一个小 trick。
概括
该技术用于 bypass ALSR,利用 12-bit 的爆破来达到获取 shell 的目的。且仅仅只需要一个 UAF 漏洞以及能创建任意大小的 chunk 的情况下就能完成利用。
- Post Title: 堆溢出-Housese_Of_XXX
- Post Author: ggb0n
- Post Link: http://ggb0n.cool/2020/06/07/堆溢出-Housese_Of_XXX/
- Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.
1.TCTF2020部分题解
2.第五空间pwn题练习
3.堆溢出-Tcache_Attack
4.堆溢出-Housese_Of_XXX
5.堆溢出基础
6.入坑二进制