Sec Hotspot 首页  排行榜  收藏本站  技术博客  RSS
统计信息
已收录文章数量:12963 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
本周热门文章
红队渗透手册之弹药篇   2020.08.08 17:12:23  288
Chrome浏览器的实用插件推荐   2020.08.04 20:01:02  99
记录一次“梅花三弄”的渗透之旅   2020.08.03 20:00:35  77
用这个网站一查,才知道自己被卖了   2020.08.04 20:06:35  72
WMCTF-WriteUp   2020.08.06 08:00:37  65
每日攻防资讯简报[Aug.3th]   2020.08.03 20:00:04  55
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
分析对malloc单链表的新保护机制
本文来自公众号:安全客   2020.06.15 18:00:25


0x00 绪论

2020年5月21日,Checkpoint Research发表了一篇文章,详细说明了对Malloc多个版本进行的一个安全修补及其影响,包括GLibC中所使用的Malloc。这个补丁试图修复用作bin数据结构的单链表存在的不安全因素。本文中,我将深入分析该安全修补的工作原理,及其对二进制漏洞利用的影响。我们将特别关注GLibC的Malloc中加入的修补,不过这种缓解措施的原理可以直接用于其他版本的Malloc(但具体实现要有所变动)。

0x01 GLibC Malloc

要理解本文其余部分,先要初步认识GLibC Malloc里的数据结构。本文只讲其中两种主要的数据结构:chunks和bins。

Chunks

Chunks是用户主要打交道的对象。chunk有两种主要状态:分配和空闲。下图展示一个空闲chunk:
第一个字段是前一个chunk的大小(prev_size),只在前面的chunk空闲时才用这个字段。第二个字段是chunk大小,包括chunk的数据部分大小和关于本chunk的一些元数据(大小字段的头3个位)。
在空闲chunk中,第三个字段用于存储指向其他空闲chunk的指针,这个字段称为前驱指针,或者叫Fd字段。第四个字段和第三个差不多,存的是后继指针,或者叫bk。Fd和bk指针在chunk被分配时是不用的,而是作为chunk的数据部分。对于本文,要知道的就是这些了。关于GLibC的细节,请见 Sploitfun 的出色文章。

Bins

GLibC中,bins是目前空闲的chunk的一个列表。所有bins都以某种链表结构存在。有几种不同的bins,其chunk大小和速度不同。在本文中,我们只需知道两种bins:fastbins和tcache。
tcache和fastbins都是空闲chunk的单链表,这表示Fd指针(chunk的第三个字段)指向bin中下一个空闲chunk。这两种bins都不使用bk指针。Fastbins和tcache还有很多差异,比如速度、安全检查等,但是在本文中,所需知道的就是这些。

0x02 单链表的安全问题

最早的 unlink 利用发布之初,人们在GLibC中加入了许多安全性和完整性检查,防止chunk破坏。其他bins(unsorted, small和large)用的是双链表,可以加入许多检查来确保chunk或bin没被破坏。但是,对于单链表,这些检查无法实现,这很遗憾,但是为什么不行呢?
一旦拥有覆盖单链表Fd指针的能力,就可以将该指针指向内存中的任意位置。用户调用Malloc时,这个伪造的chunk(包含我们写入的Fd指针地址)就会返回给用户。到此,因为指针指向任意位置,我们就可以写入那个地方。利用这个原语,就可以用多种方法达到代码执行,例如覆盖函数指针,或者在此之上创建其他原语。

0x03 安全更新

如上所述,Malloc中的单链表Fd指针容易被利用,对安全造成极大威胁。因此,Checkpoint Research决定对单链表加入新的安全保障。加入的措施可以归结为二:强制chunk对齐和指针修饰(pointer mangling)。以下讨论这二者。

强制chunk对齐

Malloc只分配以8字节(32位)或16字节(64位)为单位的chunks。因此,chunks只应以0x8(仅对于32位)或0x0结尾。新加入的安全检查验证给定chunk对齐到了预期的位置。下图展示了有效的chunk所能指向的位置。
这样一个简单的完整性检查限制了伪造chunk创建的位置,32位下减少了7个位置,64位下减少了15个位置。后文将会对其意义加以讨论。

指针修饰

