Nebula漏洞利用包CVE-2016-0189漏洞利用分析

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 的首页,如下图所示。

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

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

关于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]。

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

图1 调试用VBScript代码

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

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

2.2. VBScript的数组

VBScript 的数组的定义如下

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

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

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

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

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

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

为什么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 函数代码中。我们看一下这个函数的逻辑:

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

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

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

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

到这里,我们明白了该漏洞的原理了,在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 是我们试图泄露的对象地址

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

断下时,

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) 的值。内存布局如下图所示:

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

了解了这点。上面这段代码就不难理解了–遍历 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 读取任意地址

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

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 地址里存放的内容。

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

写入与读取原理大致一样。这里只是将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 进行利用。

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文件,达到执行文件的目的。

相应的代码片段如下。

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

5. 总结

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

参考文献

[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

原文发布于微信公众号 - FreeBuf(freebuf)

原文发表时间:2017-04-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏大内老A

.NET Core采用的全新配置系统[2]: 配置模型设计详解

在《.NET Core采用的全新配置系统[1]: 读取配置数据》中,我们通过实例的方式演示了几种典型的配置读取方式,其主要目的在于使读者朋友们从编程的角度对.N...

18810
来自专栏Java编程技术

UML建模(时序图)

时序图是一种强调消息时序的交互图,他由对象(Object)、消息(Message)、生命线(Lifeline)和Combined Fragments组成,它主要...

6502
来自专栏SnailTyan

Python的命令行参数解析

命令行参数解析在编程语言中基本都会碰到,Python中内置了一个用于命令项选项与参数解析的模块argparse。下面主要介绍两种解析Python命令行参数的方式...

2690
来自专栏noteless

[二十五]JavaIO之RandomAccessFile

1443
来自专栏Java帮帮-微信公众号-技术文章全总结

JavaWeb10-request&response你不得不学(1)

? request&response 一.request和response的介绍 1. request和response的作用执行流程 Web服务器收到客户端...

7864
来自专栏互扯程序

java中的内存模型

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。

1744
来自专栏玄魂工作室

看代码学安全(8 )preg_replace函数之命令执行

--------------------------------------------------------------------------------...

2453
来自专栏JAVA技术zhai

并发编程的艺术

关于 Java 并发也算是写了好几篇文章了,本文将介绍一些比较基础的内容,注意,阅读本文需要一定的并发基础。

1372
来自专栏java一日一条

Java 多线程同步的五种方法

因为当我们有多个线程要同时访问一个变量或对象时,如果这些线程中既有读又有写操作时,就会导致变量值或对象的状态出现混乱,从而导致程序异常。举个例子,如果一个银行账...

1732
来自专栏更流畅、简洁的软件开发方式

数据访问层的使用方法

数据访问层的使用方法。 数据访问层的使用方法 一、操作语句部分 简单的说就是传入一个操作语句,然后接收返回值就可以了。为了简化代码和提高效率,所以呢设置了五种返...

3358

扫码关注云+社区

领取腾讯云代金券