0x0简介
libc2.29的题目又与2.27的机制有所不同这里总结下
0x1 新增机制
tcache是glibc-2.26引入的一种新技术,目的是提升堆管理的性能,早期的libc对tcache基本没任何防护,简直到了为所欲为的地步,一不检查double free,二不检查size大小,使用起来比fastbins还要简单。
查看glibc-2.29 malloc.c的源码,tcache_entry结构体增加了一个新指针key放在bk的位置,用于检测double free。
1 | typedef struct tcache_entry |
在之前的版本,要填满tcache非常简单粗暴,如果程序不清空指针,可以由头到尾free同一个chunk,直接把tcache填满,在2.29下这个方法不再适用。下面继续看一下tcache_put和tcache_get部分的源码,看看这个新指针起到如何的作用。
1 | /* Caller must ensure that we know tc_idx is valid and there's room |
当一个属于tcache大小的chunk被free掉时,会调用tcache_put,e->key被写入tcache_perthread_struct的地址,也就是heap开头的位置。而当程序从tcache取出chunk时,会将e->key重新清空。
然后再看一下_int_free中tcache部分如何进行double free检测。
1 | static void |
首先_int_free会检查chunk的key是否为tcache_perthread_struct地址,然后会遍历tcache,检查此chunk是否已经在tcache中,如有则触发malloc_printerr报错free(): double free detected in tcache 2。
简单总结一下,2.29下tcache触发double free报错的条件为:
1 | e-key == &tcache_perthread_struct && chunk in tcachebin[chunk_idx] |
新增保护主要还是用到e->key这个属性,因此绕过想绕过检测进行double free,这里也是入手点。绕过思路有以下两个:
- 如果有UAF漏洞或堆溢出,可以修改e->key为空,或者其他非tcache_perthread_struct的地址。这样可以直接绕过_int_free里面第一个if判断。不过如果UAF或堆溢出能直接修改chunk的fd的话,根本就不需要用到double free了。
- 利用堆溢出,修改chunk的size,最差的情况至少要做到off by null。留意到_int_free里面判断当前chunk是否已存在tcache的地方,它是根据chunk的大小去查指定的tcache链,由于我们修改了chunk的size,查找tcache链时并不会找到该chunk,满足free的条件。虽然double free的chunk不在同一个tcache链中,不过不影响我们使用tcache poisoning进行攻击。
0x3 i春秋新春抗疫 Document(kn0ck)
很明显的一个uaf,很好的利用了tcache的特性
思路就是先填满tcache,然后再泄露,利用小块的0x8内的内容写到free_hook
环境用的是现成的pwndocker,然后libc是用的glibc-all-in-one中下载的
1 | from pwn import * |