首页
社区
课程
招聘
[原创]CVE-2016-0189 vbs脚本引擎损坏漏洞分析
发表于: 2018-6-9 15:31 21036

[原创]CVE-2016-0189 vbs脚本引擎损坏漏洞分析

2018-6-9 15:31
21036

CVE-2016-0189是一个vbscript脚本引擎损坏漏洞,最初作为一个0day被用在针对韩国的APT攻击中,并在2016年3月10号于MS16-051中被修复。3个月后,国外安全人员通过补丁比对分析了漏洞成因,并将利用代码上传到Github。从此0189便被广泛纳入各种挂马,在今年的CVE-2018-8174出现之前,CVE-2016-0189一直是较新版本IE的挂马首选。

由于之前在调试这个漏洞时发现参考资料很少,特别是国内只看到一篇文章讨论这个漏洞,于是决定把调试过程写一下,不足之处请见谅。

在vbs解析引擎中,vbscript!AccessArray函数用来访问数组成员。例如访问一个二维数组A的成员A(1,1)时,vbs解析引擎就会调用这个函数,根据传入的索引计算待访问的地址。

2015年Hacking Team的泄漏中有两个通过重载valueof函数来触发的flash 0day CVE-2015-5119 / CVE-2015-5122。这两个漏洞的思路是当赋值方是个对象,而被赋值方出于某种原因要求接收一个数值时,会调用该对象的valueof方法进行转换,而valueof方法是可以被重载的,这样就可以在重载的valueof函数中进行一些自定义的操作,例如释放对象,改变数组大小等。

vbscript!AccessArray函数内,当访问语句为如下形式时,就有可能进入上面的情景,而事实确实如此。

具体的逻辑如下图所示,cp_var_index->vt代表索引变量的类型,当索引类型为VT_I2VT_I4时,直接返回该对象的值,而其他情况下会调用vbscript!rtVariantChangeTypeEx函数,并在里面调用oleaut32!VariantChangeTypeEx函数,随后会调用对象的valueof方法。


我们来看一下 VT_I2VT_I4 代表什么:

The value of a VT_I2 type property MUST be a 2-byte signed integer. It MUST be formatted in little-endian byte order.

The value of a VT_I4 type property MUST be a 4-byte signed integer. It MUST be formatted in little-endian byte order.

当传入索引是有符号短整型和有符号长整型型时,就直接使用其值,而当索引为其他类型时(例如当传入对象为js对象时,变量类型为VT_DISPATCH),就调用vbscript!rtVariantChangeTypeEx函数将对象值转化为要求的类型,最终通过调用valueof方法返回该值。

如果我们在重载的valueof内改变数组的大小,当返回上层继续访问数组元素时,就会产生问题。poc中的思路是:先定义一个比较大的二维数组A(1, 2000),然后通过访问A(js_obj, 2)去调用重载的js_obj.valueof()方法将一维索引转化为合理的数组下标。在js_obj.valueof()内将数组缩小为A(1, 1),然后迅速用UAF进行占位,并在valueof方法的最后返回1,作为转换后的索引。转换完成后访问A(1, 2) 。然而这时候的A(1, 2)已经变成了占位后的内存。攻击者多次利用这一特性来操控内存,分别实现泄漏一个类对象地址,任意地址读,和任意地址写。在此基础上找到vb的安全选项开关,利用一个单精度浮点数(vbSingle)的类型值(4)去覆盖原来的安全开关属性值(0x0E -> 0x?4),从而开启上帝模式。

vbscript.dll内判断对象安全性的函数是COleScript::InSafeMode,汇编代码如下图所示。可以看到test指令对 dword ptr [ecx+174] 的值与0x0B(00001011)进行与运算,若结果为0,即认为不处于SafeMode,放行对象执行。

windows 7 sp1 x86 无补丁 + vbscript.dll/oleaut32.dll 5.8.7601.17514 + windbg 6.11 x86

poc的主入口为exploit函数,原代码的注释写的很清晰,可以看到exploit函数分为5个步骤:

getAddr函数的逻辑又可以分为如几步:

遍历y数组,通过对比VarType找出s对象(s是一个空class实例)并返回其地址

VBScriptClass继承了NameTbl,其头部是一个NameTbl结构体,NameTbl结构体偏移0x08处的值是一个指向NameList结构体的指针,NameList结构体偏移0x2C处是一个CDISPIDTable结构体,而CDISPIDTable结构体偏移0x08处的值是一个指针数组,每个数组元素都指向一个VAR结构体,代表一个类成员在类对象中的实体,整个关系如下所示:

先来看一下resize后的aw.A:

再来看一下占位成功后的y数组情况:

下图分别以aw.A视角(红色框区域)和y视角(黄色框区域)看s对象,不难发现有4字节的错位:


理解上图之后,我们就可以根据条件查找包含s对象的y数组成员,调试时恒为y(0)。poc里面申请32次内存是确保释放后的内存一定被y中的某个成员再次使用,进而从32个成员里面找出包含s对象的那个成员。

泄漏了s对象的地址后,我们通过相关数据结构去获取vbscript!SafetyOption的内存地址,并将其改写为0x00或0x04。查找过程如下:

下面对此进行说明。

