EdgeHTML是Windows 10中引入的下一代网页浏览器(代号Spartan)所用的新渲染引擎,因其使用广泛 - 涵盖移动端到PC端,故理解它的攻击面和相应防护措施是很重要的。
在本文中,我们将讨论EdgeHTML的攻击面并对各攻击维度进行枚举,同时,我们还会阐述一种用于比较EdgeHTML和MSHTML的方法,以此了解fork过程中所发生的变化,当然更为重要的是发掘可能被攻击的新特性和新增内部函数。最后,我们将讨论相关的漏洞利用防护措施,即它们是如何防御特定类型漏洞的,此外,还会讨论那些目前仍然适用的绕过技术。
EdgeHTML[1, 2]是微软Edge浏览器(早前代号为Project Spartan)中新引入的渲染引擎,它是目前Internet Explorer中在用的Trident(MSHTML)渲染引擎的一个fork,主要为了支持现有的Web标准以及删除其中陈旧的代码。
据估计[3]fork过程中大约有超过22万行代码被删除,并重新添加了大约30万行代码用于程序互操作的修缮与新功能的实现。站在安全研究的角度看,理解这些变化所带来的影响,换言之渲染引擎在攻击面上的变化,是既有趣而又重要的 - 新的攻击维度是什么?攻击者先前使用的一些方式还有效吗?另一个需要重点理解的方面是新渲染引擎应对攻击时的防护措施 - 漏洞利用的保护策略是否有变化?默认设置下与MSHTML相比攻击者进行利用的难度有多大?回答这些基本问题正是本文的目的所在。
接下去的内容分为三大块。第一部分(概述)将简要介绍漏洞利用的攻击面和防护措施,后面还会再进行深入的探讨,其中给出的“EdgeHTML攻击面及其防护”示意图可作为本文余下篇幅的参考,此外,这部分还讨论了我在早前研究过程中用于比对EdgeHTML和MSHTML的方法。第二部分(漏洞利用攻击面)我们将深入分析组成EdgeHTML攻击面的那些不同攻击维度,并指出从MSHTML到EdgeHTML后重要攻击维度的变化情况。而在最后一部分(漏洞利用防护措施)我们将讨论不同的保护策略,它们都有助于增大EdgeHTML引擎上进行漏洞利用的难度和代价。
另外,本文的分析是基于64位Windows 10 build 10240系统中的Edge浏览器展开的(edgehtml.dll版本号为11.0.10240.16384)。
在深入解读EdgeHTML引擎的攻击面与防护措施相关细节前,本节先来做个简要的介绍,同时本节还将讨论EdgeHTML和MSHTML的比较方法,这能帮助我们找出代码中的主要变化。
EdgeHTML引擎模块(%System32%\edgehtml.dll)负责解析和渲染网页中各种潜在的恶意内容,它属于Edge浏览器的内容处理单元(MicrosoftEdgeCP.exe),该单元是一个64位的AppContainer沙箱进程。
这里的攻击面图示给出了EdgeHTML引擎所接受的不同输入类型以及负责处理它们的各EdgeHTML类,注意这些类只是相应解析或处理过程的初始入口,其中一些可能还需要借助额外的EdgeHTML类。此外,图示还列出了EdgeHTML类用于处理输入时所依赖的函数库。
对于HTML,EdgeHTML会使用内部的解析器来处理标记,而对基于XML的标记(XHTML,SVG,XML),引擎会额外使用XmlLite进行解析,且如果标记中有引用XSL样式表,那么引擎会首先通过MSXML6对XML进行转换,然后才将输出内容交给标记处理单元,至于CSS的解析同样也由内部解析器处理。对于图像的解码,EdgeHTML主要依赖于Windows Imaging Component (WIC),而对于音频/视频内容的解码,EdgeHTML则主要依赖Media Foundation (MF),同时,用于音频/视频字幕的计时文本轨道文件是由EdgeHTML的内部解析器负责处理的,当然这其中还需要借助XmlLite。而字体渲染则会通过DirectWrite进行处理。另外,由标记标签的解析或经由动态创建所生成的DOM对象会通过DOM API暴露出来,其中一些DOM对象可能需要依赖相应函数库来实现它们的功能。此外,EdgeHTML引擎在默认情况下还可实例化内置或预安装的渲染器 - 例如,用于处理PDF内容的WinRT PDF渲染器,以及用于处理Flash内容的Adobe Flash Player程序。
EdgeHTML渲染引擎所处的64位内容处理进程中用到了多种漏洞利用的防护措施,其中就包括了ASLR保护(高熵且强制启用ASLR)、DEP保护和AppContainer进程隔离保护。并且EdgeHTML及其依赖在编译时均采用了栈缓冲区安全检查保护技术(/GS)和最新引入的执行流保护技术(CFG)。此外,例如虚表保护(VTGuard)以及新的内存垃圾回收机制(MemGC)等额外措施也被专门用于EdgeHTML的防护。
当我一开始对EdgeHTML进行研究的时候就很想知道从MSHTML到EdgeHTML的fork过程中代码的主要变化是什么。由于类和命名空间能够表征一组相关的代码,而这些代码又反过来对应着程序的某一特性,因此,我尝试的方法是借助IDAPython枚举函数或变量名,然后提取其中的命名空间部分,接着去除那些重复项并对得到的结果进行排序,最后,通过diff工具对得到的MSHTML和EdgeHTML命名空间列表进行比较:
来看一个比对结果的例子,如下EdgeHTML中的删除类表明了引擎在WMF和EMF图像格式支持上的变化(详见3.3小节):
-CImgTaskEmf -CImgTaskWmf
而从另一比对结果的例子可看出CFastDOM下又新增了命名空间,即通过DOM API导出了新的DOM对象类型(详见3.5小节):
+CFastDOM::{…more…} +CFastDOM::CXPathEvaluator +CFastDOM::CXPathExpression +CFastDOM::CXPathNSResolver +CFastDOM::CXPathResult +CFastDOM::CXSLTProcessor
最后,再看一个有意思的比对结果,如下表明其中的一些代码是从Blink渲染引擎[4,5](Blink是从WebKit的WebCore部分fork来的,大部分的命名空间已从“WebCore”重命名为“blink”)移植过来的:
+blink::WebThread +WebCore::AnalyserNode +WebCore::AudioArray<float> +WebCore::AudioBasicInspectorNode +WebCore::Audio{…more…}
进一步分析可知这部分移植代码主要用于EdgeHTML引擎中对Web音频的支持。
另外,除了用于比较命名空间,这个基本的方法还可用于函数名、类方法名(以识别新的或移除的类功能)、字符串(可给出新功能的描述 - 例如:日志字符串)、导入表(以识别新的依赖库、用到的特定API)和导出表的比对。
使用此方法时需要留意命名空间被重命名的情况,这会导致比对结果中命名空间的添加及删除,因此需要做进一步的验证,该方法还要求相关的符号文件是可用的。此外,二进制的比对[7]则是识别两个二进制文件间差异的另一选择。
在本节,我们将列举EdgeHTML引擎所处理的不同输入以及与此相关的代码接口。需要注意的是给出的EdgeHTML类仅是解析或处理时的初始入口点,其中一些可能还依赖额外的类来进行处理,获取这些类的目的在于当我们需要了解引擎是如何处理特定的输入类型时可借助它们来设置断点。例如,若要了解基于XML的标记其预解析时的工作原理,则可通过以下方式设置CXmlPre类中的断点:
(WinDbg)> bm edgehtml!CXmlPre::*
此外,如果引擎依赖其它函数库来处理特定的输入类型,那么此函数库和用到的特定接口也会被列出来。
渲染引擎的主要任务之一是处理标记和样式,对于HTML和CSS,EdgeHTML引擎依靠其内部类进行解析,而对基于XML的标记,引擎则借助XmlLite [8]和MSXML6 [9]进行解析:
EdgeHTML分两个阶段来处理标记,从CHtmPre::Exec()或CXmlPre::Exec()开始的预解析阶段涉及到标记的初始解析、将解析的标签写入标签流以及开始下载引用资源(如果可用 - 比如图像和CSS文件),从CHtmPost::Exec()开始的后解析阶段则从标签流中获取标签,如果需要可对标签执行进一步解析,并最终创建DOM对象。
而通过调用xmllite!CreateXmlReader()函数实例化的IXmlReader接口会被CXmlPre作为解析器用于预解析基于XML的标记,同时,当检测到XML文件中有引用XSL样式表时,此接口也会被CBaseXSLTFilter用作XML解析。另外,MSXML6的IXMLDOMDocument接口会被CBaseXSLTFilter用于转换引用了XSL样式表的XML文件。
引擎中一个重要的变化是对于二进制行为的支持[10],包括删除内置的VML。基于VML的(VGX.DLL)漏洞[11]是很严重的,因其虽然过时,但在IE11/MSHTML下仍然可以默认使用。
预计随着时间的推移,渲染引擎对标记/样式的处理,特别是CSS和HTML的解析会有所更新,因为届时将会有新的Web标准需要新的HTML标签、HTML属性、CSS属性等来支持。与此相关的一个较有名0day例子是MSHTML CSS递归导入漏洞[12]。
图像渲染是EdgeHTML引擎的另一基本任务,其中图像文件可通过链接或以HTML标签(如<img>
和<embed>
标签)的方式传给引擎。
我们可以通过查看g_rgMimeInfoImg数组来枚举所支持的图像格式,此数组包含指定图像MIME类型的MIMEINFO项以及用于实例化相关图像处理接口的函数。
如下是EdgeHTML支持的图像格式,从Library列可以看出所有的图像都是通过Windows Imaging Component (WIC)[13]进行处理的(SVG格式则通过3.1小节中描述的标记来处理):
对于图像的处理,EdgeHTML首先通过CWicGlobals::GetWicImagingFactory()函数实例化WIC的IWICImagingFactory接口,接着再调用IWICImagingFactory::CreateDecoder()函数来为特定的图像格式实例化一个IWICBitmapDecoder接口。
一个有意思的变化是EdgeHTML中对WMF和EMF图像格式的支持被删除了,意味着先前借助GDI库来解析远程WMF和EMF文件的依赖也被移除了,与此相关的远程利用漏洞已经是屡见不鲜了[14,15,16]。
通过链接或HTML的<audio>
和<video>
标签,我们可将任意音频/视频内容交由渲染引擎处理,所支持的音频/视频格式的Mime信息可在g_rgMimeInfoAudio
和g_rgMimeInfoVideo
数组中得到。此外,正如下表所示,EdgeHTML中用于处理音频/视频内容的依赖库是Media Foundation(MF)[17]:
EdgeHTML引擎会借助MFCreateMediaEngine()函数来实例化MF的IMFMediaEngine接口,以此设定媒体源并对播放进行控制。
除了音频/视频文件外,EdgeHTML引擎还会处理计时文本轨道[18],我们可通过HTML的<track>
标签来指定,引擎中支持的两种文本轨道文件格式如下:
其中,TTML是基于XML的,处理它的EdgeHTML类会借助XmlLite的IXmlReader接口进行解析。
任意字体都可通过CSS @font-face 规则传给EdgeHTML渲染引擎[19]。而对于字体解析漏洞[20],特别的,如果能通过浏览器渲染引擎触发的话,那么就可能造成远程利用 - CVE-2011-3402 [21,22]就是这样一个例子,它为GDI中的字体解析漏洞(位于Win32k.sys模块),此漏洞最初被用于0day攻击,后来被集成到浏览器的漏洞利用套件中。
EdgeHTML引擎使用DirectWrite [23]来渲染字体且支持的格式如下:
所需DirectWrite接口的实例化是由CDXResourceDomain::EnsureDXFactories()函数完成的,且引擎会通过CDXEmbeddedFontFace::Initialize()函数检测实际的字体格式,并通过IDWriteFactory::CreateCustomFontFileReference()调用在CDXPrivateFont::Initialize()函数中执行自定义字体的注册。
与GDI不同,DirectWrite(DWrite.dll)由其所在的用户态进程来解析字体文件。另外,EdgeHTML引擎中一个显著的变化是EOT字体格式的支持被删除了,这意味着解析EOT字体的T2EMBED [24]和GDI依赖也被删除了,因此用于解析字体的函数库就相应减少了。
DOM (Document Object Model) API [25]是渲染引擎中最大的攻击面之一。当解析HTML文档时,渲染引擎会实例化那些用于表示HTML标签的DOM对象,同时引擎还将创建诸如document这样的核心对象,且新的DOM对象还可通过JavaScript代码来动态实例化,我们这里讨论的DOM API则提供了一种操作这些对象的方法。
当DOM对象的属性被更改或其方法经由脚本被调用时,渲染引擎中会执行相应的代码:
因DOM API调用而执行的渲染引擎代码可改变DOM树、DOM对象和渲染引擎内部对象,其中,非可预期的输入、非可预期的状态改变以及错误的内部状态都可能导致产生诸如释放后重用[26](例子如下)、堆溢出[27]、无效指针访问[28]等内存错误漏洞。
借助2.2小结所述的比对方法,我们可在CFastDOM命名空间下找到EdgeHTML引擎中那些新增的DOM对象类型:
+CFastDOM::CAnalyserNode +CFastDOM::CAriaRequestEvent +CFastDOM::CAudioBuffer +CFastDOM::CAudioBufferSourceNode +CFastDOM::CAudioContext +CFastDOM::CAudioDestinationNode +CFastDOM::CAudioListener +CFastDOM::CAudioNode +CFastDOM::CAudioParam +CFastDOM::CAudioProcessingEvent +CFastDOM::CBiquadFilterNode +CFastDOM::CClipboardEvent +CFastDOM::CCommandEvent +CFastDOM::CConvolverNode +CFastDOM::CCryptoKey +CFastDOM::CCryptoKeyPair +CFastDOM::CCSS +CFastDOM::CCSSConditionRule +CFastDOM::CCSSGroupingRule +CFastDOM::CDataCue +CFastDOM::CDataTransferItem +CFastDOM::CDataTransferItemList +CFastDOM::CDeferredPermissionRequest +CFastDOM::CDelayNode +CFastDOM::CDynamicsCompressorNode +CFastDOM::CEventTarget +CFastDOM::CGainNode +CFastDOM::CGamepad +CFastDOM::CGamepadButton +CFastDOM::CGamepadEvent +CFastDOM::CHashChangeEvent +CFastDOM::CIsolatedGlobalScope +CFastDOM::CMediaDeviceInfo +CFastDOM::CMediaDevices +CFastDOM::CMediaStream +CFastDOM::CMediaStreamError +CFastDOM::CMediaStreamErrorEvent +CFastDOM::CMediaStreamTrack +CFastDOM::CMediaStreamTrackEvent +CFastDOM::CMSAppAsyncOperation +CFastDOM::CMSHeaderFooter +CFastDOM::CMSPrintManagerTemplatePrinter +CFastDOM::CMSTemplatePrinter +CFastDOM::CMSWebViewSettings +CFastDOM::CNavigationEventWithReferrer +CFastDOM::COfflineAudioCompletionEvent +CFastDOM::COfflineAudioContext +CFastDOM::COscillatorNode +CFastDOM::COverflowEvent +CFastDOM::CPannerNode +CFastDOM::CPermissionRequest +CFastDOM::CPermissionRequestedEvent +CFastDOM::CRTCDtlsTransport +CFastDOM::CRTCDtlsTransportStateChangedEvent +CFastDOM::CRTCDtmfSender +CFastDOM::CRTCDTMFToneChangeEvent +CFastDOM::CRTCIceCandidatePairChangedEvent +CFastDOM::CRTCIceGatherer +CFastDOM::CRTCIceGathererEvent +CFastDOM::CRTCIceTransport +CFastDOM::CRTCIceTransportStateChangedEvent +CFastDOM::CRTCRtpListener +CFastDOM::CRTCRtpReceiver +CFastDOM::CRTCRtpSender +CFastDOM::CRTCRtpUnhandledEvent +CFastDOM::CRTCSrtpSdesTransport +CFastDOM::CRTCSsrcConflictEvent +CFastDOM::CScriptProcessorNode +CFastDOM::CServiceUIFrameContext +CFastDOM::CStereoPannerNode +CFastDOM::CSVGForeignObjectElement +CFastDOM::CVideoTrack +CFastDOM::CVideoTrackList +CFastDOM::CWaveShaperNode +CFastDOM::CXMLHttpRequestUpload +CFastDOM::CXPathEvaluator +CFastDOM::CXPathExpression +CFastDOM::CXPathNSResolver +CFastDOM::CXPathResult +CFastDOM::CXSLTProcessor
这些新增DOM对象类型表示EdgeHTML中新引入了的代码或代码路径,它们可通过DOM API来访问。
就枚举DOM对象的属性和方法而言,我们可以借助JavaScript的for...in语句。下述例子用到了新的XSLTProcessor DOM对象类型:
而通过比较DOM对象属性的枚举结果,我们可以得出那些已存在的DOM对象类型其属性的变化情况。以下是document对象的属性比对片段,至于新增DOM对象类型也是相似的:
[…] +document.evaluate document.execCommand document.execCommandShowHelp +document.exitFullscreen document.fgColor -document.fileCreatedDate […]
此外,我们还可通过IDA中名称窗口的查询来确认DOM对象的属性和方法:
通过跟踪名称窗口中列出的其中一个函数,我们最终可以找到表示DOM对象类型的实际EdgeHTML类:
伴随着新的特性被添加到Edge浏览器中[29],相对应的可能会引入新的DOM对象类型,或者将新的属性/方法添加到已存在的DOM对象中,以此将这些新功能提供给开发人员。这些新的DOM对象类型、属性和方法反过来也会成为新的被攻击代码,从而增大了渲染引擎的攻击面。
虽然从技术层面上讲它们并不属于渲染引擎的一部分且它们自身各有一组复杂的解析及渲染操作,但Windows的内置PDF渲染器[30](自Windows 8.1以来)和预安装的Adobe Flash Player(自Windows 8以来)仍可被认为是EdgeHTML引擎用于渲染各种文件格式的众多依赖之一,它们都是预安装的[3],且默认情况下均可被EdgeHTML渲染引擎实例化:
其中,PDF渲染器是由CPDFHelper::LoadPdfDoc()函数实例化的,而Flash渲染器的实例化则是从CCodeLoad::BindToObject()函数开始的。
从攻击者的角度看,能额外借助复杂的渲染器必然会有下述优势:(1)这些复杂的渲染器都有一组可被利用的攻击面及对应漏洞;(2)它们的某些特性可被用来绕过漏洞的利用防护 - 一个例子是利用Flash的JIT生成代码绕过CFG保护 [31],另一个例子是0day利用中借助Flash Vector对象的corruption技术[32]绕过ASLR保护,此例由IE渲染引擎漏洞来实现内存Flash Vector对象的corruption[33]。虽然对通过Flash的JIT绕过CFG保护以及借助Flash的Vector对象绕过ASLR保护已有相关的防护措施(见4.3小节和[34]),但这两个例子很好的阐述了如何借助程序的功能来实现利用。
在图像和字体渲染方面,EdgeHTML引擎的攻击面是在减少的,因为它不再支持EMF和WMF格式的图像以及EOT字体,处理这些格式的依赖库代码(GDI和T2EMBED)中含有远程利用漏洞也是小有历史了。此外,删除VML的支持(二进制行为)也有助于进一步减少EdgeHTML引擎的攻击面。
然而,同其它现代浏览器一样,这之中又引入了新的特性,而这些新功能则通过新的DOM对象类型/属性/方法以及新的标记/样式规范来实现。就EdgeHTML引擎而言,我们在DOM API中发现了新的攻击维度,包括新的DOM对象类型以及已有DOM对象类型中新添加的属性和方法。
此外,下述依赖库在EdgeHTML引擎中是有用到的:
通过分析这些库的使用,我们进一步认识到了它们的重要性,毕竟我们现在对渲染引擎如何使用它们以及攻击者如何能通过恶意输入来远程访问这些代码有了更多的理解。
既然我们对引擎的攻击面已经有了解了,那下面就来看看攻击者为了成功在EdgeHTML或其任何依赖中实现利用都需要绕过哪些漏洞利用防护,此外,我们还会讨论和提及那些由安全人员发现的已知防护措施绕过方法或防护中的薄弱点。
本节对EdgeHTML模块所在的内容处理单元中涉及的缓解方案仅作简要介绍,而把详细的讨论放在EdgeHTML引擎及其依赖的利用防护上。至于Windows堆相关的防护已经在各种论文或报告[35,36,37,38,39,40]中有详细讨论,这里就不再赘述了。
在Win10 64位系统中,EdgeHTML渲染引擎模块(%System32%\edgehtml.dll
)所在的内容处理单元(MicrosoftEdgeCP.exe)默认运行在64位,且启用了ASLR保护(HEASLR,ForceASLR)和DEP保护,并通过AppContainer来实现沙箱功能:
Edge内容处理单元的防护措施与Windows 8中Immersive版的IE相同,但与Windows 10,Windows 8(Desktop版的IE)和Windows 7上的IE11不同。
下表给出了不同Windows版本上Edge和IE内容处理单元所默认用到的保护措施:
其中,64位技术有助于缓解传统的堆喷利用,该利用会将攻击者可控的数据在堆上进行喷射,从而把数据布局到特定的地址处。当然,根据漏洞的不同,“相对堆喷”[28,41]也是可能的,例如漏洞包含一个有效堆指针,但指针在计算时被加上了一个攻击者可控的或错误的数值。
同时,内容处理单元中还启用了高熵ASLR保护(HEASLR)以及强制ASLR保护(ForceASLR)[42,43],其中,HEASLR保护给可重定位的内存区域增加了额外的信息熵,而ForceASLR保护能够避免将不支持ASLR保护的DLL模块加载到固定的内存地址。在启用ForceASLR保护的进程中,绕过ASLR保护一般需要利用那些可预测内存地址的对象指针或使用漏洞来实现内存信息泄露,由于微软目前正积极地解决前者[44,42],因此越来越多的攻击将会依靠漏洞进行信息泄露[45,32]。
此外,AppContainer则是Windows 8中新引入的进程隔离机制,它被用在IE的增强型保护模式[46]沙箱中,可限制进程的读/写访问权限和相关功能。目前,有几种方法能够绕过AppContainer沙箱保护(以及其它沙箱保护),这些方法包括了利用内核漏洞[47,48]、利用中级或更高权限进程中的漏洞[46,49,50]以及利用可写资源[49]。
基于栈的缓冲区溢出通常被用于控制程序的执行流程,为了检测此类利用,EdgeHTML引擎及其依赖均采用缓冲区安全检查(/GS) [51]选项进行编译。该检测机制会在保存局部变量的缓冲区后面设置一个安全cookie,然后在函数返回前检查此安全cookie,以确保返回地址和保存的寄存器没有被缓冲区溢出所覆盖。另外,该机制还对数据进行了备份,相关参数和局部变量的副本被存储在缓冲区之前,以防止它们在缓冲区溢出的情况下被破坏:
该缓解措施已在各种论文[52,53]中进行了深入讨论,并且相关机制还在不断地更新[42],以期提高防护的覆盖面。当然,此机制的一个局限是它并未考虑攻击者能够控制特定位置写入数据的情况[54,20](例如可控的缓冲区索引/指针),这允许攻击者直接越过安全cookie进行写入。
EdgeHTML引擎及其依赖中引入了新的漏洞利用防护措施,即CFG保护(Control Flow Guard)[55,56]。当启用CFG保护时,编译器将在程序中添加额外的检测代码,以确保间接调用的目的地址是有效的。此策略主要用于检测并阻止异常的执行流程,比如通过设置执行地址的方式将流程重定向到ROP链中。
此防护机制的内部原理已被深入研究过了,相关内容发表于各种论文/报告中[57,58]。针对CFG保护的绕过[31],一种方法是借助Flash的JIT生成代码,因为它是动态解析的,固其内部的间接调用不会被CFG保护覆盖到。然而,这种绕过技术现在已通过Flash中额外引入的代码得到了防护,只要生成调用指令,那么相应的就会通过ntdll!LdrpValidateUserCallTarget()函数进行检查。此外,其它绕过CFG保护的思路还包括跳转到有效的API地址(如LoadLibrary)[41]、覆盖堆栈数据(如返回地址)[57,41]等。
VTGuard(Virtual Table Guard)[42]是EdgeHTML中的另一漏洞利用防护措施,但此机制并没有被应用到相关依赖中。VTGuard是在IE10中首次引入的,其目的在于检测虚函数表是否有效,主要针对通过内存中可控的C++对象来控制程序执行流的利用情形,它在虚函数表中添加了一个__vtguard随机值,执行虚函数调用前将对该值进行检查:
此防护的一个缺点是它仅适用于EdgeHTML中的类对象,并且如果能通过内存信息泄露获取__vtguard的地址,那么就可以简单地进行绕过。
Memory GC(MemGC)[59]是在Win10的EdgeHTML和MSHTML渲染引擎中首次引入的,它衍生于早期的Memory Protector [60, 61, 62]漏洞利用防护。
与Memory Protector一样,MemGC的目的是通过阻止内存块(chunk)的释放(如果还能找到相关的引用)来缓解UAF(use-after-free)漏洞[26]的利用。但是,与Memory Protector只检查寄存器以及堆栈中的内存块(chunk)引用不同,MemGC还会扫描托管堆中的内容来查找引用,这种附加检查意味着它能进一步减少攻击者可利用的UAF漏洞。
配置
MemGC是默认启用的,在Edge和IE中都可通过“OverrideMemoryProtectionSetting”属性进行配置,相应的注册表项如下:
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Internet Explorer\Main OverrideMemoryProtectionSetting = %DwordValue%
其中,%DwordValue%可取下述任意值:
MEMGC堆管理
MemGC使用单独的托管堆(MemGC堆)进行对象空间的分配,且通过并发的垃圾回收机制执行标记和清除操作,以此识别和回收堆中未被引用的内存块(chunk),这个过程中MemGC会依赖Chakra(JavaScript引擎)的内存管理代码来实现大部分功能。
在用到的分配方案中,MemGC首先会通过VirtualAlloc()函数申请大量被称为Segment的内存空间,接着将这些Segment按4096字节划分为Page页面,而后再将其中的一组Page作为一个Block块,在此基础上按照相似大小原则进行对象空间的分配:
其中,EdgeHTML/MSHTML DOM对象以及大量的渲染引擎内部对象都是由MemGC管理的,此外,由于MemGC中已经使用了单独的托管堆,所以如果它是启用的,那么将不会再用到隔离堆。
分配
在EdgeHTML引擎的MemGC实现中,当需要分配一个托管对象时,edgehtml!MemoryProtection::HeapAlloc<1>()或edgehtml!MemoryProtection::HeapAllocClear<1>()函数将被调用,并转而通过chakra!MemProtectHeapRootAlloc()函数进行处理。而chakra!MemProtectHeapRootAlloc()函数首先会寻找合适的bucket,然后从指向的Block中为其分配一个相应的chunk,最后将此chunk标识为root。在垃圾回收中,root标识表示该对象/chunk在程序中存在直接引用,因此不能被回收,同时,在搜索chunk引用时也会扫描这些标识为root的对象/chunk。
释放
当对象需要被释放时,引擎将会调用edgehtml!MemoryProtection::HeapFree()函数进行处理并转而执行chakra!MemProtectHeapUnrootAndZero()调用。对于chakra!MemProtectHeapUnrootAndZero()函数,它将定位此chunk所在的具体Block,并将chunk的内容清零,然后去除它的root标识。通过清除root标识,此chunk成了潜在的回收目标,如果回收器未找到关于此chunk的相关引用,那么它就会被回收。
垃圾回收
一旦未被root标识的chunk总大小达到特定阈值,那么就会由chakra!MemProtectHeap::Collect()函数触发垃圾回收机制。垃圾回收过程(因其复杂性,这里仅描述相关的核心功能)将通过标记和清除操作来回收那些未被引用的且未被root标识的chunk,其中的部分操作将在chakra!Memory::Recycler::StartConcurrent()函数下发的独立线程(chakra!Memory::Recycler::ThreadProc)中进行。
在标记阶段,首先会清空所有的chunk标记位,然后标记所有root标识的chunk(通过chakra!Memory::Recycler::BackgroundResetMarks()函数),接着扫描root标识的chunk(通过chakra!Memory::Recycler::ScanImplicitRoots()函数)、寄存器以及堆栈(通过chakra!MemProtectHeap::FindRoots()函数)来搜索chunk指针,并将找到的那些存在引用的chunk进行标记。最终,当标记阶段完成后,那些仍未被标记的chunk将可重新用于对象的分配。
在撰写本文时,所涉案例中尚无已知的有关MemGC和Memory Protector的绕过手法,但与其它利用防护一样,将来可能也会出现相关的绕过技术。另一方面,目前公开的有借助Memory Protector实现32位IE中ASLR保护绕过的技术,以及借助Memory Protector实现的64位IE上用于近似分配过程中(包括堆分配)地址区间的时序攻击,当然,这里给出的例子并不是针对Memory Protector的绕过。
默认配置下,与Windows 10、Windows 8(Desktop版的IE)以及Windows 7上运行的IE11内容处理单元相比,EdgeHTML引擎所在的内容处理单元中涉及的漏洞利用防护措施要更加全面 - 它默认运行在64位,从而允许ASLR保护工作于HEASLR模式,这导致了传统的堆喷技术变得不再可行或非常不可靠,因此,为了实现可控数据的内存布局,攻击者必然要开发出更为精确的利用技术。
另一主要区别是,Egde中采用了约束性更强的AppContainer来实现沙箱的功能,这极大限制了引擎中利用程序的访问权限和相应功能,所以除非此漏洞还存在于特权进程或系统组件[20]中,否则就需要借助另外的漏洞进行AppContainer沙箱逃逸。
同时,栈缓冲区安全检查(/GS)能减少潜在的可利用堆栈漏洞,而要绕过执行流保护则需要借助于新的利用技术,并且同Memory Protector一样,MemGC将会进一步减少引擎中可利用的UAF漏洞。
总体来说,依托于这些防护手段,攻击者要想在EdgeHTML引擎中发掘可利用的漏洞则需要更多的投入,若需开发可靠利用则尤甚。言虽此,但攻击者势必会不断寻找新的方法来绕过这些保护,可以预见的是防护措施也将随着时间的推移而逐渐演变。
EdgeHTML渲染引擎(以及其它浏览器渲染引擎)的攻击面将不可避免地随着Web新标准的实行而不断增多,其中的大部分将会来自对新标记/样式的解析,最明显的莫过于那些经由DOM API导出给开发人员(当然还有攻击者)的新功能。
另一方面,引擎中新增的攻击面会通过应用于内容处理单元、相关依赖库以及其自身模块中全面的利用防护来进行缓解,这些防护措施将使许多引擎漏洞变得不可利用或者开发利用程序的难度变得非常大。
此外,下述与EdgeHTML引擎相关的研究领域不仅重要且很有意思,涉及内容都是可被远程访问且广泛用到的库/特性:
引擎中所用Windows组件的原理研究、代码审计以及Fuzzing:XmlLite、MSXML6、Windows Imaging Component(WIC)、Media Foundation(MF)、DirectWrite和WinRT PDF Renderer。其中一些可能已经有公开成果了(比如DirectWrite [20]),但是还需要更多有关的研究,这样我们才能对此类关键组件的安全性有所了解。
内部实现(算法细节,数据结构等)、Heap Grooming、堆元数据攻击(如果可能)以及对MemGC绕过技术的研究。本文对MemGC进行了初步的探讨,此外,针对其内部原理的进一步分析、研究如何通过MemGC堆进行攻击利用以及研究如何绕过MemGC保护将有助于理解其防护中的薄弱点,从而实现对它的改进。
最后,衷心希望本文能对你理解EdgeHTML渲染引擎安全性方面的知识起到帮助:P
*注:参考文献的信息详见原文