前言
如上所述,CVE-2019-1208
是UAF
漏洞,这类安全漏洞可以破坏有效数据、引发进程crash、并且可以精心利用最终导致任意代码执行。而对于本文介绍的CVE-2019-1208而言,成功利用此漏洞的攻击者可以获得系统当前用户权限。如果当前用户具有admin权限,则攻击者可以劫持系统——从安装或卸载程序,查看和修改数据 到 创建具有完全权限的用户。
潜在影响
还有一种更有形的攻击情形,攻击者向不知情的用户发送钓鱼邮件,并诱使他们通过IE访问恶意网站(存在CVE-2019-1208的漏洞)。
或者,攻击者可以发送带有附件的垃圾邮件,其中包含该漏洞的exp。这些附件可以是启用了IE渲染引擎的Microsoft Office文档,也可以是嵌入了ActiveX控件的的应用程序,而ActiveX控件又包含exp。
攻击者还可以在一些合法网站上布置攻击代码,例如那些接受用户的输入的网站。
下图为VbsJoin
的代码流:
漏洞是如何被发现的?
我的研究始于BinDiff
,当时我正试图比较vbscript.dll
的5月和6月的版本,寻找其中被更改的函数,而这个模块包含VBScript引擎的API函数。最终我发现通过SafeArrayAddRef,SafeArrayReleaseData和SafeArrayReleaseDescriptor函数进行了一些修复
然而,受我之前发现的漏洞CVE-2018-8373的启发,进一步分析后我使用VBScriptClass经过下列步骤触发了UAF漏洞:
-
arr = Array(New MyClass)
,创建一个SafeArray并在arr[0]处保存VBScriptclass: MyClass
-
Callback: arr = Array(0)
,Join(arr)
将触发MyClass的回调函数Public Default Property Get
。在此回调中,为变量创建了一个新的SafeArray,如下图所示而这个新的SafeArray不受SafeArrayAddRef
函数的保护。因此,正常的代码流假设 被这个回调打破
-
arr(0) = Join(arr)
,当从Public Default Property Get
返回时,VBsJoin中的代码将会调用SafeArrayReleaseData
和SafeArrayReleaseDescriptor
来减少SafeArrayData
和SafeArrayDescriptor
的引用计数。但是,新的SafeArray不受SafeArrayAddRef
保护,并且SafeArrayData
和SafeArrayDescriptor
的引用计数为0。因此,新的SafeArray的
SafeArrayData
和SafeArrayDescriptor
将在函数SafeArrayReleaseData
和SafeArrayReleaseDescriptor
被free,如下图所示:
其中,代码快照显示内存中的
arr = Array(New MyClass)
(顶部),内存中的arr = Array(0)
和callback
位于底部
将VbsJoin的返回值保存到arr(0)时,PoC在vbscript!AccessArray
中崩溃(见下图),因为SafeArrayDescriptor
被free,Variant arr仍然保存释放的SafeArrayDescriptor
的指针。
显示PoC如何在
vbscript!AccessArray
中触发crash的代码快照
PoC是否成功触发UAF?
在某种程度上,这个回答是肯定的
为了演示如何完全触发UAF,我使用了basic string/binary string(BSTR)
作为数据结构。SafeArray是一个多维数组,但由于VBsJoin只能处理一位数组,因此我更改了回调中的SafeArray的维度。
不幸的是,它仍然没有用,它会抛出一个运行时错误,指出数组类型在Join中不匹配。不过没有关系,我使用On Error Resume Next来绕过这个运行时错误,下图为修改过后的PoC:
在获得了0x20字节的已释放内存后,我使用大小为0x20字节的BSTR来伪造一个较大size的SafeArray。
通过使用堆风水,这个BSTR可以稳定地重用0x20字节的已释放内存,如下图所示:
我终于得到了一个伪造的一维数组SafeArray,一共有0x7fffffff个元素,每个元素大小为1字节,如下图:
伪造的SafeArray(顶部)和读/写的固定地址(底部)
到目前为止,我已经可以伪造一个SafeArray,可用于读/写0x00000000到0x7fffffff的内存
为了泄露一些读/写地址以便利用,我参考了Simon Zuckerbraun之前的研究并使用堆喷射来获得一些固定的读/写地址(0x28281000)
从UAF到RCE
如Simon Zuckerbraun的博客中所述,我使用Scripting.Dictionary对象来完成远程代码执行,但使用另一种方法来伪造一个假的Dictionary。这一次,我使用了BSTR并将它们带出来
Faked Dictionary内存布局如下:
- 使用内存读/写函数来读取原始的Dictionary内存,将其数据保存到一个BSTR,并将
VBADictionary::Exists
替换为kernel32!Winexec
- 将之后用到的
Winexec
参数\..\clac.exe
写入此BSTR - 将此BSTR保存到
util_memory+0x1000
,并修改util_memory + 0x1000 – 8 = 9
以使fake_array(util_memory + 0x1000)
成为对象 - 使用
fake_array(util_memory + &h1000)
,利用dummy
来触发Winexec
函数
最终利用结果,成功弹出计算器:
这个漏洞对IE意味着什么?
在2019年8月13日,已在Windows 10中禁用的VBScript 又同时在Windows 7,8中禁止IE使用。因此,上文详述的PoC是在本地模式下开发的。但正如微软所说,仍然可以通过注册表或组策略启用此设置。同样,用户和组织应始终采用最佳实践:保证系统打上补丁和保持更新,禁用不需要(或限制使用)的组件,并派样网络安全意识,如垃圾邮件和其它社会工程学威胁
研究的完整细节在本技术简报中