1. 引言

在最近的一年里, 漏洞利用工具包(EK/Exploit Kit)市场风云变幻。2016年六月初,曾经极为猖獗的Angler EK 销声匿迹,Neutrino EK 迅速填补了空白。随后短短不到3个月时间,Neutrino EK 又转为地下,RIG EK继而成为最流行的漏洞利用工具包。今年3月初,RIG又淡出视线,而迎来了新的Nebula EK。

Nebula EK包中对CVE-2016-0189的漏洞利用,比其它漏洞利用包的漏洞利用方有了一定改进,这里进行一下深入分析。

所用工具:

? IE11(调试Javascript)

? windbg

Nebula EK 的首页,如下图所示。

 1.jpg

不难看出,页面里包含了混淆过的Javascript代码。进一步分析,这些代码是否有种似曾相识的感觉?对了,代码的混淆方法跟落日漏洞利用工具包(Sundown EK)极为相似。

页面中共有2段Javascript代码, 混淆方法相同。第一段Javascript代码,去掉混淆后,代码如下, 不难看出,这段代码就是CVE-2016-0189的漏洞利用。

 2.jpg

关于CVE-2016-0189,网上已有一些分析报告,比如Theori的分析[1]。 然而这些分析报告,并未提供在内存层面的技术细节,使得读者总有种知其然,不知其所以然的感觉。本文试图利用windbg来分析漏洞利用时的内存布局,使得读者对该漏洞利用有更深刻的理解。

2. CVE-2016-0189的关键知识点

CVE-2016-0189是个关于VBScript的漏洞。VBScript 脚本引擎代码在vbscript.dll 中。

2.1. VBScript的变量

VBScript 的内存变量占用0×10个字节, 前两个字节定了变量类型VARTYPE。常见类型定义如下表所示[2]。

21.jpg

我们可以在VBScript代码中插入IsEmpty()函数,然后在windbg的vbscript!IsEmpty() 函数上设置断点来观察内存[3]。

22.jpg

图1  调试用VBScript代码

23.jpg

这里,0a560198就是str对象,这里 0008两个字节表示的是VARTYPE,根据表可知,值恰好为vbString(VT_BSTR = 8) 而偏移0×8处存放的就是字符串地址,

24.jpg

这里还可以看出,字符串是Unicode存放的。

2.2. VBScript的数组

VBScript 的数组的定义如下

25.jpg

当访问数组元素时,VBScript引擎会调用 AccessArray 函数来计算元素的存放地址。

试着用windbg 调试如下代码, 在vbscript!AccessArray处下断点:

26.jpg

27.jpg

堆栈中0ab04380即为数组A地址

28.jpg

数组元素存放在 pvData (0x7dfd130) 起始的地方

在 vbscript!AccessArray+0x9d: 指令处停下   (不同版本这个偏移值可能会有出入)

29.jpg

这条指令非常关键,它计算元素A(1,2) 的地址。 这里esi 就是SAFEARRAY的地址,而esi+0c是pvData的地址。eax值就是元素相对于pvData的偏移。查看eax值

30.jpg

为什么A(1,2)的偏移会是0×50呢。 VBScript的数组内存布局和C语言有些不太一样。A数组的内存布局为 A(0,0) A(1,0) A(0, 1), A(1,1), A(0, 2), A(1,2),  而每个元素大小为0×10字节(cbElements)。因此 A(1,2) 的偏移为0×50。

2.3. CVE-2016-0189 漏洞原理初探

CVE-2016-0189 的漏洞在AccessArray 函数代码中。我们看一下这个函数的逻辑:

 3.jpg

这个函数在根据索引计算偏移时,如果索引是VT_I2 或者VT_I4时,直接使用它的值。而如果是其它类型时候,将会调用rtVariantChangeTypeEx 函数来计算索引值。如果索引是JavaScript对象的话,将会调用索引对象的valueOf函数来获取索引的值。

