Quantcast
Channel: CodeSection,代码区,网络安全 - CodeSec
Viewing all articles
Browse latest Browse all 12749

Flash 0Day: CVE-2018-15982 Exploit复现

0
0
*本文原创作者:elli0tn0phacker,本文属于CodeSec原创奖励计划,未经允许禁止转载
0×00 背景

2018年12月5日,360发表博客《“毒针”行动 针对“俄罗斯总统办所属医疗机构”发起的0day攻击》披露了其在2018年11月29日捕获到的使用Flash 0day:CVE-2018-15982漏洞配合微软Office Word文档发起的APT攻击事件。文章从攻击流程,漏洞成因,漏洞利用,补丁分析,攻击载荷分析等多角度全面介绍了这次攻击的技术细节,非常值得大家仔细阅读学习。

也许是因为篇幅有限,文章关于漏洞成因和漏洞利用部分细节描述略简,阅读完文章后可能会有如下一些问题:

1)在没有符号表的情况下,如何定位存在漏洞的函数add_keySet;

2)DRCWB是什么;

3)漏洞利用部分动态调试情况;

4)HackingTeam Bypass DEP/CFG执行Shellcode的技术细节。

笔者在学习了这篇文章后,完成了该CVE的Exploit复现,在此记录下完整的分析过程,并尝试解释上述问题,与大家分享。但水平有限,文中错误之处恳请斧正。

0×01 Flash 调试

众所周知,Flash没有符号表,难以对相应的Native Code下断点。同时AVM拥有自己的Custom Heap,因此也无法通过开Page Heap第一时间定位漏洞现场。所以Flash的调试往往比有符号表的浏览器的调试略微复杂一些。在学习了前人的经验后,笔者一般通过如下方法调试Flash:

Flash编译器会将AS3 code编译成ABC code,在执行阶段AVM2会对每一段ABC code验证后生成对应的一段JIT code,在没有符号表的情况下,如果可以对生成的JIT code下断点,再单步跟踪就可以找到调用的Native Code了。

以调试Metadata::setObject为例,首先构造如下测试代码:


Flash 0Day: CVE-2018-15982 Exploit复现
这里将需要分析的setObject方法单独放在一个测试方法testMetadata()中,如果可以对testMetadata()下断点,那命中断点后单步跟踪就很容易定位到setObject对应的Native Code了。那如何对testMetadata()下断点呢,《漏洞战争》中提到了DbgFlashVul这个工具,可以对as3方法下断点:
Flash 0Day: CVE-2018-15982 Exploit复现

命中testMetadata() JIT后的代码,继续跟入,很快就找到了setObject的Native Code实现:


Flash 0Day: CVE-2018-15982 Exploit复现

这样就找到了setObject_impl,进一步跟入最终可以找到add_keySet的代码。

需要注意的是这个工具中inline hook的几个native 函数的特征码不同flash版本是不同的,因此需要根据flash版本更新 Hook函数的特征码才可以正常使用。

如果不熟悉这款工具的话,还可以通过另一种方法来定位JIT Code:借助已知符号表函数下断点,再根据Call Stack定位。AS3提供ExternalInterface.call(functionName:String,…arguments)方法来调用JS,而mshtml.dll和jscript9.dll的符号表是已知的,因此我们可以在需要断下AS3方法入口点插入类似的辅助调试语句:


Flash 0Day: CVE-2018-15982 Exploit复现
通过对已知符号函数下断点,命中断点后根据Call Stack也是可以找到JIT Code的入口点的。
Flash 0Day: CVE-2018-15982 Exploit复现
0×02 漏洞成因

CVE-2018-15982是一个存在于flash PSDK com.adobe.tvsdk.mediacore.metadata包的UAF漏洞。Metadata类提供了一个类似Key-Value的容器,Key为String,Value为Object。该漏洞存在于Metadata的SetObject方法, SetObject方法用来向Metadata对象存放数据:


Flash 0Day: CVE-2018-15982 Exploit复现
SetObject对应的Native实现如下:

1.传入Key(String)和value(Object),调用setObject_impl:


Flash 0Day: CVE-2018-15982 Exploit复现
2.setObject_impl内部会调用add_keySet将key(String)按索引保存至Metadata对象的keySet属性:
Flash 0Day: CVE-2018-15982 Exploit复现
3.add_keySet将传入的key(String)依次保存到KeySet中:
Flash 0Day: CVE-2018-15982 Exploit复现
AMV2中StringClass继承于MMgc::RCObject,根据MMgc文档,必须使用DRCWB宏来管理MMgc::RCObject对象指针,如果未使用,MMgc::RCObject的引用计数不会增加,GC后,String内存被释放,该对象指针变成一个悬挂指针:
Flash 0Day: CVE-2018-15982 Exploit复现

所以这个Bug的根本原因是:将String对象传递给KeySet后,没有增加String对象的引用计数,GC后String对象被释放,但是KeySet中仍然保存了该String对象的地址,最终形成悬挂指针。

触发漏洞的PoC如下:


Flash 0Day: CVE-2018-15982 Exploit复现

