Glibc高版本堆利用方法总结
截止到目前,主要总结在
2.35~2.37之间仍然残存的堆利用手法。
可以在Bilibili上观看视频进行学习,或者在Youtube上观看视频进行学习。
进入到glibc-2.31之后,很多原有的堆利用方法就失效,因此glibc给堆分配机制陆陆续续打上了很多patch,目前来看,与堆利用有关的patch有:
tcachebin堆指针异或加密(glibc-2.32引入)tcahebin链的数量检查(glibc-2.33引入)fastbin堆指针异或加密(glibc-2.32引入)- 堆内存对齐检查(
glibc-2.32引入) - 移除
__malloc_hook和__free_hook(glibc-2.34引入) - 引入
tcache_key作为tcache的key检查(glibc-2.34引入) __malloc_assert移除掉IO处理函数(glibc-2.36引入)- 移除
__malloc_assert函数(glibc-2.37引入) - 将
global_max_fast的数据类型修改为uint8_t(glibc-2.37引入)
根据目前已有的patch,结合之前已有的堆利用方法,总结2.35版本之后的攻击向量与攻击面,给出针对这些攻击面的攻击手段,并对某些攻击面的利用方法进行思考和拓展。如有错误或遗漏,欢迎批评指正。
本文所提到的house of系列的利用手段,可以参考我之前写的博客Glibc堆利用之house of系列总结 - roderick - record and learn! (roderickchan.cn)。
1-攻击向量
1-1 tcachebin
事实上,在泄露地址的基础上劫持tcachebin的next,依然可以任意地址分配。
1-1-1 绕过指针保护
绕过指针异或的保护方法主要有两种:
-
当
tcachebin链表中只有一个chunk的时候,此时chunk->next << 12即可得到堆地址。 -
当
tcachebin链表的前两个chunk的地址相差不是很大的时候,可以用下面的公式计算:def calc_heap(addr): s = hex(addr)[2:] s = [int(x, base=16) for x in s] res = s.copy() for i in range(9): res[3+i] ^= res[i] res = "".join([hex(x)[2:] for x in res]) return int16_ex(res)这里的
addr就是头部chunk的加密后的next,只泄露一次就能还原出来。
1-1-2 劫持tcache_ptheread_struct
这个结构体的重要性不言而喻,劫持了这个结构体可以控制tcachebin的分配。一般可以用tcachebin stash unlink或者largebin attack劫持。
1-1-3 修改线程tcache变量
在tls区域,有一个线程变量tcache,如果能用largebin attack修改tcache变量,也可以控制tcache的分配。
1-1-4 修改mp_结构体
关注与tcache有关的几个变量:
struct malloc_par
{
//......
#if USE_TCACHE
/* Maximum number of buckets to use. */
size_t tcache_bins;
size_t tcache_max_bytes;
/* Maximum number of chunks in each bucket. */
size_t tcache_count;
/* Maximum number of chunks to remove from the unsorted list, which
aren't used to prefill the cache. */
size_t tcache_unsorted_limit;
#endif
};
修改掉tcache_bins可以把很大的chunk用tcachebin管理;修改掉tcache_count可以控制链表的chunk的数量。tcache_max_bytes目前没啥用,tcache_unsorted_limit可以影响unsortedbin链表的遍历过程。
1-2 fastbin
1-2-1 house of corrosion
使用的范围只能在2.35~2.37,进入到2.37之后,global_max_fast的类型被修改为int8_t,使用该技巧可以控制的地址范围大大缩小。
有关house of corrosion的技巧可以参考House-of-Corrosion 一种新的堆利用技巧 - 先知社区 (aliyun.com)。
1-2-2 tcache reverse into fastbin
目前检查了对齐,所以要注意控制的地址要是0x?0结尾,否则报错。利用效果是任意地址写一个libc地址。
虽然0x?0写的是加密后的堆地址,但是0x?8会写上tcache_key,这也是可以利用的点。而且,在写上地址后,还能分配到该处。其利用过程如下:
- 分配
13个fastbin范围内的chunk,假设大小为A - 全部释放这
13个chunk - 分配
7个,把tcachebin[A]耗尽 - 把
fastbin最后一个chunk的fd修改为addr - 调用一次
malloc(A)即可触发tcache reverse into fastbin,可以分配到addr,也能给addr/addr+8处写上地址/数
1-3 smallbin
1-3-1 house of lore
很显然,house of lore依然可以使用,但是house of lore使用的时候,一方面是需要满足victim->fd->bk == victim;另一方面,需要绕过下面讲的tcache stash unlink流程。除此之外,还需要注意内存对齐的问题。
1-3-2 tcache stash unlink attack
在我之前的博客中,分析house of rust的时候总结过这个利用手法。
第一个技巧叫 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处
可以看到,和fastbin reverse into tcache的攻击方法很类似,但是得到的效果不一样。TSU可以在任意地址写libc地址,而TSU+除了可以写libc地址,还能再任意地址分配。
1-4 largebin
目前能用的largebin attack只能使用下面这个分支:
/* maintain large bins in sorted order */
if (fwd != bck)
{
/* Or with inuse bit to speed comparisons */
size |= PREV_INUSE;
/* if smaller than smallest, bypass loop below */
assert (chunk_main_arena (bck->bk));
if ((unsigned long) (size)
< (unsigned long) chunksize_nomask (bck->bk))
{
fwd = bck;
bck = bck->bk;
victim->fd_nextsize = fwd->fd;
victim->bk_nextsize = fwd->fd->bk_nextsize;
fwd->fd->bk_nextsize = victim->bk_nextsize->fd_nextsize = victim;
}
else
{
//......
}
//......
}
效果是可以任意地址写堆地址。
largebin attack往往会与其他攻击方法结合起来,因为其写地址的能力,可以修改变量,所以常常用来构造写原语。
1-4-1 house of husk
house of husk方法仍然可以利用,需要找到一个格式化字符串的场景,且打house of husk的时候,至少需要两次格式化字符串。
1-4-2 libc/ld上的变量
libc/ld的地址空间上关键变量非常多,比如_IO_list_all,pointer_guard、tcache等等。具体的方法会在相关的篇幅里面进行详细说明和补充。
1-5 IO_FILE
1-5-1 house of kiwi
在这个commit里面将__malloc_assert的实现逻辑修改了。

