2020年1月17日,微软发布了针对IE远程代码执行漏洞(CVE-2020-0674)的Security Advisory(ADV200001),并指出该0day漏洞已经被运用于针对性攻击。目前,微软已经发布相关补丁进行修复。

该漏洞影响组件为jscript.dll,该动态链接库是微软Internet Explorer浏览器的Javascript引擎之一,其中IE8及以下使用jscript.dll,IE9及以上默认使用jscript9.dll,但网页可以通过<script>标签指定在IE8兼容性模式下加载jscript.dll,因此IE9、IE10、IE11都受到此漏洞影响。从操作系统范围来看,本漏洞影响范围横跨Windows 7至Windows 10中所有的个人操作系统和服务器操作系统。

该漏洞是一个Use-After-Free漏洞,攻击样本使用UAF达成类型混淆,进而获取全局内存读写能力并绕过ASLR等漏洞利用缓解技术,并从指定ip地址请求下一步攻击载荷,最终达到远程代码执行。

启明星辰ADLab安全研究员根据反病毒厂商捕获到的样本对此漏洞进行了分析,发现漏洞CVE-2020-0674其实与CVE-2019-1429从漏洞原理上是同一个漏洞,但触发漏洞的样本截然不同,两次推出的补丁也不完全相同。

应对措施

使用Windows更新和补丁修复此漏洞。

禁用jscript.dll,Security Advisory(ADV200001)中已经给出:

漏洞和补丁分析

PART1

在开启页堆的IE浏览器中调试,崩溃现场如下:

根据栈回溯可以对应到html样本的typeof调用。在样本中,经过复杂的引用操作,在arr3中,前一部分元素应该为undefined,后一部分元素应为RegExp对象,但使用typeof访问某元素时报错为“已释放的页堆空间”,可以看出这是一个由垃圾回收机制引起的问题。在用户默认设置下,即未开启页堆时,arr3中的某一个元素i会导致arr3[i]) === "number"成立,此时即引发类型混淆。

IE jscript的垃圾回收(Garbage Collect, GC)基于Mark-Sweep算法,即从定义为“根”的数据结构开始,寻找其所有引用到的对象标记为正在使用,而没有在标记的对象被当作不再使用,其内存空间将在垃圾回收过程中被释放。因此从崩溃现场看,本漏洞的成因是Mark-Sweep的标记过程出现了问题,也就是对象之间的引用出现了问题。

补丁分析的结论支持了上述猜测。安装补丁后,对新旧jscript.dll进行bin diff,可以看到垃圾回收算法在多个对象的标记过程(Scavenge)着重处理了一个值为0x400C的特殊情况,以NameList对象为例:

根据逆向分析和文档,这个枚举类型的值是VARIANT->VarType域。其中,0x400C代表该对象是一个指针类型的对象,指向另一个VARIANT,其指针域位于offset 8的位置,也即*((_DOWORD *)i + 2)。可以看到,此处的修补是取出指针值,传递给VAR::Scavenge函数。而VAR::Scavenge再次对0x400C的枚举型变量添加了特殊处理:

VAR::Scavenge函数对传入对象迭代地解引用,直到获得非指针的对象,也即若干层指针的最终指向,将其传入GcContext::ScavengeVar。GcContext::ScavengeVar函数逻辑较为简单,该函数通过与0xF7FF的与操作对传入对象进行标记,该与操作是将第12位清零。

经过测试,CVE-2019-1429与CVE-2020-0674的样本在各个“未修复”和“已修复”版本中表现完全一致。其UAF的对象的标记过程确实经由NameList::ScavengeCore,在CVE-2019-1429中是Array索引的Object对象,在CVE-2020-0674中是Array索引的RegExp对象,NameList::ScavengeCore决定了其是否被标记。

因此对于本漏洞的成因得出结论:在Mark-Sweep标记算法中,遇到指针类型的对象时应该解引用并标记对应对象;本例中,缺乏解引用的过程导致了漏洞的产生。

PART2

进一步分析可以发现,针对CVE-2019-1429和CVE-2020-0674微软先后推出了两个patch,以Windows 10 Version 1903 for 32-bit Systems为例,分别是KB4524570和KB4532693,但最终都升级到后者:

KB4524570和KB4532693都包含了对上述漏洞核心原理的修复,其中前者对jscript.dll有较大改动,而后者改动则简洁很多。KB4532693还包含另一个改进,使用“冗余容灾”的思路提供了另一处加强;此处加强位于Javascript引擎中call和dispatch的基础设施中,而不是对各种对象逐个补救。

KB4532693对jscript.dll中的ScrFncObj::Call函数进行了重新组织,对于CallWithFrameOnStack和CallWithFrameOnHeap(自定义名称)这两种情况,用ScrFncObj::PerformCall统一。在ScrFncObj::PerformCall中,把调用使用的函数参数加入垃圾回收的“根”中:

经过验证,在PerformCall的加固下,即使NameList等对象出现问题,在函数调用中作为参数的对象仍然被正确标记,不会触发漏洞。因而虽然两个补丁都可以完全抵御两个CVE的exploit,仍可以认为KB4532693是比KB4524570稍微高明一点的修补。

PART3

除此之外,两个补丁虽然能够在默认配置下抵御上述漏洞,对应jscript.dll仍然有一个称为LegacyGC的兼容项,已修补代码中仍然根据GcContext::IsLegacyGCEnabled()的函数查询结果来判断检查是否介入。根据逆向分析可知,该函数查询一个注册表项:

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Policies\ee1ca8aa-4402-4da1-bbe2-69a09c483a56

在此项为1时意为“兼容使用老的GC机制”,将使KB4532693中的加强失效,对于KB4524570则会完全失效。因此该注册表项的内容也涉及IE浏览器的安全性,需要予以注意。

参考链接:

1.https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/ADV200001

2.https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1429

3.https://www.virustotal.com

声明:本文来自ADLab,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。