原文:https://www.zerodayinitiative.com/blog/2018/10/18/cve-2018-8460-exposing-a-double-free-in-internet-explorer-for-code-execution

简介


最近,微软发布了Internet Explorer(IE)的更新补丁,修复了其中的CVE-2018-8460漏洞。在所有受支持的Windows版本中,IE 11都存在该漏洞。按照微软的说法,该漏洞是一种普通的“内存破坏”型漏洞,但实际上,该漏洞是由CSS机制中的Double Free漏洞所致,攻击者可以利用该漏洞发动远程代码执行攻击。因此,这是一个非常值得进一步研究的案例。

漏洞详解


实际上,该漏洞位于处理对属性cssText进行写操作的代码中。开发人员可以通过这个DOM属性来获取或设置CSS属性字符串,并可以通过一次操作检索或替换样式对象的全部内容。

在使用cssText属性为DOM中的元素指定样式过程中,IE将根据需要触发DOMAttributeModified事件。当攻击者处理DOMAttributeModified事件并将其用作重新输入cssText属性设置器的机会时,麻烦就开始了。处理对cssText执行写入操作的代码路径不是为了这样的重新输入而编写的。具体来说,它没有预料到在执行过程中会通过重新输入对样式对象的内容进行额外的修改。因此,当cssText的(外部)调用完成时,元素的CStyleAttrArray处于损坏状态,其中两个元素中存放的指针都指向内存中相同得CSS值字符串:

实际上,CStyleAttrArray中的字符串指针的重复现象是不应该出现的。因为,当这个CStyleAttrArray被销毁时,析构函数会对每个指针都调用一次HeapFree函数。由于列表中有一个重复的指针,因此,会使用同一个指针值对HeapFree函数调用两次,这样,就会出现Double Free漏洞。请注意,内存的分配是在Windows进程的堆上进行的,因此,MemGC在这里并不适用。

下面是用于触发Double Free漏洞的PoC代码:

漏洞利用


Double Free漏洞的利用方法非常有难度。这是因为当前的Windows堆管理器都进行了相应的安全加固处理,可以自动检测到HeapFree函数的参数是否为已经释放的堆块,如果是的话,会通过立即关闭进程来防止进一步的内存破坏行为。

然而,利用Double Free漏洞的方法是的确是存在的。准确来说,如果第一次调用HeapFree函数和第二次调用HeapFree函数的间隙,能够设法在被释放地址处重新分配内存的话,则可以完全绕过上述缓解措施。在这种情况下,当第二次调用HeapFree函数时,堆管理器会将相应的内存块视为已分配的内存块,而非已释放的内存块。因此,堆管理器看不出任何问题。然后,它将着手释放该段内存,从而可能进一步导致其他可利用的条件。

利用Double Free漏洞的策略总结如下:

  1. 触发第一个HeapFree调用,释放位于地址A的对象X。
  2. 引发新的内存分配操作,为新对象Y分配一段内存,让它位于地址A处。实际上,有时可能需要喷射许多对象,从而确保其中有一个对象位于A处。
  3. 触发第二个HeapFree调用。这时将释放对象Y,因为Y位于A处。但是,对于Y的释放可能为时过早,因为在其他地方仍然可能还有尚未完成使命的指针还在指向它。
  4. 触发新的内存分配操作,为第三个对象分配内容,仍然让它位于地址A处。我们将这个对象称为Z(如上所述,仍可能需要进行喷射操作)。
  5. 触发用到对象Y的代码。这些代码的操作对象实际上是对象Z,因为当前位于地址A处的恰好就是对象Z。至此,已经具备了可利用的条件。

如果可以实现这些步骤,结果将是毁灭性的,因为攻击者对对象Y和Z的类型有很大的控制权。最有可能的情况是,这可以用于信息泄露和控制流劫持,从而发动完整的RCE攻击,而不需要与任何其他漏洞相配合。

在上面的步骤2中,在两次释放操作之间进行可靠的内存分配可能是最具挑战性的。就本例来说,我们该如何做到这一点呢?让我们看看CAttrArray::Free中的代码,其中出现了两次内存释放操作:

简而言之,这里是一个循环结构。在每次迭代,它都会将第一个数组元素中的数据复制到临时结构(v13)中,并调用memmove将其余元素向前移动一个位置(即数组元素1现在变成元素0,其他依此类推),然后调用ProcessHeapFree处理在已删除的数组元素中找到的指针。之后,继续循环,直到数组为空为止。请注意,循环结构中还包含一些其他代码(此处未显示),这些代码用于响应其他类型的数组元素。据推测,正是由于其他代码的复杂性的缘故,所以才有必要使用这种低效的O ( n^2)算法来清除数组。

由于存放重复指针的两个数组元素在数组中彼此相邻,因此,在两次调用ProcessHeapFree的间隙所能够执行的代码非常少。那么,我们如何才能在两次释放操作之间完成内存分配呢?

请注意,在对ProcessHeapFree的连续调用间隙,还将调用memmove。假如我们可以将这个移动操作变成一个漫长的过程的话,就会在两次调用ProcessHeapFree间隙引入一段时间延迟。如果我们使用第二个线程在第一个线程清除数组的同时快速连续调用HeapAlloc的话,我们就可以赢得竞争,并在两次调用ProcessHeapFree的间隙完成所需的内存分配工作。

只有当攻击者能够控制memmove的大小时间使其变得非常大时,这种方法才能奏效。当我们研究这个漏洞时,结果让人大吃一惊:要想增加memmove的大小,只需向元素添加额外的自定义样式属性,从而增加CStyleAttrArray的大小即可。例如,我们可以添加名为-ms-xyz1、-ms-xyz2之类的样式。这一点在上面的PoC中就可以看出来。但是,我们发现,对于IE来说,样式属性的数量上限为100万个。由于每个属性在CStyleAttrArray中都是由16字节元素来表示的,因此,最大memmove将只有16MB。这并不能拖延多少时间。我们认为,仍然可以通过引入一些额外的技巧来利用这种方法,例如,使用大量的内存分配线程来提高赢得竞争条件的概率。简而言之,这种技术在理论上是说得通的,但是,在实践中能否用于利用这个特定的漏洞还有待观察。

在这种情况下,更有希望的方法可能是找到一种修改PoC的方法,使得CStyleAttrArray中的重复指针不在相邻元素中,以便在两次调用ProcessHeapFree间隙可以运行更长的代码。最好能够为脚本添加生成回调函数的代码,这样我们就可以在无需赢得竞争的情况下完成内存分配了。

小结


读者可能已经注意到了,在服务器上,该漏洞的威胁程度为“中等”,但对于客户端来说,其威胁程度为“高危”。这是因为,在服务器上,IE在默认情况下会以增强安全配置(ESC "ESC"))下运行。如果禁用该功能的话,该漏洞对于服务器的威胁程度也会上升到高危级别。在漏洞利用指数方面,微软为这个漏洞的打分为1分,这意味着在接下来的30天内,可能会出现针对该漏洞的攻击。该漏洞是由ca0nguyen(ZDI-18-1137)提交给ZDI计划的。在调查这个案例时,我在IE中发现了一个OOB写入漏洞,这可能是微软将这个漏洞归类为“内存破坏”的一个原因。需要说明的是,这两个漏洞都是由这个CVE的补丁修复的。如果读者还没有安装对应的补丁程序的话,那可要抓紧行动了。

与往常一样,我可以在Twitter上找到@HexKitchen,并跟随团队获取最新的漏洞利用技术和安全补丁。

点击收藏 | 0 关注 | 1
  • 动动手指,沙发就是你的了!
登录 后跟帖