Glibc堆利用之house of 系列
Last Update:
Word Count:
Read Time:
Page View: loading...
转载自Glibc堆利用之house of系列总结 - roderick - record and learn! (roderickchan.cn)
1 - 前言
Glibc 的 house of 系列攻击手法基于都是围绕着堆利用和 IO FILE 利用。还有很多堆利用手法也非常经典,但是由于其没有被冠以 house of xxxx,故没有收录到本文中。如果想学习所有的详细的堆攻击手法,强烈建议 follow 仓库 how2heap进行学习。我相信,只要把 how2heap 里面的每一个堆利用手法都学懂学透了,glibc 堆利用你将尽在掌握。
在开始系列总结之前,我会给出一个表格,表格里面分别是 house of xxxx 和对应的优秀的解析文章,在此非常感谢各位师傅们的总结。如果你在阅读本文的过程中想完整地查看某一个手法地详细利用过程,那么可以直接回到表格,点击对应的链接进行学习。目前的最新版本为 2.37,但是,目前的 ubuntu:23.04 还没开始用 glibc-2.37,使用的仍然是 glibc-2.36。
如果还有哪些 house of xxxx 的利用手法没有收录进来,或你对本文存有一些疑问,或者你发现本文某些内容编写错误,还请留言指正。
需要注意的是,除了关注各种 house of 利用技巧本身,更重要的是,需要关注该利用技巧背后的思想和原理。如果你能从这一系列的利用手法中提炼出一些通用的攻击向量或者攻击思想,日后在面对其他的场景,你也能更快的找到系统的漏洞点并加以利用。学习 glibc 堆利用更多的是为了举一反三,为了更好地掌握漏洞挖掘模式、漏洞分析方法,而不仅仅是为了比赛。
house of 系列的表格如下,适用版本不考虑低于 glibc-2.23 的版本。我将在下文中进一步阐述每一个利用手法的原理、使用场景与适用范围。
此外,阅读下文之前需要了解:
- 下面所述的
chunk A,地址A指的是chunk header地址,而不是user data地址。 - 漏洞成因基本上都是堆溢出、
UAF等
2-house of 系列
2.1-house of spirit
漏洞成因
堆溢出写
适用范围
2.23—— 至今
利用原理
利用堆溢出,修改 chunk size,伪造出 fake chunk,然后通过堆的释放和排布,控制 fake chunk。house of spirit 的操作思路有很多,比如可以按如下操作进行利用:
- 申请
chunk A、chunk B、chunk C、chunk D - 对
A写操作的时候溢出,修改B的size域,使其能包括chunk C - 释放
B,然后把B申请回来,再释放C,则可以通过读写B来控制C的内容
相关技巧
起初 house of spirit 主要是针对 fastbin,后来引入了 tcachebin 后,也可以使用 tcachebin 版本的 house of spirit。利用方法与 fastbin 场景下类似,注意好不同版本下的检查条件即可。
利用效果
- 劫持
fastbin/tcachebin的fd之后,可以任意地址分配、任意地址读写
2.2-house of einherjar
漏洞成因
溢出写、off by one、off by null
适用范围
2.23—— 至今- 可分配大于处于
unsortedbin的chunk
利用原理
利用 off by null 修改掉 chunk 的 size 域的 P 位,绕过 unlink 检查,在堆的后向合并过程中构造出 chunk overlapping。
- 申请
chunk A、chunk B、chunk C、chunk D,chunk D用来做gap,chunk A、chunk C都要处于unsortedbin范围 - 释放
A,进入unsortedbin - 对
B写操作的时候存在off by null,修改了C的P位 - 释放
C的时候,堆后向合并,直接把A、B、C三块内存合并为了一个chunk,并放到了unsortedbin里面 - 读写合并后的大
chunk可以操作chunk B的内容,chunk B的头
相关技巧
虽然该利用技巧至今仍可以利用,但是需要对 unlink 绕过的条件随着版本的增加有所变化。
最开始的 unlink 的代码是:
| 1 2 3 4 5 6 7 8 9 10 | /* Take a chunk off a bin list */ #define unlink(AV, P, BK, FD) { \ FD = P->fd; \ BK = P->bk; \ if (__builtin_expect (FD->bk != P || BK->fd != P, 0)) \ malloc_printerr (check_action, "corrupted double-linked list", P, AV); \ else { \ // ..... \ } \ } |
| ———————— | ———————————————————— |
| | |
只需要绕过__builtin_expect (FD->bk != P || BK->fd != P, 0) 即可,因此,不需要伪造地址处于高位的 chunk 的 presize 域。
高版本的 unlink 的条件是:
新增了 chunksize (p) != prev_size (next_chunk (p)),对 chunksize 有了检查,伪造的时候需要绕过。
利用效果
- 构造
chunk overlap后,可以任意地址分配 - 结合其他方法进行任意地址读写
例题
例题:
2016_seccon_tinypad
2.3-house of force
漏洞成因
堆溢出写 top_chunk
适用范围
2.23——2.29- 可分配任意大小的
chunk - 需要泄露或已知地址
利用原理
对 top_chunk 的利用,过程如下:
- 申请
chunk A - 写
A的时候溢出,修改top_chunk的size为很大的数 - 分配很大的
chunk到任意已知地址
相关技巧
注意,在 glibc-2.29 后加入了检测,house of force 基本失效:

利用效果
- 任意地址分配
- 任意地址读写
2.4-house of lore
漏洞成因
堆溢出、use after free、edit after free
适用范围
2.23—— 至今- 需要泄露或已知地址
利用原理
控制 smallbin 的 bk 指针,示例如下:
- 申请
chunk A、chunk B、chunk C,其中chunk B大小位于smallbin - 释放
B,申请更大的chunk D,使得B进入smallbin - 写
A,溢出修改B的bk,指向地址X,这里有fake chunk - 布置
X->fd == &B - 分配两次后即可取出位于
X地址处的fake chunk
相关技巧
在引入了 tcache stash unlink 的时候,需要注意绕过:
要么使其满足 tc_victim = last (bin)) == bin、要么使其满足:tcache->counts[tc_idx] ≥ mp_.tcache_count。否则可能会因为非法内存访问使得程序 down 掉。
实际上,这个技巧用得不是很多,因为在同等条件下,更偏向于利用 fastbin/tcachebin。
利用效果
- 任意地址分配
- 任意地址读写
2.5-house of orange
漏洞成因
堆溢出写
适用范围
2.23——2.26- 没有
free - 可以
unsortedbin attack
利用原理
house of orange 可以说是开启了堆与 IO 组合利用的先河,是非常经典、漂亮、精彩的利用组合技。利用过程还要结合 top_chunk 的性质,利用过程如下:
stage1
- 申请
chunk A,假设此时的top_chunk的size为0xWXYZ - 写
A,溢出修改top_chunk的size为0xXYZ(需要满足页对齐的检测条件) - 申请一个大于
0xXYZ大小的chunk,此时top_chunk会进行grow,并将原来的old top_chunk释放进入unsortedbin
stage2
- 溢出写
A,修改处于unsortedbin中的old top_chunk,修改其size为0x61,其bk为&_IO_list_all-0x10,同时伪造好IO_FILE结构 - 申请非
0x60大小的chunk的时候,首先触发unsortedbin attack,将_IO_list_all修改为main_arena+88,然后unsortedbin chunk会进入到smallbin,大小为0x60;接着遍历unsortedbin的时候触发了malloc_printerr,然后调用链为:malloc_printerr -> libc_message -> abort -> _IO_flush_all_lockp,调用到伪造的vtable里面的函数指针
相关技巧
- 在
glibc-2.24后加入了vtable的check,不能任意地址伪造vatble了,但是可以利用IO_str_jumps结构进行利用。 - 在
glibc-2.26后,malloc_printerr不再刷新IO流了,所以该方法失效 - 由于
_mode的正负性是随机的,影响判断条件,大概有1/2的概率会利用失败,多试几次就好
利用效果
- 任意函数执行
- 任意命令执行
2.6-house of rabbit
漏洞成因
堆溢出写、use after free、edit after free
适用范围
2.23——2.26- 超过
0x400大小的堆分配 - 可以写
fastbin的fd或者size域
利用原理
该利用技巧的核心是 malloc_consolidate 函数,当检测到有 fastbin 的时候,会取出每一个 fastbin chunk,将其放置到 unsortedbin 中,并进行合并。以修改 fd 为例,利用过程如下:
- 申请
chunk A、chunk B,其中chunk A的大小位于fastbin范围 - 释放
chunk A,使其进入到fastbin - 利用
use after free,修改A->fd指向地址X,需要伪造好fake chunk,使其不执行unlink或者绕过unlink - 分配足够大的
chunk,或者释放0x10000以上的chunk,只要能触发malloc_consolidate即可 - 此时
fake chunk被放到了unsortedbin,或者进入到对应的smallbin/largebin - 取出
fake chunk进行读写即可
相关技巧
2.26加入了unlink对presize的检查2.27加入了fastbin的检查
抓住重点:house of rabbit 是对 malloc_consolidate 的利用。因此,不一定要按照原作者的思路来,他的思路需要满足的条件太多了。
利用效果
- 任意地址分配
- 任意地址读写
2.7-house of roman
漏洞成因
use after free、堆溢出
适用范围
2.23——2.29- 可以
use after edit - 不需要泄露地址
- 需要爆破
12 bit,成功的概率1/4096
利用原理
可以说这个技巧是 fastbin attack + unsortedbin attack 的组合技,利用思路如下:
- 申请
chunk A、chunk B、chunk C和chunk D,chunk B的大小为0x70 - 释放
chunk B,使其进入到fastbin[0x70] - 溢出写
A,修改chunk B的size,使其大小在unsortedbin范围 - 再次释放
B,B进入unsortedbin中 - 部分写
B的fd,使得fd指向malloc_hook-0x23 - 利用
A的溢出写修正B的size,连续分配两次0x70,即可分配到malloc_hook上方 - 触发
unsortedbin attack,将__malloc_hook写为main_arena+88 - 部分写
__malloc_hook的低三个字节,修改为one_gadget - 再次
malloc即可拿到shell
相关技巧
- 使用
house of roman的时候,需要采用多线程爆破 - 可以使用其他方法代替,比如先攻击
stdout泄露地址,使得爆破的成本降低
利用效果
- 执行
one_gadget - 绕过
ASLR
2.8-house of storm
漏洞成因
堆溢出、use after free、edit after free
适用范围
2.23——2.29- 可以进行
unsortedbin attack - 可以进行
largebin attack,修改bk和bk_nextsize - 可以分配
0x50大小的chunk
利用原理
house of storm 也是一款组合技,利用开启了 PIE 的 x64 程序的堆地址总是 0x55xxxx... 或者 0x56xxxx... 开头这一特性,使用一次 largebin attack 写两个堆地址,使用一次 unsortedbin attack 写一次 libc 地址,可以实现任意地址分配。虽然 house of storm 最后能达到任意地址分配,但是由于其所需的条件比较多,一般可以用其他更简便的堆利用技术代替。利用思路如下:
- 进行一次
unsortedbin attack,其bk修改为addr - 进行一次
largebin attack,其bk修改为addr+0x10,bk_nextsize修改为addr-0x20+3 - 申请
0x50大小的chunk即可申请到addr处
相关技巧
需要注意的有:
- 该方法成功的几率是
50%,因为0x55会触发assert断言,0x56才能成功 - 申请
addr处的chunk的时候需要从unsortedbin里面取
利用效果
- 任意地址分配
2.9-house of corrosion
漏洞成因
堆溢出、use after free
适用范围
2.23—— 至今- 任意大小分配
- 可以修改
global_max_fast - 不需要泄露地址
利用原理
一个非常 tricky 的方法,可以绕过 aslr,不需要泄露地址都能达成 rce,可以很很多方法结合起来应用。先说利用原理:
- 使用
unsortedbin attack/largebin attack等方法,成功修改global_max_fast的值为很大的值。如果使用unsortedbin attack,不需要泄露地址,爆破1/16即可 - 申请任意大小的
chunk,这些chunk都会被视为fastbin chunk,然后利用这些chunk来进行读和写
此时的计算公式为:
1 | |
读原语:
- 假设对应的地址
X上存储着Y,现在的目的是泄露出Y - 根据偏移计算出来
chunk size,修改chunk A的size为计算出来的值,释放chunk A到地址X处 - 此时,
A->fd就被写入了Y - 通过打印即可泄露出
Y的信息
写原语 1:
- 假设对应的地址
X上存储着Y,现在的目的是修改地址X存储的Y为其他值 - 根据偏移计算出来
chunk size,修改chunk A的size为计算出来的值,释放chunk A到地址X处 - 此时,
A->fd就被写入了Y - 修改
A->fd为目标值 - 分配一次
chunk A就可以把地址X存储的值为任意值
写原语 2:
- 假设地址
X上存储着Y、地址M上存储着N,现在的目的是把N写到地址X处 - 根据偏移计算
chunk size1,先释放chunk A到地址X处,此时有地址X处存储chunk A地址,chunk A->fd为Y - 根据偏移计算
chunk size2,再次释放chunk A到地址M处,此时有地址M处存储chunk A地址,chunk A->fd为N - 修正
chunk A的大小为chunk size1,分配1次chunk即可使得N转移到地址X处,当然在转移的过程中可以适当的修改N
显然,借助写原语 2,即可在不需要泄露地址的前提下将__malloc_hook 等写为 one_gadget,爆破的概率是 1/4096。
相关技巧
- 虽然至今都能使用
house of corrosion,但是在glibc-2.37版本中,global_max_fast的数据类型被修改为了int8_u,进而导致可控的空间范围大幅度缩小。 house of corrosion也可以拓展到tcachebin上- 适当控制
global_max_fast的大小,把握控制的空间范围 - 可以和
IO_FILE结合起来泄露信息
利用效果
glibc上的地址泄露- 执行
one_gadget
2.10-house of husk
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以修改
__printf_arginfo_table和__printf_function_table - 可触发格式化字符串解析
利用原理
严格来说,这个漏洞是与堆的关系并不是很大,主要是根据 printf 的机制进行利用。但是,该技术可以和很多堆利用手法结合起来。
调用处 1:
利用方式为:
__printf_function_table和__printf_arginfo_table分别写为chunk A和chunk B的地址- 设占位符为
α,此时chunk B的内容应该为p64(0) x ord(α-2) + p64(one_gadget)
调用处 2:
利用方式为:
__printf_function_table和__printf_arginfo_table分别写为chunk A和chunk B的地址- 设占位符为
α,此时chunk A的内容应该为p64(0) x ord(α-2) + p64(one_gadget)
该处调用在高版本被删除。
相关技巧
- 该技巧一般和
largebin attack结合起来 - 在低于
2.36版本中,__malloc_assert中有格式化字符串的解析 - 还有一个
__printf_va_arg_table也是可以利用的,但是条件比较苛刻
利用效果
- 执行
one_gadget - 执行
rop控制程序执行流
2.11-house of atum
漏洞成因
1 | |
适用范围
2.26——2.30- 可以修改
tcachebin的next和key
利用原理
这是一个关于 tcachebin 的技巧,用于修改 chunk presize/size,利用过程如下:
- 申请
chunk A,大小在fastbin范围内 - 释放
A,连续释放8次,此时,A的fd被清0,A也被放置到了fastbin里面 - 申请一个
chunk,将其fd修改为A - 0x10,此时tcache中的counts为6 - 再申请一个
chunk,从fastbin里面取,但是会把fastbin里面剩余的一个chunk链入到tcachebin - 再次分配就会分配到地址
A-0x10处,就可以修改原来A的presize/size等
相关技巧
2.30之后逻辑变了,原来是判断entry[idx]!=NULL,2.31之后判断count[idx] > 0- 有时候需要绕过
tcache->key的检测
利用效果
- 修改
chunk size以及chunk presize
2.12-house of kauri
漏洞成因
堆溢出
适用范围
2.26——2.32
利用原理
利用原理很简单,修改 tcachebin 的 size,然后使其被放到不同大小的 tcachebin 链表里面去。我感觉这个技巧是很基础的 tcachebin 技巧,甚至不应该被称之为 house of。
相关技巧
- 无
利用效果
- 多个
tcachebin链表中存放同一个chunk
2.13-house of fun
漏洞成因
堆溢出、use after free
适用范围
2.23——2.30- 可以申请
largebin范围的chunk
利用原理
或许这个技巧应该叫做 largebin attack。
在这个 sourceware.org Git - glibc.git/blobdiff - malloc/malloc.ccommit 被检测了:

相关技巧
- 无
利用效果
- 任意地址写堆地址
2.14-house of mind
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以分配任意大小的
chunk
利用原理
主要利用的是:
1 2 3 4 |
#define heap_for_ptr(ptr) \ ((heap_info *) ((unsigned long) (ptr) & ~(HEAP_MAX_SIZE - 1))) #define arena_for_chunk(ptr) \ (chunk_non_main_arena (ptr) ? heap_for_ptr (ptr)->ar_ptr : &main_arena) |
|---|---|
如果是 non-mainarean 的 chunk,会根据其地址找到 heapinfo,然后找到 malloc_state 结构体。
因此,利用技巧是:
- 根据要释放的
fastbin chunk A的堆地址,找到对应的heap_for_ptr地址 - 在
heapinfo地址处伪造好相关变量,重点是mstate指针 - 修改
chunk A的non-main标志位,释放到伪造的arena里面,控制好偏移即可
相关技巧
- 一般来说,可以分配任意大小的
chunk,还能堆溢出,很多技巧都能用 - 这个技巧是希望大家关注对于
arena的攻击 - 甚至可以直接修改
thread_arena这个变量
利用效果
- 任意地址写堆地址
2.15-house of muney
漏洞成因
堆溢出
适用范围
2.23—— 至今- 能分配
mmap的chunk - 能修改
mmap的chunk的大小
利用原理
这个技巧被称之为 steal heap from glibc。主要的点有以下几个:
libc.so.6映射的地址空间,前面都是与符号表、哈希表、字符串表等重定位或者解析函数地址有关,前面一段的权限是r--mmap(NULL, ...)是会分配到libc.so.6的上方的
基于这两个知识点,利用过程如下:
- 申请
chunk A,假设为0x40000大小,则会走mmap申请,并且申请到libc.so.6的上方 - 修改
chunk A的大小为0x45000,设置MMAP标志位 - 释放
chunk A,则会把libc.so.6的0x5000的内存也释放掉 - 再次申请
0x45000,就可以控制libc.so.6原来的符号表、哈希表等等 - 触发一次
dl_runtime_resolve等就能控制程序执行任意代码
相关技巧
- 需要伪造的符号表、哈希表等需要逐步调试
- 可以扩展为
steal heap from everywhere
利用效果
- 任意代码执行
2.16-house of botcake
漏洞成因
1 | |
适用范围
2.26—— 至今- 多次释放
chunk的能力
利用原理
该技巧可以用于绕过 tcache->key 的检查,利用过程如下:
- 申请
7个大小相同,大小大于0x80的chunk,再申请三个,分别为chunk A和chunkB和chunk C - 释放前
7个和chunk A,前面7个都会进入到tcachebin里面,chunk A进入到unsortedbin - 释放
chunk B,则chunk B会和chunk A合并 - 从
tcachebin分配走一个 - 再次释放
chunk B,此时B同时存在与unsortedbin和tcachebin
相关技巧
- 在高版本需要绕过指针保护的检查
利用效果
- 构造出堆重叠,为后续利用做准备
2.17-house of rust
漏洞成因
堆溢出
适用范围
2.26—— 至今- 可以进行
tcache stash unlinking攻击 - 可以进行
largebin attack - 不需要泄露地址
利用原理
原作者的博客写得很复杂,我这里提炼出关键信息。该技巧就是 tcachebin stash unlinking+largebin attack 的组合技巧。
首先需要知道 tcachebin stash unlinking,下面称之为 TSU 技巧:
tcachebin[A]为空smallbin[A]有8个- 修改第
8个smallbin chunk的bk为addr - 分配
malloc(A)的时候,addr+0x10会被写一个libc地址
还要知道 tcachebin stash unlinking+,下面称之为 TSU+ 技巧:
tcachebin[A]为空smallbin[A]有8个- 修改第
7个smallbin chunk的bk为addr,还要保证addr+0x18是一个合法可写的地址 - 分配
malloc(A)的时候,addr会被链入到tcachebin,也就是可以分配到addr处
以 0x90 大小的 chunk 为例,此时的 tcache_key 还是指向 tcache_perthread_struct + 0x10 的:
- 第一步,把
tcachebin[0x90]填满,把smallbin[0x90]也填满 - 第二步,把最后一个
smallbin 0x90的chunk的size改成0xb0,将其释放到tcachebin[0xb0],这一步主要是为了改变其bk指向tcache_perthread_struct + 0x10,可以部分修改低位的字节,以便下一步分配到目标区域 - 第三步,使用
largebin attack往上一步的bk->bk写一个合法地址,然后耗尽tcachebin[0x90],再分配的时候就会触发TSU+,之后就能分配到tcache_perthread_struct结构体 - 第四步,还是堆风水,但是用
TSU技术,在tcache_perthread_struct上写一个libc地址(比前面一步要简单很多) - 第五步,通过控制
tcache_perthread_struct结构体,部分写上面的libc地址,分配到stdout结构体,泄露信息 - 第六步,通过控制
tcache_perthread_struct结构体分配到任意地址
上面的过程最好的情况下需要爆破 1/16,最差 1/256。
但是,2.34 之后,tcache_key 是一个随机数,不是 tcache_perthread_struct + 0x10 了。
所以,此时可以加上 largebin attack,把以上的第二步变为:继续用 largebin attack 向其 bk 写一个堆地址,然后还要部分写 bk 使其落在 tcache_perthread_struct 区域。其他步骤一样。
或者,在 smallbin 里面放 9 个,这样第 8 个的 bk 肯定就是一个堆地址。此时就需要爆破 1/16 的堆,1/16 的 glibc 地址,成功的概率是 1/256。
相关技巧
- 总的来说,就是利用
tcachebin stash unlinking打tcache_perthread_struct - 利用
largebin attack构造合法地址
利用效果
- 任意地址分配
- 任意函数执行
2.18-house of crust
漏洞成因
堆溢出
适用范围
2.26——2.37- 可以进行
tcache stash unlinking攻击 - 可以进行
largebin attack - 不需要泄露地址
利用原理
其他步骤和上面的 house of rust 一样,但是到第五步的时候,去修改 global_max_fast
后面的步骤和 house of corrosion 是一样的,通过写原语打 stderr 修改 one_gadget 拿到 shell。
相关技巧
house of crust = house of corrosion + house of rust2.37之后,house of corrosion使用受限
2.19-house of io
漏洞成因
堆溢出
适用范围
2.26—— 至今
利用原理
其他博客上对该方法的介绍如下:
1 |
The tcache_perthread_object is allocated when the heap is created. Furthermore, it is stored right at the heap's beginning (at a relatively low memory address). The safe-linking mitigation aims to protect the fd/next pointer within the free lists. However, the head of each free-list is not protected. Additionally, freeing a chunk and placing it into the tcachebin also places a non-protected pointer to the appropriate tcache entry in the 2nd qword of a chunks' user data. The House of IO assumes one of three scenarios for the bypass to work. First, any attacker with a controlled linear buffer underflow over a heap buffer, or a relative arbitrary write will be able to corrupt the tcache. Secondly, a UAF bug allowing to read from a freed tcache eligible chunk leaks the tcache and with that, the heap base. Thirdly, a badly ordered set of calls to free(), ultimately passing the address of the tcache itself to free, would link the tcache into the 0x290 sized tcachebin. Allocating it as a new chunk would mean complete control over the tcache's values. |
|---|---|
可以看出来,其实就是对 tcache_perthread_struct 结构体的攻击,想办法将其释放掉,然后再申请回来,申请回来的时候就能控制整个 tcache 的分配。
相关技巧
- 围绕
tcache_perthread_struct进行攻击
利用效果
- 任意地址分配
2.20-house of banana
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以进行
largebin attack - 能执行
exit函数
利用原理
首先是 largebin attack 在高版本只能从下面这个分支利用:
也就是,双链表里面至少存在一个 largebin chunk,且目前要入链的 chunk 比最小的还小,修改了 bk_nextsize 之后就会触发。可以造成任意地址写堆地址。
然后是 exit 调用的时候,会调用到_dl_fini 函数,执行每个 so 中注册的 fini 函数:
可以触发 call 的有两个点,第一个点可以 call 到很多指针,是一个数组;另一个点就只有一个函数。
剩下的工作就是根据代码绕过检测,调用到调用点。
所以,利用的思路有:
- 直接伪造
_rtld_global的_ns_loaded,布局好其他内容,使其调用到fini_array - 伪造
link_map的next指针,布局好其他内容,使其调用到fini_array - 修改
link_map->l_addr,根据偏移使其调用到指定区域的函数
相关技巧
- 伪造
fini_array数组的时候,是从后往前遍历的 - 有时候远程的
rtld_global的偏移与本地不一样,需要爆破 - 如果不想逐个伪造,可以直接用
gdb从内存里面dump出来,然后基于偏移修改内存即可
利用效果
- 任意代码执行
2.21-house of kiwi
漏洞成因
堆溢出
适用范围
2.23——2.36- 在
malloc流程中触发assert
利用原理
主要是提供了一种在程序中调用 IO 流函数的思路:
可以看到,调用到了 fxprintf 和 fflush。
至于原 house of kiwi 所提到的控制 rdx 的思路,在很多版本中无法使用,因为 IO_jumps_table 都是不可写的,故此处不再详述。
相关技巧
- 在
2.36之后,__malloc_assert被修改为:
1 2 3 4 5 6 7 8 9 |
_Noreturn static void __malloc_assert (const char *assertion, const char *file, unsigned int line, const char *function) { __libc_message (do_abort, "\ Fatal glibc error: malloc assertion failure in %s: %s\n", function, assertion); __builtin_unreachable (); } |
|---|---|
而在 2.37 该函数直接被删掉了。
如果
stderr在libc上,需要修改调stderr处的指针,也有可能在程序的地址空间上伪造的技巧如下,触发
fxprintf(stderr,......):|
1 2 3 4 5|flags & 0x8000的话,不用伪造_lock flags & ~(0x2 | 0x8) 必须成立,避免走到unbuffered的流程 mode 设置为0 vtable默认调用的是偏移0x38的函数,如果想劫持为_IO_xxx_overflow,需要设置为_IO_xxx_jumps-0x20 flags 可以设置为" sh||",前面有两个空格,此时还需要设置_lock,不想设置_lock的时候,flags可以为"\x20\x80;sh||"|
| ———— | ———————————————————— |
| | |
利用效果
- 触发
IO处理流程,为后续利用做准备
2.22-house of emma
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以进行两次
largebin attack - 或者可以进行两次任意地址写堆地址
- 可以触发
IO流操作
利用原理
在_IO_cookie_jumps 中存在一些_IO_cookie_read 等函数,如下:
可以看到有函数指针的调用。但是对函数指针使用 pointer_guard 进行了加密:
循环右移后,再异或。
因此,利用思路如下:
- 截至某个
IO_FILE的指针(IO_list_all/stdxxx->chain等都可以)为堆地址 - 堆上伪造
IO_FILE结构,其vtable替换为_IO_cookie_jumps+XX,XX为一个偏移量 - 伪造好函数指针和调用参数,指针需要循环异或和加密
- 调用到
_IO_cookie_read等函数,进而执行任意函数
相关技巧
- 常用的
gadget有: pointer_guard就在canary下面,偏移可能需要爆破
利用效果
- 任意函数执行
2.23-house of pig
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以进行
largebin attack - 可以触发
IO流操作
利用原理
在_IO_str_jumps 中,存在着_IO_str_overflow 函数:
从函数中就能看到,利用流程如下:
- 伪造
IO_FILE的_IO_buf_base - 合理控制
_IO_buf_end-_IO_buf_base的值,进而控制分配的chunk的大小,分配到布局好的地址 - 在
memcpy中覆盖地址,如可以覆盖__malloc_hook/__free_hook等
该方法需要结合其他堆利用技术,需要保证 malloc 分配出来的 chunk 的地址是可控的。该方法主要提供了对 IO 系列函数中间接调用 mallc/free/memcpy 的组合利用。
相关技巧
- 可以
largebin attack打掉mp_.tcachebins,进而能把很大的chunk也放进入tcache进行管理 - 高版本没有
hook的话,可以利用memcpy@got,通过覆写got来进行rce - 可以多次
house of pig组合调用
利用效果
- 任意函数执行
ROP控制程序执行流
2.24-house of obstack
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以执行一次
largebin attack - 可以触发
IO流操作
利用原理
一条新的利用链,伪造 vtable 为_IO_obstack_jumps,然后调用到_IO_obstack_xsputn,紧接着调用 obstack_grow,其代码为:
1 2 3 4 5 6 7 8 9 |
#define obstack_grow(OBSTACK, where, length) \ __extension__ \ ({ struct obstack *__o = (OBSTACK); \ int __len = (length); \ if (_o->next_free + __len > __o->chunk_limit) \ _obstack_newchunk (__o, __len); \ memcpy (__o->next_free, where, __len); \ __o->next_free += __len; \ (void) 0; }) |
|---|---|
然后在_obstack_newchunk 调用了 CALL_CHUNKFUN 这个宏
这个宏会调用到函数指针:
1 2 3 4 |
# define CALL_CHUNKFUN(h, size) \ (((h)->use_extra_arg) \ ? (*(h)->chunkfun)((h)->extra_arg, (size)) \ : (*(struct _obstack_chunk *(*)(long))(h)->chunkfun)((size))) |
|---|---|
因此,其就是利用该函数指针进行控制程序的执行流。
相关技巧
伪造的 IO_FILE 布局如下:
- 利用
largebin attack伪造_IO_FILE,记完成伪造的chunk为A(或者别的手法) chunk A内偏移为0xd8处设为_IO_obstack_jumps+0x20chunk A内偏移为0xe0处设置chunk A的地址作为obstack结构体chunk A内偏移为0x18处设为1(next_free)chunk A内偏移为0x20处设为0(chunk_limit)chunk A内偏移为0x48处设为&/bin/shchunk A内偏移为0x38处设为system函数的地址chunk A内偏移为0x28处设为1(_IO_write_ptr)chunk A内偏移为0x30处设为0(_IO_write_end)chunk A内偏移为0x50处设为1(use_extra_arg)
glibc-2.37 开始这个方法的调用链为:__printf_buffer_as_file_overflow -> __printf_buffer_flush -> __printf_buffer_flush_obstack->__obstack_newchunk。
利用效果
- 任意函数执行
2.25-house of apple1
漏洞成因
堆溢出
适用范围
2.23—— 至今- 程序从
main函数返回或能调用exit函数 - 能泄露出
heap地址和libc地址 - 能使用一次
largebin attack(一次即可)
利用原理
利用_IO_wstr_overflow 将任意地址存储的值修改已知值:
比如修改 tcache 变量、mp_结构体、pointer_guard 变量等。
修改成功后,再使用其他技术控制程序执行流。
相关技巧
house of apple1 是对现有一些 IO 流攻击方法的补充,能在一次劫持 IO 流的过程中做到任意地址写已知值,进而构造出其他方法攻击成功的条件。
利用效果
- 任意地址写已知堆地址
2.26-house of apple2
漏洞成因
堆溢出
适用范围
2.23—— 至今- 已知
heap地址和glibc地址 - 能控制程序执行
IO操作,包括但不限于:从main函数返回、调用exit函数、通过__malloc_assert触发 - 能控制
_IO_FILE的vtable和_wide_data,一般使用largebin attack去控制
利用原理
_IO_WIDE_JUMPS 没有检查_wide_vtable 的合法性:
1 2 3 4 5 6 7 8 |
#define _IO_WOVERFLOW(FP, CH) WJUMP1 (__overflow, FP, CH) #define WJUMP1(FUNC, THIS, X1) (_IO_WIDE_JUMPS_FUNC(THIS)->FUNC) (THIS, X1) #define _IO_WIDE_JUMPS_FUNC(THIS) _IO_WIDE_JUMPS(THIS) #define _IO_WIDE_JUMPS(THIS) \ _IO_CAST_FIELD_ACCESS ((THIS), struct _IO_FILE, _wide_data)->_wide_vtable |
|---|---|
所以利用_IO_wfile_jumps 等伪造_wide_vtable 即可。
相关技巧
利用_IO_wfile_overflow 函数控制程序执行流时对 fp 的设置如下:
_flags设置为~(2 | 0x8 | 0x800),如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为sh;,注意前面有两个空格vtable设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap地址(加减偏移),使其能成功调用_IO_wfile_overflow即可_wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A_wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0_wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0_wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B_wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C
利用效果
- 任意函数执行
2.27-house of apple3
漏洞成因
堆溢出
适用范围
2.23—— 至今- 已知
heap地址和glibc地址 - 能控制程序执行
IO操作,包括但不限于:从main函数返回、调用exit函数、通过__malloc_assert触发 - 能控制
_IO_FILE的vtable和_wide_data,一般使用largebin attack去控制
利用原理
__libio_codecvt_in 等函数,可以设置 gs->__shlib_handle == NULL 绕过 PTR_DEMANGLE 对指针的保护,然后通过_IO_wfile_underflow 调用到__libio_codecvt_in 来控制函数指针,执行任意代码。
相关技巧
利用_IO_wfile_underflow 函数控制程序执行流时对 fp 的设置如下:
_flags设置为~(4 | 0x10)vtable设置为_IO_wfile_jumps地址(加减偏移),使其能成功调用_IO_wfile_underflow即可fp->_IO_read_ptr < fp->_IO_read_end,即满足*(fp + 8) < *(fp + 0x10)_wide_data保持默认,或者设置为堆地址,假设其地址为A,即满足*(fp + 0xa0) = A_wide_data->_IO_read_ptr >= _wide_data->_IO_read_end,即满足*A >= *(A + 8)_codecvt设置为可控堆地址B,即满足*(fp + 0x98) = Bcodecvt->__cd_in.step设置为可控堆地址C,即满足*B = Ccodecvt->__cd_in.step->__shlib_handle设置为0,即满足*C = 0codecvt->__cd_in.step->__fct设置为地址D, 地址D用于控制rip,即满足*(C + 0x28) = D。当调用到D的时候,此时的rdi为C。如果_wide_data也可控的话,rsi也能控制。
利用效果
- 任意函数执行
2.28-house of gods
漏洞成因
堆溢出
适用范围
2.23——2.27- 泄露堆地址和
libc地址 - 任意大小分配
利用原理
这个技巧比较有意思,非常建议把作者的原博客读一下。我会简述一下该技巧的利用过程。
总的来说,该技巧最终的目的是伪造一个 fake arena,通过劫持 main_arena.next 字段完成。
其主要过程为:
- 通过
binmap的赋值,将其当做chunk的size,然后修改unsortedbin链的bk指向binmap,作者选择的是0x90大小的chunk,释放后恰好让binmap称为0x200,然后binmap->bk是main_arena(初始状态下main_arena.next = &main_arena),然后main_arena->bk= fastbin[0x40] - 分配
0x1f0大小的chunk就刚好能分配到binmap - 之后修改掉
main_arena的system_mem为很大的值和next指向fake arena - 然后用
unsortedbin attack打掉narenas,将其改为一个很大的数 - 然后分配两次
malloc(0xffffffffffffffbf + 1),触发arena_get_retry,进而触发两次reused_arena,就能把fake arena给thread_arena变量 - 最后直接伪造
fastbin任意地址分配
相关技巧
- 仅仅借助
unsortedbin链就能控制main_arena的next和system_mem - 利用
binmap的值构造出合法的size
利用效果
- 劫持
thread_arena为fake_arena
3 - 总结
- 总结了
28种house of系列利用手法 - 给出了每种利用手法的影响版本、适用范围、利用原理等
- 所有的利用方法都可以在源码中找到答案,因此强烈建议将源码反复阅读
- 可以根据目前已有的技术提出新的组合技
4 - 参考
[1] 堆利用系列之 house of spirit - 安全客 - 安全资讯平台 (anquanke.com)
[2] shellphish/how2heap: A repository for learning various heap exploitation techniques. (github.com)
[3] Overview of GLIBC heap exploitation techniques (0x434b.dev)
[4] [原创] CTF 中 glibc 堆利用 及 IO_FILE 总结 - Pwn - 看雪论坛 - 安全社区 | 安全招聘 | bbs.pediy.com (kanxue.com)
[5] PWN——House Of Einherjar CTF Wiki 例题详解 - 安全客 - 安全资讯平台 (anquanke.com)
[6] Top chunk 劫持:House of force 攻击 - 安全客 - 安全资讯平台 (anquanke.com)
[7] House of Lore - CTF Wiki (ctf-wiki.org)
[8] House of orange - 安全客 - 安全资讯平台 (anquanke.com)
[9] house of rabbit
[10] House of Roman - CTF Wiki (ctf-wiki.org)
[11] House of storm 原理及利用 - 安全客 - 安全资讯平台 (anquanke.com)
[12] House-of-Corrosion 一种新的堆利用技巧 - 先知社区 (aliyun.com)
[13] house-of-husk 学习笔记 - 安全客 - 安全资讯平台 (anquanke.com)
[14] House of Muney 分析 - 安全客 - 安全资讯平台 (anquanke.com)
[15] 奇安信攻防社区 - 深入理解 House of Botcake 堆利用手法 (butian.net)
[17] house of banana - 安全客 - 安全资讯平台 (anquanke.com)
[18] House OF Kiwi - 安全客 - 安全资讯平台 (anquanke.com)
[19] house of emma
[20] house of pig 一个新的堆利用详解 - 安全客 - 安全资讯平台 (anquanke.com)
[21] 一条新的 glibc IO_FILE 利用链:_IO_obstack_jumps 利用分析 - 跳跳糖 (tttang.com)
[22] House of Apple 一种新的 glibc 中 IO 攻击方法 (1) - roderick - record and learn! (roderickchan.cn)
[23] House of Apple 一种新的 glibc 中 IO 攻击方法 (2) - roderick - record and learn! (roderickchan.cn)
[24] House of Apple 一种新的 glibc 中 IO 攻击方法 (3) - roderick - record and learn! (roderickchan.cn)
[25] GlibcHeap-house of muney - roderick - record and learn! (roderickchan.cn)
[26] house-of-gods/HOUSE_OF_GODS.TXT at master · Milo-D/house-of-gods (github.com)