首页
社区
课程
招聘
[原创]浅析libc2.38版本及以前tcache安全机制演进过程与绕过手法
发表于: 2024-11-9 16:02 5318

[原创]浅析libc2.38版本及以前tcache安全机制演进过程与绕过手法

2024-11-9 16:02
5318

本文重点关注tcache本身的结构、取用放入的原子操作,以及其free安全机制的演变过程,

大概分水岭在于

2.26(tcache出现)

2.28(_int_free引入key防止双重释放)

2.32(PROTECT_PTR的引入波及tcache利用)

2.34(使得key随机生成,而非tcache_perthread_struct的地址)

在这个过程中,可以看到ptmalloc力保tcache的性能,导致没有做到对堆利用的完全封堵

请见谅,本文对于tcache针对各sizebin的具体操作,将不会表现太多细节,因为如果要关注安全性,其实关注它们的底层一点的安全机制即可

首先不得不提的是,“快”,贯穿整个tcache机制之中——这决定了它的一些安全特性。

这使得刚开始2.26版本下突然引入的tcache没有任何安全检查,在后面的补丁中,也只是做了必要的、不太影响性能的安全检查。

tcache适用于非large bin大小的堆内存的管理,使用单链表,fd指针,

每当有新的内存大小被申请,其会创造一个新的入口点(entry)与其大小关联起来,以后申请和释放no large大小的块都会首先经过tcache内部指定大小的入口点的堆块遍历,查找有无合适的块,其中,这个大小通过以下方式来计算

下面根据glibc版本的演变过程作为时间轴来看看tcache安全机制的变化:

4b4K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3W2F1L8%4u0Q4x3V1k6Y4L8r3W2T1j5#2)9J5c8X3u0D9L8$3u0Q4x3V1k6J5k6h3I4W2j5i4y4W2i4K6u0r3x3W2)9J5k6e0t1$3i4K6u0r3L8h3q4K6N6r3g2J5i4K6u0r3L8h3q4D9L8r3!0U0i4K6u0r3L8h3q4D9L8r3!0U0i4K6u0W2j5H3`.`.

所有需要了解的部分都在malloc.c

画了一下图片来纵观就非常清晰了,这里可以看清tcache的内部结构是怎么样的
![图片描述] 图片描述(upload/attach/202411/869206_9ZHPKB7NGN2W72P.webp)
其中,一些限制值已经在代码上有预先的定义,当然这些个值也可以在编译阶段进行修改

tcache_perthread_struct,顾名思义,是每个线程都维护一个的tcache结构体,可以说其就是整个tcache的主干结构体
图片描述
其维护了两个数组,实际上,这两个数组的每个位置,是一一对应的关系:一个负责计数,另一个负责管理tcache,一般使用tc_idx(tcache_index)这一名称作为数组索引
而每个索引tc_idx,其实对应的是不同大小的tcache bins,比如16字节大小的各个tcaches,32字节大小的各个tcaches,64字节大小的各个tcaches,会被分门别类地摆放到各自的entries[tc_idx]之下
其分门别类的方式也比较特别,实际上tc_idx是由csize2tidx()方法生成的,其参数tbytes其实就是用户请求内存的大小
图片描述
其算法只有一句话:
图片描述
式中,
x指代需要分配的大小
MALLOC_ALIGNMENT为各chunk的大小单位,MALLOC_ALIGNMENT在64位下是16字节,在32位下是8字节
而MINSIZE在64位下是8字节,32位下是4字节

以下注释解释了MALLOC_ALIGNMENT的计算结果
图片描述
counts负责计数,计算的数目是每个索引之下的tcache bins数目
而entryies负责保存"入口",其指向某大小的首个tcache bins
值得一提的是,这所有的内容其实都是在堆上的,tcache_perthread_struct则更是在top_chunk附近的首个位置

早期tcache的tcache_entry结构如下
图片描述
tcache_entry实际上指向tcache堆空闲块的fd位置,所以next其实跟fd是一个位置,作用相同,指向该大小tcache下的下一空闲块

