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
 19- link_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
 13- link_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/