这似乎并没有太大问题,然而我们看一下漏洞利用脚本的valueOf函数

 4.jpg

除了返回值”1”因为, 它调用了SumggleFag函数

查看该函数的代码可以发现:它调用了ZeroineL函数,而该函数将数组重新定义成了Cod(1,1)  (k1=1)

 5.jpg

 6.jpg

到这里,我们明白了该漏洞的原理了,在AccessArray中计算索引值的时候,索引如果是个JavaScript对象,可以通过函数valueof来重新修改该数组的大小!  当数组的大小变小时,将会导致访问越界。

3. CVE-2016-0189 漏洞利用深入分析

Nebula EK的漏洞利用在越界访问的基础上实现了:

1. 泄漏VBScript对象的地址

2. 读取任意地址

3. 写入任意地址(受限)

这3种手段在实现原理上都是类似的。通过将数组定义 Cod从(1,2000), 缩小到(1,1),从而在AccessArray 函数中实现了越界访问。同时,立即使用大量y(i)  对释放掉的内存重新进行占位。由于y(i)是可控的,如果越界访问到y(i)的内存区,即可控制对内存的访问。

下面我们利用windbg对内存进行观察,分析1.泄露VBScript对象地址的情形。

需要注意的是,由于脚本中有大量数组操作,如果我们在AccessArray函数上下断点,可能会断下很多次。这里介绍一个小技巧:先将断点用bd命令禁掉,在想断下的VBScript 脚本前加入MsgBox函数,当MsgBox弹出后,我们再把断点用be命令恢复。这样就可以确保断在我们想断的地方。

为清晰起见,我们将在脚本中将aw.Cod(0,0)设成0×11223344。

利用该技巧,我们在vbscript!AccessArray+0x9d断在

 aw.Cod(arg1, 2) = s // arg1.valueOf() == 1 , s 是我们试图泄露的对象地址

这行越界访问(写入)处。

断下时,

31.jpg

0x2a659d0 就是 aw.Cod数组的pvData 地址,03 00 两个字节是vbLong (VT_I4), 而 44 33 22 11恰好为我们赋的值。 

前面已经分析过aw.Cod(arg1,2), 即 aw.Cod(1,2), 该元素相对pvData的偏移就是0×50, 由于aw.Cod现在被重新设定为(1,1),所以 大小为2*2*0×10=0×40, 也就是说 awCod(arg1,2) 越界了0×10个字节。

aw.Cod(1,2) 的地址为 2a659d0 + 0×50 = 0x2a65a20。而从地址0x2a65a1c起(标为绿色的部分)是某个成功占位y(i) 的值。内存布局如下图所示:

32.jpg

由上图可知,Aw.cod(1,2) 正好与y(i)的第四个字节开始的部分重叠。

 7.jpg

了解了这点。上面这段代码就不难理解了–遍历 y的所有元素,找到与Aw.cod(1,2) 重叠的那个y(i).  由于s 被写入了Aw.cod(1,2), 即 yi(i) 的第四个字节开始的部分。那么Mid(y(i), 3, 1)

正好是第四个字节开始的2个字节(注意Mid函数下表是从 1 开始的),它存放的是对象的类型的值。 4个字节之后,则是对象的地址。这样,只须读取y(i)的内容,就可以获取对象的地址了。

概括起来,Aw.cod 和 某个y(i)  重叠,我们可以通过Aw.cod的越界操作来将数据写入可控的y(i)里。

下面讨论情形2 读取任意地址

 8.jpg

这次我们在占位的内存中,成功构造如下数据:

33.jpg

Aw.cod(1,2) 将越界访问到 我们构造的占位内存 地址0x2f72878。

08 00 为vbString类型VT_BSTR, 而48 c2 1b 01   四个字节则为试图读取的地址。

当执行 o = aw.Cod(arg1,2)时, VBScript引擎将会把地址0x2f72878作为一个vbString对象来处理。而48 c2 1b 01   四个字节则作为字符串内容的地址来处理。