首先了解一下tcache的最基础的行为,取用tcache和放入tcache,这两个行为的定义直接决定了tcache的安全机制是否足够安全

这是tcache的原子行为之一:放入tcache
仅仅检查了所操作的tc_idx是否比限制值TCACHE_MAX_BINS更大,然后使用了头插法(O(1))来加入新的tcache_entry节点,这样比尾插法(O(n))更快
然后更新对应count[tc_idx]
图片描述

这是tcache的原子行为之二:取用tcache
同样,仅仅检查了所操作的tc_idx是否比限制值TCACHE_MAX_BINS更大
然后更新对应count[tc_idx]
图片描述
这里两个原子行为的问题就是,tcache太追求速度了,舍弃了一切的安全机制,不做任何检查,导致极易进行double free,或者poisoning(其实就是溢出)等攻击
实际上后期正是在这里做了一点文章,加入了安全机制

读罢早期tcache,其实最大的问题就是没有任何检查给攻击者造成麻烦,但是矛盾就是,这本身是一种强调速度的结构,如果加入安全机制比较消耗性能,那就失去了这个结构的存在意义。所以,现今tcache引入了一种key检验的机制,目的是在尽可能低的性能开销情况下,给没有获得信息泄露能力的攻击者造成一定麻烦
405K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6T1L8h3W2F1L8%4u0Q4x3V1k6Y4L8r3W2T1j5#2)9J5c8X3u0D9L8$3u0Q4x3V1k6J5k6h3I4W2j5i4y4W2i4K6u0r3x3W2)9J5k6e0t1^5i4K6u0r3L8h3q4K6N6r3g2J5i4K6u0r3L8h3q4D9L8r3!0U0i4K6u0r3L8h3q4D9L8r3!0U0i4K6u0W2j5H3`.`.
总的来说,中期直到现今的结构大体上是差不多的,示意图如下
图片描述

图片描述

图片描述
更换了counts的类型,从char数组变为无符号整形,也许是预防了一手类型混淆,而且本来就应该这样写吧(?)


[招生]系统0day安全-IOT设备漏洞挖掘(第6期)!

最后于 2天前 被是气球呀编辑 ,原因: 修正错误问题
收藏
免费 3
支持
分享
最新回复 (3)
雪    币: 65
活跃值: (507)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
2
1024
2024-12-20 10:17
0
雪    币: 5012
活跃值: (19185)
能力值: ( LV13,RANK:317 )
在线值:
发帖
回帖
粉丝
3
文章里讲csize2tidx计算的宏展开代码,说“MINSIZE在64位下是8字节,32位下是4字节”,我算了下这样说是错的,MINSIZE在64位下是0x20字节,32位下是0x10字节。比如在64位下的计算过程是这样的:
MALLOC_ALIGNMENT => 0x10
MALLOC_ALIGN_MASK => (MALLOC_ALIGNMENT - 1) => 0xf
MIN_CHUNK_SIZE => (offsetof(struct malloc_chunk, fd_nextsize)) => 0x20
MINSIZE => (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)) => 0x20
2025-1-4 11:29
1
雪    币: 4241
活跃值: (1314)
能力值: ( LV11,RANK:190 )
在线值:
发帖
回帖
粉丝
4
0x指纹 文章里讲csize2tidx计算的宏展开代码,说“MINSIZE在64位下是8字节,32位下是4字节”,我算了下这样说是错的,MINSIZE在64位下是0x20字节,32位下是0x10字节。比如在64 ...
噢噢感谢师傅指出问题,回看了一下相关代码,看来我当时粗心大意了哈哈,没有看到代码里面对于 MINSIZE、MIN_CHUNK_SIZE 、MALLOC_ALIGNMENT 、MALLOC_ALIGN_MASK 的宏定义,看着那个计算示例没有试多几次就下结论了,可能已经误导别人了QwQ,
很细心的师傅哈哈,感谢指正~♪(・ω・)ノ
2天前
0
游客
登录 | 注册 方可回帖
返回