这个修补大致来说就是fastbin和tcache的Fd指针加入了混淆,阻止没有地址泄露的利用和相对覆盖这两种方法。
指针修饰的公式如下:
| New_Ptr = (L >> 12) XOR P | ,其中L是存储位置,P是fastbin/tcache的Fd指针。
Fd指针和存储位置被ASLR随机化,New_ptr因此也随机化了。
简单来说,这公式就是把指针的存储位置和Fd指针自身相异或。异或前,存储位置先被右移12位,这是因为存储位置和Fd指针的低12位是可以确定的。因此,想要对Fd指针的头12位随机化就必须移位。不然,相对覆盖仍然可以轻而易举地利用,因为末12位总是一样的。
解修饰(demangling)指针和上面的公式一模一样。修饰后的指针为P,存储位置L不变。异或后,原来的Fd指针就回来了。
这里对指针修饰做了概述。如果想了解更多,请见GLibC修补的作者的文章。

0x04 安全意义

以上对Malloc进行的两点改动对二进制漏洞利用的意义有轻有重,以下详细讨论其意义。

0x05 强制chunk对齐的意义

虽然改动看起来很少,但对Malloc来说却是巨大的一改。对于64位二进制文件,强制chunk对齐将把伪造chunk的位置限制为每16个地址中只有1个可用。这使得漏洞利用的难度增加了,尤其是在用户几乎不能控制程序中的值的那些二进制中。虽然利用漏洞还远远没到不可能的地步,但是每增加一点约束,利用就更复杂、更困难一点。在将来,要使伪造chunk可用,所有fastbin和tcache chunks都得进行对齐。

对__malloc_hook的fastbin attack

需要考虑的另一种情形是经典的函数指针(__malloc_hook)覆盖攻击,通过此攻击可以达到代码执行。从fastbin分配chunk时,chunk大小会经过验证,确保和fastbin大小相同。如果验证失败,Malloc就会中止。为了绕过这个安全检查,__malloc_hook附近的一个地址(__memalign_hook)以未对齐的方式被使用,来得到有效的chunk大小0x7F。这种攻击的例子可以看 这里 。加入对齐约束之后,这种攻击对fastbins就不再可行了。

0x06 指针修饰的意义

在Checkpoint Research的文章中,概述了指针修饰的主要目的:阻止部分覆盖和完全覆盖(在没有内存地址泄漏的情况下)。下面将予以讨论。

再见了,字节相对覆盖

在此修补出现之前,堆chunk指针的部分覆盖非常普遍。部分覆盖顾名思义就是:覆盖指针的一部分。
例如,把指针从0x80000080更改为0x80000040,我们就可以将堆chunk指向完全不同的位置。这个简单的技巧将使我们能够将chunk指向不同的位置,而无需知道堆上的实际地址!利用部分覆盖的另一个例子可以在不含地址泄露的 House of Roman 中看到,其中在3个不同的地方使用相对覆盖来实现远程代码执行。
现在,在没有堆泄漏的情况下,相对覆盖就需要暴力穷举才能完成了。在需要某个特定字节的情况下,该漏洞利用仅能以1/256的机会有效,即暴力穷举所有可能字节。由于增加了一层非确定性的计算,漏洞利用比以前的Malloc版本更难了。

在非堆位置进行tcache bin/fastbin攻击变得更困难

常见的一种攻击是将Fd指针设置为某函数指针的位置。这之所以可行,是因为用该函数指针分配chunk时,该指针可以被其他东西(例如system或one_gadget)覆盖。如前所述,__malloc_hook是一个很好的目标。覆盖函数指针攻击的一个例子如下:
1.泄露LibC地址,确定__malloc_hook的位置
2.将一个chunk释放到tcache bin中
3.覆盖tcache chunk的Fd指针,指向__malloc_hook
4.分配指向__malloc_hook的chunk
5.将__malloc_hook的值设为system或一个one_gadget,来达到代码执行
上述的攻击非常常见,只需一个LibC地址泄露即可进行。加入指针修饰后,这种攻击不再有效。想伪造指向GLibC的堆指针,必须在第3步覆盖指针前先修饰这个指针,这就大大增加了漏洞利用的难度。
此外,有了指针修饰,想在栈、.bss节或者在PLT/GOT里创建伪造chunk,就必须先泄露堆。这和上面所解释的LibC的情况一样,只是拓展到了其他非堆的位置。总的来说,这使得堆利用难度大大增加了。

泄露堆地址

一开始了解到指针修饰时,我以为fastbin和tcache指针泄露都无效了。但是,捣鼓了一会儿之后,我发现一种解修饰指针的方法,可以在堆泄露前进行。