poc中读取CSession对象指针的代码如下所示:

由前面的分析已知addr是一个VBScriptClass实例指针,我们可以看到代码把addr+8的地址传入leakMem函数,并通过Mid(mem, 3, 2)将CSession对象指针获取出来并转换为16进制。

为什么上述代码不写成如下形式?

为了回答这个问题,我们先来看一下leakMem函数的实现:

leakMem的基本逻辑是在占位内存中构造一个字符串地址(Data High)为待读取地址的字符串对象,然后再次利用漏洞触发UAF,从而使aw.A(1, 2)处改写为一个VT_BSTR对象,随后读取该对象,从而使addr处的数据被当做定长字符串读出,最后在读取的字符串中定位CSession指针对应的部分并转化为对应的32位地址。

图片出处

我们来回顾一下BSTR对象的结构:

图片出处

关键点在于字符串前面的4字节,这4字节是一个长度域,指定了后面待读取的unicode字符串长度。现在再来看一下前面问题的那个问题。

这是本次调试中getAddr函数返回的VBScriptClass实例:

原poc中传入的是addr+8,那么可以构造出如下的BSTR结构:

这样可以成功读取包含Csession地址的字符串。

如果代码这样写:

但当传入addr+c时,构造的BSTR如下:

此时构造的BSTR的长度域为0,数据无法正常读出。

poc中读取COleScript对象指针的代码如下,原理和上一步完全相同,此处不再过多分析:

在IE8中,vbscript!SafetyOption位于COleScript对象的+0x174处。poc代码中通过调用overwrite函数去覆写这一值,如下:

代码中伪造一个type=0x400C间接寻址对象,接着触发漏洞,随后将一个Csng(单精度浮点)对象的写入COleScript+0x16C开始的16个字节处,COleScript+0x174处正好写入Csng的type值4,从而开启上帝模式。

开启上帝模式后,就可以弹出cmd窗口了。

下面通过逆向某安全软件来看一下对CVE-2016-0189的动态检测方案。

首先hook oleaut32!VariantChangeTypeEx函数

可以看到代码中对CVE-2016-0189的检测逻辑为:在调用oleaut32!VariantChangeTypeEx函数前后检查rgsabound[0].cElements所对应的第二维度的大小,若调用后的大小小于调用前的大小,则视为检出。

这里有一个疑问,MSDN对多维数组的rgsabound域解释.aspx)如下:

但调试时发现A(1, 2000)的rgsabound实际使用顺序和文档描述相反,看检测逻辑里面判断的也是rgsabound[0]->cElements。我们以实际调试结果为主。

特别感谢 Hu JiangXu XilinYang Kang 在调试过程中的指导

《CVE-2016-0189》 https://theori.io/research/cve-2016-0189

《theori-io/cve-2016-0189》 https://github.com/theori-io/cve-2016-0189

《Nebula漏洞利用包CVE-2016-0189漏洞利用分析》 http://www.freebuf.com/sectool/131766.html

《WinDbg 漏洞分析调试(三)之 CVE-2014-6332》 https://paper.seebug.org/240/

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

 
 
 

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2019-3-26 13:08 被银雁冰编辑 ,原因:
上传的附件:
收藏
免费 1
支持
分享
最新回复 (7)
雪    币: 2083
活跃值: (919)
能力值: ( LV4,RANK:45 )
在线值:
发帖
回帖
粉丝
2
support~~~感谢冰神分享
2018-6-9 16:35
0
雪    币: 3907
活跃值: (5817)
能力值: ( LV12,RANK:200 )
在线值:
发帖
回帖
粉丝
3
其实看雪论坛现在发md非常方便,可以尝试一下。
2018-6-10 18:32
0
雪    币: 984
活跃值: (104)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
方便告知一下这是哪一家的安全软件吗
2018-8-16 16:46
0
雪    币: 6064
活跃值: (12624)
能力值: ( LV12,RANK:312 )
在线值:
发帖
回帖
粉丝
5
2020-12-29 17:12
0
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
前辈,为什么是将 Csng (单精度浮点)对象写入 COleScript+0x16C 开始的 16 个字节处啊?我调试时发现它是写在 COleScript+0x174 开始的 16个字节处啊。
2023-2-24 16:45
0
雪    币: 9662
活跃值: (4588)
能力值: ( LV15,RANK:800 )
在线值:
发帖
回帖
粉丝
7
猫头鹰111 前辈,为什么是将 Csng (单精度浮点)对象写入 COleScript+0x16C 开始的 16 个字节处啊?我调试时发现它是写在 COleScript+0x174 开始的 16个字节处啊。
不好意思,回复有点晚。两种可能:1. 我的文章也许存在错误;2. 你的软件版本和我调试时略有差异。无论哪种情况,以你在调试器内看到的结论为准,毕竟调试器不会撒谎。
2023-3-20 22:15
0
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
银雁冰 不好意思,回复有点晚。两种可能:1. 我的文章也许存在错误;2. 你的软件版本和我调试时略有差异。无论哪种情况,以你在调试器内看到的结论为准,毕竟调试器不会撒谎。
好的,感谢。
2023-5-26 14:26
0
游客
登录 | 注册 方可回帖
返回
//