读取该字符串的内容则获取了 48 c2 1b 01    地址里存放的内容。

最后我们看一下写入任意地址(受限)的情形。

 9.jpg

写入与读取原理大致一样。这里只是将CSng(0) 赋值给了 aw.Cod(1,2),从而导致 CSng 的类型值 4(VT_R4) 被写入了指定地址。这里脚本并未实现任意值的写入,然而并不影响漏洞的利用。

最后我们看一下,整个漏洞利用是如何工作的:

1) 首先,创建一个 对象,通过上述的方法(1) 泄露该对象的地址;

2) 在该对象偏移为8的地方,利用方法(2)读取内存获取CSession对象指针;

3) 然后在CSession对象偏移为4的地方,获取COleScript 对象;

4) 而COleScript 对象0×174的地方则存放了SafetyOption,也就是所谓的上帝模式(GodMode)标记。利用上述方法(3)将其值修改为4,从而成功开启上帝模式。

4. 一个有趣的DLL劫持技巧

分析本来到这里可以结束了,然而我们在Nebula的漏洞利用包里发现了一个有趣的DLL劫持技巧。尽管在前面,上帝模式已经成功开启了,当IE的保护模式开启时(默认是开启的),Vista以后的版本在执行Shell.Applicaion对象的ShellExecute 函数时, 会弹出一个如下的对话框(除非被加载的程序在ElevationPolicy中定义了静默加载,比如notepad.exe),向用户询问是否允许。正是由于这个原因,一些漏洞利用包选择了仅在XP系统上对CVE-2016-0189 进行利用。

 10.jpg

Nebula EK采用了一个BlackHat 2014[2]上 公布的技巧来规避这个问题。IE浏览器在创建Shell.Application 对象时,会加载%systemroot%\system32\shell32.dll。通常情况下%systemroot%为c:\windows目录。然而,攻击者可以通过如下方式进行DLL劫持攻击。

1) 在%temp%\ 目录下创建system32目录

2) 在上面创建的目录下生成一个假的shell32.dll文件,用来加载需要加载的程序。Nebula EK 在VBScript脚本中会生成一个3K左右的文件。

3) 在脚本创建Shell.Application对象之前,利用WScript.Shell 来 修改 %system32%环境变量,指向1)创建的目录

4) 创建Shell.Application 对象,从而自动加载 假的shell32.dll文件,达到执行文件的目的。

 相应的代码片段如下。

 11.jpg

需要指出的是,通过这种方式加载的进程是低完整性(Low Integrity)的。通常被加载的程序还需要通过本地漏洞来进一步提权。

5. 总结

Nebula EK 通过CVE-2016-0189的一个漏洞,实现了对任意内存的读写,从而开启了上帝模式。并且配合一个DLL劫持技巧,达到了绕过IE保护模式,静默执行任何文件的目的。

6. 参考文献

[1] PATCH ANALYSIS OF CVE-2016-0189. http://theori.io/research/cve-2016-0189

[2] https://msdn.microsoft.com/en-us/library/aa263402(v=vs.60).aspx

[3] 启明星辰CVE-2014-6332分析报告。http://www.venustech.com.cn/UserFiles/20141210_%E6%97%A0%E9%9C%80%E6%8B%85%E5%BF%83%E6%BD%9C%E8%97%8F%E4%BA%8618%E5%B9%B4%E7%9A%84%E5%BE%AE%E8%BD%AF%E6%B5%8F%E8%A7%88%E5%99%A8%E8%BF%9C%E7%A8%8B%E4%BB%A3%20%E7%A0%81%E6%89%A7%E8%A1%8C%E6%BC%8F%E6%B4%9E.pdf

[4] Write Once, Pwn Anywhere.

https://www.blackhat.com/docs/us-14/materials/us-14-Yu-Write-Once-Pwn-Anywhere.pdf

*原创作者:兰云科技银河实验室