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 rust
2.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+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
)
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
设置为0
fp->_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/