how2heap
house of手法
前言
house of的手法只需理解其中的技巧,了解如何从小小的漏洞获得rce
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 | /* Take a chunk off a bin list */ |
只需要绕过__builtin_expect (FD->bk != P || BK->fd != P, 0) 即可,因此,不需要伪造地址处于高位的 chunk 的 presize 域。
高版本的 unlink 的条件是:
1 | /* Take a chunk off a bin list. */ |
新增了 chunksize (p) != prev_size (next_chunk (p)),对 chunksize 有了检查,伪造的时候需要绕过。
利用效果
- 构造
chunk overlap后,可以任意地址分配 - 结合其他方法进行任意地址读写
House of muney
利用版本
2.23—— 至今
这里分析函数延迟绑定机制中写入真实地址的过程
首先push n;push m;jmp _dl_runtime_resolve_xsavec;
n是函数在.rela.plt的位置
m是codebase
1 | => 0x7ffff7fd8d30 <_dl_runtime_resolve_xsavec>: endbr64 |
保存工作状态,然后jmp r11,即jmp _dl_fixup
一下分析_dl_fixup
1 | /* This function is called through a special trampoline from the PLT the |
只了解了大概流程
利用过程
ptmalloc 堆分配器在分配超大内存 > 128K 的时候,会调用 mmap 申请系统内存,此时申请到的内存一般位于 libc.so.6 映射的内存地址的低地址处。house of muney 的核心在于修改 mmap 内存的 size 大小,使其能把 libc.so.6 的符号表、哈希表等数据所在的地址空间也释放掉。然后再把这一片空间给申请回来,就能伪造符号表、哈希表,那么在解析函数实际地址的时候就能控制其解析为任意地址,进而控制程序执行流。
A = mmap(addr=NULL, length=0x1000,...)- 修改
A的size,为0x1000 + XXX free(A),实际执行的是:munmap(A, 0x1000 + XXX),就可以偷取glibc的内存mmap(addr=NULL, length=0x1000 + XXX, ... ),然后输入数据,就可以控制 “偷去” 的内存的内容- 在进行符号解析的时候,进行任意函数调用
需要伪造的结构有:
- bitmask_word
- bucket
- hasharr,需要多伪造几个,并不是第一个就满足条件
- target symbol ->st_value,符号表中,除了st_value修改为目标地址外,其他成员建议保持不变
简单来说,就是伪造符号表,让初次调用funcA时的真实地址,解析成funcB达到任意代码执行的效果
给出模板
1 | add(0, 0x40000 - 0x2000) |
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之后,可以任意地址分配、任意地址读写
house of force
漏洞成因
堆溢出写 top_chunk
适用范围
2.23——2.28- 可分配任意大小的
chunk - 需要泄露或已知地址
利用原理
对 top_chunk 的利用,过程如下:
- 申请
chunk A - 写
A的时候溢出,修改top_chunk的size为很大的数 - 分配很大的
chunk到任意已知地址
相关技巧
注意,在 glibc-2.29 后加入了检测,house of force 基本失效:

利用效果
- 任意地址分配
- 任意地址读写
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 的时候,需要注意绕过:
1 |
|
要么使其满足 tc_victim = last (bin)) == bin、要么使其满足:tcache->counts[tc_idx] ≥ mp_.tcache_count。否则可能会因为非法内存访问使得程序 down 掉。(对应tcache为空或者满)
实际上,这个技巧用得不是很多,因为在同等条件下,更偏向于利用 fastbin/tcachebin。
利用效果
- 任意地址分配
- 任意地址读写
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里面的函数指针(也可以exit调用之类的,感觉不一定局限于malloc_printerr)
相关技巧
- 在
glibc-2.24后加入了vtable的check,不能任意地址伪造vatble了,但是可以利用IO_str_jumps结构进行利用。 - 在
glibc-2.26后,malloc_printerr不再刷新IO流了,所以该方法失效 - 由于
_mode的正负性是随机的,影响判断条件,大概有1/2的概率会利用失败,多试几次就好
利用效果
- 任意函数执行
- 任意命令执行
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 的利用。因此,不一定要按照原作者的思路来,他的思路需要满足的条件太多了。
利用效果
- 任意地址分配
- 任意地址读写
house of roman
漏洞成因
use after free、堆溢出
适用范围
2.23——2.29- 可以
use after edit - 不需要泄露地址
- 需要部分字节
利用原理
申请chunkA,chunkB,chunkC,chunkD,chunkE,其中chunkB的大小为0xd0
在chunkB中写入
"A"*0x68+p64(0x61)释放掉B,B进入unsortedbin,然后chunkA溢出修改chunkB的size为0x71,此时chunkB的fd和bk都是main_arena+88
然后将chunkD和chunkE都释放进fastbin中,size域为0x70的
然后利用uaf部分地址写,将chunkB伪造的chunk链入fastbin中
1
0x70: 0x555555757160 —▸ 0x555555757020 —▸ 0x7ffff7dd1b78 (main_arena+88) ◂— 0x7ffff7dd1b78
然后修改chunkB的fd的低2字节,使B->fd= malloc_hook - 0x23
然后分配3个chunk的size为0x70的就能获得这个fake chunk了
然后在之前就要多分配一个0x70大小的chunk此时来通过uaf修复fastbin(注意堆风水)
之后通过unsortedbin attack来修改malloc_hook为一个libcbase+0xXXXX
之后部分写malloc_hook的低字节使其偏向onegadget,需要爆破
相关技巧
- 使用
house of roman的时候,需要采用多线程爆破 - 可以使用其他方法代替,比如先攻击
stdout泄露地址,使得爆破的成本降低
利用效果
- 执行
one_gadget - 绕过
ASLR
house of storm
漏洞成因
堆溢出、use after free、edit after free
适用范围
2.23——2.28因为2.29的unsortedbin attack失效- 可以进行
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里面取
利用效果
- 任意地址分配
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 | chunk size = (chunk addr - &main_arena.fastbinsY) x 2 + 0x20 |
原语1:(任意地址任意写)
假如我们要修改的地址为0x2000上的数据,此时我们的main_arena.fatbinsY的地址是0x1000
计算size=(0x2000-0x1000)*2+0x20=0x2020,所以就要先事先chunkA=malloc(0x2018)
之后通过unsortedbin attack或者largebin attack,改写global_max_fast
此时free掉事先分配好的chunkA,目标地址会指向A

通过UAF修改A的fd为value,*A=value

之后再分配回来,value也就成功写入对应的target_addr

原语2:(转移已经存在的值)
- 首先假设目标地址为0x2000,0x3000上存有一个libc地址,fastbinY的地址为0x1000,计算
size1=(0x2000-0x1000)*2+0x20=0x2020,size2=(0x3000-0x1000)*2+0x20=0x4020 - 此时分配两个size都为size1的chunkA和chunkB,并且通过unsortedbin attack或者largebin attack改写global_max_fast
- 之后释放掉chunkA和chunkB

- 通过UAF,部分写chunkA的fd使其指向本身,达到double free类似的效果

- 再把A给分配回来,同时篡改chunkA的size为size2,释放掉A

- 再次篡改A的size,恢复为size1,然后malloc(size1),就成功完成src->dst数据的转移
相关技巧
- 虽然至今都能使用
house of corrosion,但是在glibc-2.37版本中,global_max_fast的数据类型被修改为了int8_u,进而导致可控的空间范围大幅度缩小。 house of corrosion也可以拓展到tcachebin上- 适当控制
global_max_fast的大小,把握控制的空间范围 - 可以和
IO_FILE结合起来泄露信息
利用效果
glibc上的地址泄露- 执行
one_gadget
house of husk
漏洞成因
堆溢出
适用范围
- 2.23–2.35
- 劫持
__printf_function_table使其不为空,劫持__printf_arginfo_table使其表中存放的spec的位置是backdoor(),执行到printf函数时就可以将执行流劫持到backdoor() - 可触发格式化字符串解析
简单来说printf对自定义的格式化字符串的处理优先于默认的格式化字符串处理,我们通过篡改
__printf_function_table来使程序认为存在注册过的自定义格式化字符串,从而触发__printf_arginfo_table上的函数指针
利用原理
调用处 1:
1 | // |
利用方式为:
__printf_function_table和__printf_arginfo_table分别写为chunk A和chunk B的地址- 设占位符为
α,此时chunk B的内容应该为p64(0) x ord(α-2) + p64(one_gadget)
调用处 2:
1 | // vfprintf-internal.c#1962 |
利用方式为:
__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控制程序执行流
house of kauri
漏洞成因
堆溢出
适用范围
2.26——2.32
利用原理
利用原理很简单,修改 tcachebin 的 size,然后使其被放到不同大小的 tcachebin 链表里面去。我感觉这个技巧是很基础的 tcachebin 技巧,甚至不应该被称之为 house of。
相关技巧
- 无
利用效果
- 多个
tcachebin链表中存放同一个chunk
house of mind
漏洞成因
堆溢出,edit after free
适用范围
2.23—— 至今- 可以分配任意大小的
chunk - 程序在子线程执行malloc
利用原理
通过多线程或者特定的分配方式,创建一块非主arena的chunkA
然后通过chunkA的地址用
1
2该方式计算出
heap_for_ptr的地址在
heap_info_addr处伪造mstate ar_ptr指针指向攻击者控制的伪造arena区域之后修改chunkA的
non_main_arena标志位为1之后释放chunkA,此时arena_for_chunk(A)会通过伪造的
heap_info_addr->ar_ptr找到伪造的arena区域而伪造的arena区域 可以 控制 fastbin 链表头,实现任意地址分配,以此实现任意地址写
相关技巧
- 一般来说,可以分配任意大小的
chunk,还能堆溢出,很多技巧都能用 - 这个技巧是希望大家关注对于
arena的攻击 - 甚至可以直接修改
thread_arena这个变量
利用效果
- 任意地址写任意值
house of botcake
漏洞成因
double free
适用范围
2.26—— 至今- 多次释放
chunk的能力
利用原理
该技巧可以用于绕过 tcache->key 的检查,利用过程如下:
- 申请7个大于size大于0x80的chunk,然后再申请3个,分别为chunkA,chunkB,chunkC(chunkC只是用来隔断
top chunk) - 释放前7个chunk进入tcache,之后释放chunkB进入unsortedbin,然后释放chunkA与chunkB合并
- 之后从tcache中分配一个chunk
- 然后再次释放chunkB,使得chunkB进入tcache,此时chunkB就既位于unsortedbin也位于tcache中
- 之后再次申请一个chunk,就得到了chunkB,但是此时的chunkB还位于unsortedbin
相关技巧
- 在高版本需要绕过指针保护的检查
利用效果
- 构造出堆重叠,为后续利用做准备
house of rust
漏洞成因
堆溢出
适用范围
2.26——至今- 可以进行
tcache 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处
第一阶段:堆风水布局(Heap Feng Shui)
- 首先先分配14个0x90的chunk(编号1-14),然后再接替释放到tcache和smallbin中(1\3\5\7\9\11\13释放到tcahce….)
- 之后通过分配大chunk将unsortedbin中的都放入smallbin
第二阶段:Tcache Stashing Unlink+ 结合 Largebin 攻击
- 然后通过WAF修改chunk14的size片段为0xB0,之后再次释放chunk14到tcahce[0xB0]中,此时他的bk就是tcahce_key,也就是
&tcahce_perthread_struct+0x10,但是同时也造成了smallbin链的破坏 - 再次修改chunk14的bk的最低字节为0x80,指向
tcache_perthread_struct + 0x80 - 0x18(0x30 tcache头部附近) - 之后通过largebin attack修改chunk14的fd以修复smallbin链
- 清空tcahe然后再次分配0x90块,触发Tcache Stashing Unlink+机制,使得0x90 tcache头部指向
tcache_perthread_struct + 0x80
第三阶段:Tcache Stashing Unlink 结合二次Largebin攻击
- 分配15个0xa0的块
- 通过将
Tcache Stashing Unlink将libc地址写入tcache_perthread_struct
第四阶段:stdout FSOP泄露libc
- 利用第三阶段写入的libc地址,部分写,多次爆破,即可将chunk分配到
_IO_2_1_stdout_结构 - 之后通过结构体复写即可触发libc泄露,泄露完就简单多了
1 | _flags = 0xfbad1800 |
第五阶段:最终Shell获取
__free_hook写system或者onegadget都是可以的
上面的过程最好的情况下需要爆破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构造合法地址
利用效果
- 任意地址分配
- 任意函数执行
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使用受限
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进行攻击
利用效果
- 任意地址分配
house of banana
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以进行
largebin attack - 能执行
exit函数
利用原理
1 | for (i = 0; i < nmaps; ++i) |
1 | ElfW(Addr) *array = |
也就是我们将l->l_info[26]写入l_info[26]的地址,array的值就是l_info[27]中存放的值
1 | unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val |
也就是说我们在l_info[28]写入l_info[28]的地址,i的值就是l_info[29]中存放的值/8
1 | while (i-- > 0) |
最后会执行array[i]->array[0],从i到0调用
2.31打orw:
通过largebin attack伪造
_rtld_global的link_map地址然后布局link_map打orw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19link_map=p64(0)
link_map+=p64(libc_base+0x223740)#l_next
link_map+=p64(0)
link_map+=p64(heap_base+0xb90+0x40)#l_real
link_map+=p64(0)*28
link_map+=p64(heap_base+0xc08+0x98+0x40)#l->l_info[26]
link_map+=p64(heap_base+0xc08+32+0x98+0x40)#l->l_info[26]->d_un.d_ptr
link_map+=p64(heap_base+0xc08+0x10+0x98+0x40)#l->l_info[28]
link_map+=p64(0x20)#//i=l->l_info[28]->d_un.d_val
link_map+=b"flag\x00\x00\x00\x00"
link_map+=p64(heap_base+0xb90+0x40)
link_map+=p64(setcontext)
link_map+=p64(ret_addr)
link_map+=p64(0)*12
link_map+=p64(0)#rdi
link_map+=p64(heap_base+0xdc8)#rsi
link_map+=p64(0)*2
link_map+=p64(0x100)#rdx
link_map+=p64(0)*2
2.27打onegadget:
link_map:
1
2
3
4
5
6
7
8
9
10
11
12
13link_map=p64(0)*1
link_map+=p64(libc_base+0x61c710)#l_next
link_map+=p64(0)
link_map+=p64(heap_base+0xb90)#l_real
link_map+=p64(0)*28
link_map+=p64(heap_base+0xc08+0x98)
link_map+=p64(heap_base+0xc08+32+0x98)
link_map+=p64(heap_base+0xc08+0x10+0x98)
link_map+=p64(8)#//i=l->l_info[28]->d_un.d_val
link_map+=p64(one_gadget)
link_map+=p64(heap_base+0xb90)
link_map+=p64(0)*58
link_map+=p64(0x800000000)
相关技巧
- 伪造
fini_array数组的时候,是从后往前遍历的 - 有时候远程的
rtld_global的偏移与本地不一样,需要爆破 - 如果不想逐个伪造,可以直接用
gdb从内存里面dump出来,然后基于偏移修改内存即可
利用效果
- 任意代码执行
house of kiwi
漏洞成因
堆溢出
适用范围
2.23——2.36- 在
malloc流程中触发assert
利用原理
主要是提供了一种在程序中调用 IO 流函数的思路:
1 |
|
可以看到,调用到了 fxprintf 和 fflush(stderr)。对stderr进行劫持
在_int_malloc中
1 | assert ((old_top == initial_top (av) && old_size == 0) || |
这个对top进行了assert判断
1 | old_size >= 0x20; |
因此只要堆溢出改个size就可以触发assert了
在__fxprintf函数中调用的是偏移0x38的指针
在fflush函数中调用到了一个指针:位于_IO_file_jumps中的_IO_file_sync指针,且观察发现RDX寄存器的值为IO_helper_jumps指针
因此有以下的利用:
- 通过largebin attack劫持stderr指针
- 通过堆溢出或其他手法触发assert
- 修改
_IO_file_jumps + 0x60的_IO_file_sync指针为setcontext+61 - 修改
IO_helper_jumps + 0xA0 and 0xA8分别为可迁移的存放有ROP的位置和ret指令的gadget位置,则可以进行栈迁移(感觉条件要求好多,但是对于assert触发的IO可以利用)
利用效果
- 触发
IO处理流程,为后续利用做准备
house of emma
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以进行两次
largebin attack - 或者可以进行两次任意地址写堆地址
- 可以触发
IO流操作
利用原理
在vtable的合法范围之内,存在_IO_cookie_jumps,可以通过偏移来调用vtable表里的所有函数
1 | static const struct _IO_jump_t _IO_cookie_jumps libio_vtable = { |
1 | static ssize_t |
这四个函数作为我们考虑范围内的函数,发现其中调用的函数都是_IO_cookie_file的一个成员
1 | /* Special file type for fopencookie function. */ |
但是我们发现
1 |
|
在Glibc2.34及以上版本中的函数都进行了指针加密
1 | 0x7ffff7e06774 <_IO_cookie_write+20>: ror rax,0x11 |
显示右移0x11位,然后和tls里的一个数据进行了异或,因此我们只需要通过TSU,largebin attack等的手法将其改为一个特定的值,就能进行绕过,反正要么泄露要么覆盖,只要能成就行
因此,利用思路如下:
- 截至某个
IO_FILE的指针(IO_list_all/stdxxx->chain等都可以)为堆地址 - 堆上伪造
IO_FILE结构,其vtable替换为_IO_cookie_jumps+XX,XX为一个偏移量 - 伪造好函数指针和调用参数,指针需要循环异或和加密(在执行
_IO_cookie_write的时候,rdi是fake_iofile,因此可以通过gadget来ROP) - 调用到
_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下面,偏移可能需要爆破
利用效果
- 任意函数执行
例题
2021 湖湘杯的 House OF Emma
house of pig
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以进行
largebin attack - 可以触发
IO流操作
利用原理
在_IO_str_jumps 中,存在着_IO_str_overflow 函数:
1 | int |
1 |
只需要合理控制_IO_buf_end和_IO_buf_base就可以实现chunk的size可控,只要分配出来的chunk也可控,那么就可以在任意地址任意写
利用流程如下:
- 先通过largebin attack将想控制的chunk_addr+0x18写上一个堆地址,然后通过tcache stashing unlink+使得分配的chunk可控
- 伪造一个
_IO_FILE_plus结构,其vtable指向_IO_str_jumps - 然后伪造的
_IO_buf_base为想写入的值的起点(所以要先在已知地址写入想写入的数据),之后合理控制_IO_buf_end实现控制分配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控制程序执行流
house of obstack
漏洞成因
堆溢出
适用范围
2.23—— 至今- 可以执行一次
largebin attack - 可以触发
IO流操作
利用原理
一条新的利用链,伪造 vtable 为_IO_obstack_jumps,然后调用到_IO_obstack_xsputn,紧接着调用 obstack_grow,其代码为:
1 |
然后在_obstack_newchunk 调用了 CALL_CHUNKFUN 这个宏
1 | void |
这个宏会调用到函数指针:
1 | void |
因此,其就是利用该函数指针进行控制程序的执行流。
相关技巧
伪造的 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)
1 | payload=flat( |
glibc-2.37 开始这个方法的调用链为:__printf_buffer_as_file_overflow -> __printf_buffer_flush -> __printf_buffer_flush_obstack->__obstack_newchunk。
利用效果
- 任意函数执行
house of apple2
漏洞成因
- 堆溢出
适用范围
2.23—— 至今- 已知
heap地址和glibc地址 - 能控制程序执行
IO操作,包括但不限于:从main函数返回、调用exit函数、通过__malloc_assert触发 - 能控制
_IO_FILE的vtable和_wide_data,一般使用largebin attack去控制
利用原理
在_IO_wfile_jumps的_IO_wfile_overflow 函数中
1 | wint_t |
调用_IO_wdoallocbuf (f);
1 | void |
调用了(wint_t)_IO_WDOALLOCATE (fp)这个函数
1 |
可以看到调用的是_wide_vtable里的__doallocate函数
1 | struct _IO_FILE_plus |
有一个_wide_data结构体,之前分析调用的是_wide_data的wide_vtable里的函数
1 | struct _IO_wide_data |
因此有以下利用:
- 将
IO_FILE中的vtable字段改为_IO_wfile_jumps - 将
IO_FILE中的wide_data设置为可控堆地址,目的是控制wide_data中的write_base和buf_base为0 - 控制
wide_data->wide_vtable为地址A,地址A满足*(A+0x68) == system(此处的system地址是自己布置的)
总结下执行到最后的位置需要绕过的检查
_flags设置为~(2 | 0x8 | 0x800),如果是需要获取shell的话,那么可以将参数写为sh;这样_flags既能绕过检查,又能被system函数当做参数成功执行。需要注意的是sh;前面是有两个空格的(这个值是0x3b68732020)_wide_data->_IO_write_base设置为0,fp->_wide_data->_IO_buf_base设置为0fp->_mode == 0和fp->_IO_write_ptr > fp->_IO_write_base,这样即可触发_IO_OVERFLOW。
相关技巧
利用_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
利用效果
- 任意函数执行
参考
https://www.roderickchan.cn/zh-cn/2023-02-27-house-of-all-about-glibc-heap-exploitation/