通过setObject向Metadata的KeySet属性存放了0×100个String(“0”, “1”, “2” … “255”),然后通过触发异常强制GC,最终array_key这个Vector.<String>中将包含指向已释放内存的String的悬挂指针, 动态调试如下:

1.通过setObject向Metadata的KeySet属性存放了0×100个String(“0”, “1”, “2” … “255”):


Flash 0Day: CVE-2018-15982 Exploit复现
2.GC后,KeySet包含了部分指向原String对象的悬挂指针:
Flash 0Day: CVE-2018-15982 Exploit复现
通过调试可以看到Metadata.KeySet指向一片连续的内存区域(从0x02b8ddb0开始),保存了key(String)的指针(这和我们之前静态分析add_keySet函数的逻辑是一致的),GC后因为String在保存到Metadata.keySet内存区域的时候没有通过DRCWB宏增加引用计数,导致String被释放,从而Metadata.keySet中保存了一系列指向原来String的悬挂指针(如动态调试中的KeySet [10],KeySet [11])。通过图示的方法可以更加容易理解这个PoC的效果:
Flash 0Day: CVE-2018-15982 Exploit复现
0×03 漏洞利用

由漏洞原理分析可以知道,通过触发漏洞可以获得一个包含一系列悬挂指针的Metadata.KeySet,每个悬挂指针指向的内存大小为0×18 Bytes(StringClass大小)。接下来考虑如何占位并利用这些悬挂指针实现远程代码执行。

这里的利用思路是:利用UAF将两个自定义类Class3,Class5的对象指针都指向这块内存,通过操作这两个对象实现类型混淆,从而进一步实现任意地址读写,混淆方法如图所示:


Flash 0Day: CVE-2018-15982 Exploit复现

这样就可以通过Class5的m_1, m_2读取Class3的m_ba, m_Class1地址,也可以通过操作Class3.m_Class1.m_1实现任意地址的读写。

具体实现步骤如下:

1.内存准备,触发漏洞,获得一个包含一系列悬挂指针的KeySet,并保存到变量arr_key中,为后面遍历占位对象使用:


Flash 0Day: CVE-2018-15982 Exploit复现
Flash 0Day: CVE-2018-15982 Exploit复现
2.使用0×100个大小为0×30 Bytes的Class5对象(this.vec5[i])尝试占位空洞内存,根据MMgc,创建一个Class5对象可能会重用两个已经释放的String内存(0×18*2=0×30 Bytes):
Flash 0Day: CVE-2018-15982 Exploit复现
因为String.length和Class5.m_1都在偏移0×10处,可以通过遍历arr_key[i].length来判断是否找到占位Class5。最终获得一个悬挂指针 array_key[i] -> this.vec5[?] -> Class5,占位的Class5在this.vec5[?]中的索引待定。
Flash 0Day: CVE-2018-15982 Exploit复现
3.释放array_key指向的内存,此时this.vec5[?]成为悬挂指针,再使用0×100个大小为0×30 Bytes的Class3对象尝试第二次占位(this.vec3[i]),Class3占位成功后,this.vec5[?].m_1会指向Class3.m_ba,通过遍历this.vec5找到这个被Class3占位的this.vec5[?],标记为index_1,通过this.vec5[index_1]就可以操作占位的Class3的内存:
Flash 0Day: CVE-2018-15982 Exploit复现
Flash 0Day: CVE-2018-15982 Exploit复现
4.通过3获得的this.vec5[index_1]修改占位的Class3的m_Class1为m_ba,通过遍历this.vec3[i].m_Class1.m_1找到这个占位的Class3即this.vec3[?],标记为index_2,从而this.vec3[index_2]同样指向这块内存:
Flash 0Day: CVE-2018-15982 Exploit复现
5.这样就得到了指向同一片内存的两个指针this.vec5[index_1],this.vec3[index_3],通过操作这两个指针就可以实现Class5和Class3的类型混淆,从而实现任意地址读写:
Flash 0Day: CVE-2018-15982 Exploit复现
6.利用任意地址读写,搜索PE的IAT、EAT,泄露VirtualProtect地址
Flash 0Day: CVE-2018-15982 Exploit复现
Flash 0Day: CVE-2018-15982 Exploit复现
7.劫持ExecMgr::call虚函数调用,替换成VirtualProtect,Bypass DEP和CFG:
Flash 0Day: CVE-2018-15982 Exploit复现
Payload.call会在Native层调用FunctionObject::AS3_call,AS3_call内部会调用ExecMgr::call,ExecMgr::call的函数调用与VirtualProtect类似并且ExecMgr::call这个间接调用不受CFG保护,因此通过劫持这个虚函数调用来执行VirtualProtect:
Flash 0Day: CVE-2018-15982 Exploit复现
8.通过劫持Payload的JIT Code指针,替换成Shellcode,调用Payload.Call(null) 最终执行Shellcode:
Flash 0Day: CVE-2018-15982 Exploit复现
最终效果:
Flash 0Day: CVE-2018-15982 Exploit复现

Viewing all articles
Browse latest Browse all 12749

Latest Images

Trending Articles





Latest Images