ZDI年度五大漏洞之——利用内存垃圾回收MemGC的盲点

2019-04-04 约 2877 字 预计阅读 6 分钟

声明:本文 【ZDI年度五大漏洞之——利用内存垃圾回收MemGC的盲点】 由作者 Agostop 于 2018-12-24 10:00:00 首发 先知社区 曾经 浏览数 1455 次

感谢 Agostop 的辛苦付出!

原文链接:https://www.zerodayinitiative.com/blog/2018/12/17/seeing-double-exploiting-a-blind-spot-in-memgc

这是我们评选的2018年五大有趣案例的第一个。这些评选出来的bug都具有一些独特的元素,使得其与今年发布的大约1400条报告不同。我们首先来看Pwn2Own冠军的一个案例,以看似不可能的方式来攻击微软Edge浏览器。

在2018年Pwn2Own大会上,Richard Zhu(fluorescence)成功攻陷多个目标,获得了世界破解大师(Master of PWN)的称号。他攻陷的目标之一是Microsoft Edge,使用的利用链包括两个Use-After-Free (UAF)漏洞。其中一个UAF漏洞十分引人注目,以至于被列为我们今年的五大漏洞之一,将会在本系列博客中对此进行详细介绍。这个漏洞编号是CVE-2018-8179

我们来深入研究这个漏洞的一些PoC代码,看看是什么让它如此惊人:

图片显示了poc代码和一些指示操作顺序的注释。主要操作是从步骤3中setRemoteCandidates的调用开始的,这个API需要传入一个JavaScript数组。如图中所示,在遍历该数组过程中出现了问题。在访问arr2[1]时,执行了getter方法(步骤4-5),该脚本能够释放arr2[0]最初引用对象所使用的内存。然后脚本回收内存并用攻击者指定的数据覆写内存(步骤5)。当setremotecandidate继续执行并试图访问arr2[0]最初引用的对象时,就会发生崩溃。

为了实现这幅图中的内容,我们需要更多地了解setremotecandidate处理其参数时发生的事情,它做了如下操作:

  1. 创建一个名为CModernArray<>的内部数组结构。
  2. 遍历arr2。对于每个元素,获取一个指向该元素的指针,并将其添加到CModernArray<>中。
  3. 迭代CModernArray<>,依次处理每个JavaScript对象。

CModernArray<>是在edgehtml.dll中定义的一个c++类。至关重要的是,它将数据存储在从MemGC堆分配的缓冲区中。概括总结下POC的操作:edgehtml.dll在arr2上迭代。在此过程中,它首先将arr2[0]复制到属于CModernArray<>的memgc控制的缓冲区,然后,当访问arr2[1]时,已经从arr2[0]复制的JavaScript对象将被释放并回收,但是CModernArray<>中仍然存在一个未清空的指针。当从CModernArray<>的索引0检索该指针时就会产生崩溃。

把所有这些信息汇总在一起后,我们现在可以理解这里存在一个漏洞是多么不可思议。在整个过程中,所有涉及的对象(JavaScript数组等)都被分配到MemGC堆上。此外,所有指向这些对象的指针都存储在memgc分配的数组和缓冲区中。那么,UAF是如何产生的呢?这类UAF正是MemGC所应该预防的。MemGC被设计成能够识别MemGC堆中当前存在的所有指针,这样当指针仍然存在时,就不能释放MemGC分配的内存。在MemGC堆分配中,MemGC应该是无所不知、无所不见的。那么,为什么MemGC没有检测到CModernArray<>中存在一个未清空指针这一事实呢?

为什么会这样呢?

我现在要告诉你一个可怕的秘密。

并没有这样一个无所不知的“MemGC堆”

实际上有两个MemGC堆,它们对彼此的分配是不可见的。

这两个MemGC堆如下所示,其中一个MemGC堆会在浏览器的JavaScript引擎Chakra中内部使用,所有基于堆的JavaScript对象以及许多内部Chakra数据结构都存储在这个堆上,我们称之为“Chakra堆”。另一个MemGC堆是由chakra.dll提供给外部使用者使用的,尤其是给edgehtml.dll用。我们将其称为“DOM堆”,它取代了前一代的,由Internet Explorer在2014年7月首次引入的内存保护机制。DOM堆用于所有DOM对象,以及从edgehtml.dll执行的大多数其他堆分配。

这两个堆共享一个实现,但是它们由chakra!Memory::Recycler类的两个不同实例表示。当垃圾回收发生时,在“标记”阶段,回收程序会扫描所有存活的堆分配,以及堆栈和处理器寄存器,寻找指向其他堆分配的指针,以便也可以将这些指针标记为活动的。由于并不是内存中的每个值都是真正的指针值,所以这次扫描会得到一些无关的结果。为了过滤掉这些无关结果,Recycler将自动拒绝属于堆分配范围之外的任何值。但是这种决定只是基于某个堆的,一个Recycler实例并不知道其他Recycler实例可能正在使用的地址区域。此外,它不能在属于其他Recycler实例的MemGC分配上放置“标记”。

顺便提一下,甚至有存在两个以上的MemGC堆。JavaScript执行的每个线程都有自己的Chakra堆实例。通常这不会造成问题,因为JavaScript对象不会与创建它的线程以外的任何线程上的代码交互。

现在我们可以理解在运行图中poc代码时发生了什么,在步骤5(见图)中,内存压力迫使Chakra堆进行垃圾收集。这是由与Chakra堆相关的Recycler实例执行的。在扫描堆栈时,recycler会遇到指向CModernArray<>缓冲区的指针,然而,它会立即筛选掉这个指针,因为CModernArray<>缓冲区已经分配到DOM堆上,而不是Chakra堆上。因此,Chakra堆的Recycler从不扫描CModernArray<>缓冲区的内容,导致它会筛掉其中所包含的指向Chakra堆分配内存的未清空指针。

总结

我们已经证明,“MemGC是无所不知的”这种概念是一种误解。虽然MemGC作为一种缓解措施非常成功,但它并非完全没有缺点。

这对于考虑如何打补丁也是有指导意义的.现在,在将每个对象添加到CModernArray<>之前,edgehtml调用chakra::JsVarAddRef来显式地将对象固定在内存中。至于edgehtml!ORTC::UnpackArrayObjectVar这种情况,MemGC陷入了困境,edgehtml中的代码必须求助于手动对象生命周期管理。

你可以关注我的Twitter@HexKitchen,或者关注我们的团队以了解最新的漏洞利用技术和安全补丁。请继续关注下一个年度五大漏洞相关博客,它将于明天发布。

关键词:[‘技术文章’, ‘翻译文章’]


author

旭达网络

旭达网络技术博客,曾记录各种技术问题,一贴搞定.
本文采用知识共享署名 4.0 国际许可协议进行许可。

We notice you're using an adblocker. If you like our webite please keep us running by whitelisting this site in your ad blocker. We’re serving quality, related ads only. Thank you!

I've whitelisted your website.

Not now