Glibc堆利用之house of系列总结
总结一下
glibc
堆利用的house of
系列利用手法,主要参考了how2heap,同时参考了其他优秀的文章。
文章很长,听首音乐放松一下吧~
- 1-前言
- 2-house of系列
- 2.1-house of spirit
- 2.2-house of einherjar
- 2.3-house of force
- 2.4-house of lore
- 2.5-house of orange
- 2.6-house of rabbit
- 2.7-house of roman
- 2.8-house of storm
- 2.9-house of corrosion
- 2.10-house of husk
- 2.11-house of atum
- 2.12-house of kauri
- 2.13-house of fun
- 2.14-house of mind
- 2.15-house of muney
- 2.16-house of botcake
- 2.17-house of rust
- 2.18-house of crust
- 2.19-house of io
- 2.20-house of banana
- 2.21-house of kiwi
- 2.22-house of emma
- 2.23-house of pig
- 2.24-house of obstack
- 2.25-house of apple1
- 2.26-house of apple2
- 2.27-house of apple3
- 2.28-house of gods
- 2.29-house of lys
- 2.30-house of snake
- 3-总结
- 4-参考
可以在Bilibili上观看视频进行学习:
或者在Youtube上观看视频进行学习:
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
的代码是:
|
|
只需要绕过__builtin_expect (FD->bk != P || BK->fd != P, 0)
即可,因此,不需要伪造地址处于高位的chunk
的presize
域。
高版本的unlink
的条件是:
|
|
新增了chunksize (p) != prev_size (next_chunk (p))
,对chunksize
有了检查,伪造的时候需要绕过。
利用效果
- 构造
chunk overlap
后,可以任意地址分配 - 结合其他方法进行任意地址读写
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
来进行读和写
此时的计算公式为:
chunk size = (chunk addr - &main_arena.fastbinsY) x 2 + 0x20
读原语:
- 假设对应的地址
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
漏洞成因
edit after free
适用范围
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
// glibc ≥ 2.30 void * __libc_malloc (size_t bytes) { //...... MAYBE_INIT_TCACHE (); DIAG_PUSH_NEEDS_COMMENT; if (tc_idx < mp_.tcache_bins && tcache && tcache->counts[tc_idx] > 0) { return tcache_get (tc_idx); } } // glibc < 2.30 void * __libc_malloc (size_t bytes) { //...... MAYBE_INIT_TCACHE (); DIAG_PUSH_NEEDS_COMMENT; if (tc_idx < mp_.tcache_bins && tcache && tcache->entries[tc_idx] != NULL) { return tcache_get (tc_idx); } }
-
有时候需要绕过
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
利用原理
主要利用的是:
|
|
如果是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
漏洞成因
double free
适用范围
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 rust
2.37
之后,house of corrosion
使用受限
2.19-house of io
漏洞成因
堆溢出
适用范围
2.26
——至今
利用原理
其他博客上对该方法的介绍如下:
|
|
可以看出来,其实就是对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
被修改为:
|
|
而在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
有:1 2 3 4 5 6 7 8 9 10 11 12 13
;栈迁移 mov rbp,QWORD PTR [rdi+0x48] mov rax,QWORD PTR [rbp+0x18] lea r13,[rbp+0x10] mov DWORD PTR [rbp+0x10],0x0 mov rdi,r13 call QWORD PTR [rax+0x28] ; rdi转rdx mov rdx, qword ptr [rdi + 8] mov qword ptr [rsp], rax call qword ptr [rdx + 0x20]
-
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
,其代码为:
|
|
然后在_obstack_newchunk
调用了CALL_CHUNKFUN
这个宏
|
|
这个宏会调用到函数指针:
|
|
因此,其就是利用该函数指针进行控制程序的执行流。
相关技巧
伪造的IO_FILE
布局如下:
- 利用
largebin attack
伪造_IO_FILE
,记完成伪造的chunk
为A
(或者别的手法) chunk A
内偏移为0xd8
处设为_IO_obstack_jumps+0x20
chunk A
内偏移为0xe0
处设置chunk A
的地址作为obstack
结构体chunk A
内偏移为0x18
处设为1
(next_free
)chunk A
内偏移为0x20
处设为0
(chunk_limit
)chunk A
内偏移为0x48
处设为&/bin/sh
chunk 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
的合法性:
|
|
所以利用_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) = B
codecvt->__cd_in.step
设置为可控堆地址C
,即满足*B = C
codecvt->__cd_in.step->__shlib_handle
设置为0
,即满足*C = 0
codecvt->__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
2.29-house of lys
漏洞成因
堆溢出
适用范围
2.23
——至今- 泄露堆地址和
libc
地址 - 任意大小分配
利用原理
调用 _IO_obstack_XXX
函数的时候,没有检查函数指针的有效性。调用链为:
|
|
相关技巧
- 伪造
vtable
为_IO_obstack_jumps
,然后触发_IO_obstack_xsputn
函数执行上述调用链
利用效果
- 任意函数执行
2.30-house of snake
和上一个 house of lys
的原理是类似的,但是用于 glibc-2.37
3-总结
- 总结了
30
种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)