0x07 堆泄露前解修饰(demangling)指针

回忆一下修饰和解修饰指针的算法: | New_Ptr = (L >> 12) XOR P | ,其中L是存储位置,P是fastbin/tcache的Fd指针。
存储位置和Fd指针很可能是同样位数,而且高12位相同。当位置被移位之后,会进行如下计算: (P的最左边12位) XOR (0) 。这泄露了指针的高12位。
比如,以存储位置0x987654987和Fd指针0x987654321为例。在下图中,存储位置已经被移位了。
从式中容易看出我们为什么泄露了指针的最左12位,因为这一部分是和0x0做的异或。
还可以观察到一件关键的事:Fd指针的set 1和存储位置的set 2是一样的!为什么这件事很关键呢?对于异或操作,如果两个值都已知,那第三个值就可以还原出来,因为异或是可逆的。现在我们知道输出和存储位置,就可以计算出Fd指针的相应位!只需将两个已知值异或即可。如下所示:

完整算法

将上述过程进行推广,就可以还原存储地址和经过修饰的指针的Fd指针。我们只做了一个假设:Fd指针和存储位置的高12位是相同的。上述过程可以继续直到指针末尾。算法如下:
1.首先,我们知道堆的头12位。由于移位,对第1轮迭代来说,存储位置指针的一些位就白送给我们了。后续的迭代则可以利用步骤3的输出。
2.其次,用这已知的位和修饰后指针的次高12位异或。输出的是Fd指针中的12位,见上图。
3.最后,如果Fd指针和存储位置指针的位不同,那么下一轮我们可以用两指针间的相对偏移把位加减回来,给出一组新的已知位值。
4.重复上述操作,直到走到修饰后指针的末尾。步骤1的已知位就是上一轮步骤3的输出。
结束时,输出就包含Fd指针和存储地址了。
注意:存储位置的末12位无法还原,因为它们在移位中丢失了。不过,因为这些位是确定性的,所以算法结束后可以加回来。要修饰Fd指针的话,末12位是没用的,移位会把它们移走。

修饰指针

用上述的算法,可以实时解修饰指针!不只是理论上可行,而且实际可用!我写了脚本来进行修饰和解修饰(包括无地址泄露的解修饰)。
脚本工具放在了 mdulin2/mangle ,可以去玩玩。repo里还有一份LibC和加载器(包含新的Malloc源码)、修改过的Malloc Playground,通过pwntools实现了友好的Python界面,还有一些如何绕过修饰缓解措施的例子。不仅有命令行界面,还有可导入的接口。我希望类似的东西可以默认加入到GEF和pwndbg中。

0x08 总结

总而言之,二进制漏洞利用还远远没到结束的那天。但是,这对于Malloc的安全性来说是一次大的改进。总体来看,有下列影响:
1.伪造chunks都必须对齐(减少了伪造chunk所能位于的位置数目)
2.相对覆盖现在需要大量的暴力穷举(很可能需要穷举一整个字节)才能进行
3.伪造Fd指针需要和存储位置进行修饰,这需要堆泄露
4.对Fd指针的堆泄露现在需要解修饰才能进行
5.非堆位置的伪造chunks现在需要进行堆泄露
希望你喜欢这篇文章,并且学到了Malloc的有趣知识。感谢 @seiranib 发表前审阅本文。如有问题或意见,欢迎联系我。Maxwell “ꓘ” Dulin致意。

0x09 尾注

文中进行了几处简化,目的是让普通人看懂,因此省略了一些内容,或者没有完整进行解释。下面是简化的地方:
  • 修饰算法中的数12其实是常量PAGE_SHIFT,但在通常情况下,这个常量就是12。

  • prev_size字段仅在下面的chunk空闲且不在tcache和fastbin中时才使用。这是因为tcache和fastbin不会对chunk的in_use位进行复位,就算chunk是空闲的。

  • 理论上,如果对分配进行精心控制,相对字节的暴力穷举可以做到1/16的正确率。但是需要其他复杂的技巧,超出了本文的范围。

  • Fastbin和tcache bins有许多差异,本文未进行解释,因为没有必要。想知道bins的更多信息的话,请点击下方阅读原文见Azeria Labs的这篇 好文

  • 另一篇文章 的研究者进行了类似的研究。本文研究是独立进行的,得到了类似的结论,其他研究者只是先于本文发表了他们的结果而已。




戳“阅读原文”查看更多内容