也就是说,在glibc-2.36及其之后,house of kiwi的利用链失效了。
而在这个commit,直接使用默认的assert,__malloc_assert被删掉了:

1-5-2 house of emma
只要_IO_cookie_jumps还在,这个方法就能继续使用。但是,由于poniter_guard处于ld的地址空间,所以某些场景是需要爆破的。
1-5-3 house of obstack
glibc-2.36的时候,_IO_obstack_jumps被去掉了,但是还有其他方法可以触发调用链。
glibc-2.37开始这个方法的调用链为:__printf_buffer_as_file_overflow -> __printf_buffer_flush -> __printf_buffer_flush_obstack->__obstack_newchunk。
1-5-4 house of apple1/2/3
apple1需要和其他技巧结合使用,可以任意地址写堆地址apple2利用的_wide_vtable缺乏校验调用函数指针apple3利用shlib_handle去绕过只指针加密调用函数指针
1-6 _rtld_global
1-6-1 house of banana
整体来看,就是hosue of banana的利用
1-6-2 利用link_map
围绕link_map有很多利用技巧,比如之前有使用格式化字符串修改掉link_map->l_addr,可以让函数解析后的地址被写入到其他地址处。而house of banana的本质也是围绕link_map做利用。
1-7 libc.got
1-7-1 libc.got in IO
比如高版本house of pig没有办法覆写hook指针,因为这些指针都被删掉了,那么可以覆写libc.got项,在IO处理函数中存在着memcpy/memmove等函数,当这些函数被调用的时候会jmp到对应的libc.got存储的地址,因此可以控制libc.got的内容来劫持RIP。
1-7-2 libc.got in malloc_printerr
此外,在malloc中的malloc_printerr和assert,都会调用到strlen的got,因此,在高版本中可劫持该函数的got,来控制RIP。
具体来看,就是在__libc_message中有调用strlen:
/* Abort with an error message. */
void
__libc_message (enum __libc_message_action action, const char *fmt, ...)
{
// ......
if (cp[0] == '%' && cp[1] == 's')
{
str = va_arg (ap, const char *);
len = strlen (str); // 这里调用了strlen
cp += 2;
}
//.....
}
}
1-8 heap_info/malloc_state
攻击堆管理中最核心的数据结构,比如有:
house of mind伪造heap_info结构体,进而控制arena- 直接打掉
thread_arena,伪造一个arena - 打掉线程的
tcache变量 - 修改
pointer_guard等
1-9 __environ
-
GLIBC_TUNABLE环境变量的设置会控制ptmalloc_init的流程,影响很多关键变量的设置,比如tcache_counts等。在这里有着设置示例Tunables (The GNU C Library)。比如export GLIBC_TUNABLES=glibc.malloc.tcache_count=2 -
有些特殊的环境变量会泄露出信息,比如
LD_SHOW_AUXV
1-10 other
这里是一些不太好归类的攻击面。有:
house of muney,一种steal heap的技巧,通过修改mmap chunk的size来达成利用exit的时候会call tls_call_list里面的函数指针,但是也要能控制pointer_guardexit的时候会调用一些锁的函数指针,某些博客中称之为exit_hook,但是在2.34之后这些hook被静态函数所代替
2-参考
[1] Tunables (The GNU C Library)
[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)
本文来自博客园,作者:LynneHuan,转载请注明原文链接:https://www.cnblogs.com/LynneHuan/p/17822172.html

浙公网安备 33010602011771号