Adobe在野漏洞:CVE-2016-4117漏洞分析

阅读量214022

|

发布时间 : 2016-08-01 12:53:04

https://p2.ssl.qhimg.com/t01e43a8d4d09eb4fc0.png

author:大宝@360天眼安全实验室

在今年五月份,国外安全专家发现了一个未知的Adobe漏洞在野外被利用。在该漏洞被披露后,Adobe发布了一个升级补丁用于修复此漏洞(APSB16-15),编号为CVE-2016-4117。同时,CVE-2016-4117漏洞被列为高危漏洞,在CVSS Score中被评为10.0,它同时影响到Windows,Mac OS X,Linux和Chrome OS。Adobe在发布的漏洞信息中提到,“Windows、mac、Linux和Chrome OS中的Adobe Flash Player 21.0.0.226和早期版本中存在一个高危漏洞,成功利用该漏洞可能会导致系统崩溃,甚至攻击者可以控制受影响的系统。”


0x0漏洞简介

CVE-2016-4117是出现在ActionScript的com.adobe.tvsdk.mediacore.timeline.operations.DeleteRangeTimelineOperation类中的一个类型混淆漏洞,最终可能导致远程代码执行,在今年五月份的时候首次出现野外样本。在此类中,存在以下两个get,set的接口(图0-0),名称为placement,假如我们以此类作为基类,在子类中创建一个相同名称的object(图0-1),avm虚拟机在解释的过程中会出现错误,从而引起类型混淆。

http://p3.qhimg.com/t01d877fdbc402d3b45.png

                               (图0-0)

http://p7.qhimg.com/t016b6a6e1496ede16b.png

(图0-1)

0x1 漏洞相关知识

要深入了解这个漏洞的深入原理,首先我们要了解avm虚拟机解释字节码getproperty(0x66)的逻辑流程。adobe在github上公布过avm虚拟机的源代码,并且有一些参考文档,尽管这是3年前的代码,但是还是可以作为参考。

首先我们可以查看getproperty这个指令的简介(图1-0):

http://p3.qhimg.com/t0173121196a4461982.png

                           图(1-0)

简单来说就是从一个object中根据后面的index取出某个属性。我们再看源代码的实现:

http://p8.qhimg.com/t01935f38a46598d38a.png

                         图(1-1)

getBinding是根据我们的属性名字,最后返回一个bind ID值,然后会根据这个值取出不同的函数或者字段的值。这个ID的值由两部分组成,分别是低三位的bit和其余的bit,低三位的bit保存的是这个property的类型,其余的bit保存这个ID真正的值,枚举值如图:

http://p3.qhimg.com/t01f728f2ca70efa77b.png

                           图(1-2)

随后的bindingKind就是将这个ID值与7相与,也就是取出低三位的bit,然后switch表根据这个值进行不同的操作,这里与漏洞相关的有两个,一个是BKING_VAR(2),一般是object,uint等变量,一个是BKING_GET(5),对应get的接口,首先我们查看BKING_VAR:

http://p6.qhimg.com/t01db8f690123e83bc4.png

                          图(1-3)

BindingToSlotId是右移三位,得到真正的ID值,然后根据这个ID值取出真正的value,getSlotAtom的逻辑也很简单,比如ID值是0x5,就取出对象偏移0x5*4的value,当然还会进行类型的判断,如这个类型是double型则取出8字节,如果是int型,则取出4字节。对比的汇编指令如下:

http://p8.qhimg.com/t016a4e0436ad4ead62.png

                        图(1-4)

然后我们再看BKING_GET:

http://p7.qhimg.com/t0160035fac0f5480ed.png

                         图(1-5)

跟进coerceEnter函数:

http://p6.qhimg.com/t014321cca96bdfe247.png

                         图(1-6)

这段代码比较复杂,首先解释几个名词:

Vtable:是一个保存as层面(不是native层面)虚函数列表的对象,里面的methods数组保存不同虚函数对象的对应的MethodEnv,一般保存在object的+0x8偏移的位置。

MethodEnv:在其+0x8保存MethodInfo对象。

MethodInfo:在其0x8 保存虚函数将要调用的函数指针。最终会调用这个函数指针从而调用get or set接口。

这段代码首先右移三位,得到真正的ID值,然后以这个ID值作为索引从methods数组中取出对应虚函数的MethodEnv对象,再调用对应的get or set接口。

对应的汇编指令如下:

http://p6.qhimg.com/t01b4ae69c571b0dcac.png

                              图(1-7)

   另一个需要了解的就是byteArray的数据结构,根据不同版本,在byteArray对象的+0x40 or +0x44 or +0x48会有一个m_buffer,而m_buffer的+0x8保存了真正保存数据的array,+0x10是length。具体如图所示:

https://p3.ssl.qhimg.com/t01faa02a889c98ee50.png

                             图(1-8)

0x2 漏洞触发分析

实验环境:Windows 7 64 bit 企业版cn

Adobe Flash:flashplayer21_0r0_213_win_sa_debug.exe

根据捕获的样本分析得出,漏洞触发的关键代码如下:

https://p4.ssl.qhimg.com/t01850240f244e5c355.png

                               图(2-0)

通过flash90.palcement语句从而会在AS虚拟机层面进入getPlacement(该部分没有源代码,暂时命名)函数,是触发漏洞的关键所在,伪代码如下:

http://p6.qhimg.com/t01850240f244e5c355.png

                              图(2-1)

首先会进入Toplevel_getproperty函数(也就是图1-1的函数),该函数没有漏洞,因此所有代码运行正常,最终返回placement在内存中的值0xd。我们知道placement等于true,也就是0x1,但是在as虚拟机保存变量的机制中,末尾三位bit也是保存类型的,官方文档如下:

http://p9.qhimg.com/t0187f311fa2b72e3ef.png

                             图(2-2)

因此true的值是1<<3 or 101 =0xd。返回值0xd大于0x4,所以跳过if语句,进入下面的漏洞触发代码部分:

http://p8.qhimg.com/t015655aadcb60c5000.png

                                图(2-3)

我们可以看到这里调用了getBinding函数,然后返回0xe2:

http://p4.qhimg.com/t01d111b7125702061a.png

                               图(2-4)

ID值0xE2如果与7相与等于0x2(因为我们已经混淆成object类型,本来是get接口),对应图(1-2),正确的流程应该是进入图(1-3)的代码,取出placement的值0xd。但是这里并没有进行任何判断,就进入图(2-3)下面的代码,我们对比和图(1-7)的代码:

http://p1.qhimg.com/t01cb92e6d99c840ba1.png

我们发现这两处地方的代码是一样的,这里是进入了case 5和7的处理流程,从Vtable中取出MethodEnv然后取出虚函数地址并且调用。但是我们应该进入case 2的处理环节的,却进入了case5和7的处理流程,我们可以查看修补该漏洞后的代码:

http://p1.qhimg.com/t01773e1da8cd5293b2.png

                             图(2-5)

修补后的代码会对getBinding后的ID值的类型进行判断,如果不是0x5的类型,则进入错误处理流程,从而避免了其他ID值的类型进入了0x5类型的处理流程。下面我们来分析如果我们把这个0x2类型的ID值进入0x5类型的处理流程会产生什么严重的问题。

http://p0.qhimg.com/t015a1a0e2d0af855a8.png

                                图(2-6)

将ID值右移三位得到0x1c,该值是placement属性在flash90对象中的偏移值(44+1c*4=b4):

http://p9.qhimg.com/t013cb2445c0ee34e87.png

                                图(2-7)

但是该值被用于从Vtable对象的MethodEnv数组中作为index值取出MethodEnv元素(图2-6中的edx+0x3c是数组的开始位置),查看Vtable对象的内存:

http://p0.qhimg.com/t01c87417fb10a1af0c.png

                             图(2-8)

Vtable对象的大小是0x60,0x1C并不是一个合法的值,经过计算后,最终会越界从下一个相邻的Vtable对象中读出MethodEnv元素。而这个Vtable对象是属于另一个对象类型Data5的,查看Data5的内存:

http://p8.qhimg.com/t010c1d0fd065a90322.png

                              图(2-9)

https://p5.ssl.qhimg.com/t01de868e2cd61d2756.png

                                图(2-10)

Data5对象的+0x8保存的Vtable对象等于0x39cbf40,与图2-8相符合,第一个属性等于0xab4130等于十进制的11223344,与图2-10相符合,最终判定该对象就是Data5对象。所以最后取出的虚函数指针是Data5对象的f2虚函数指针:

http://p8.qhimg.com/t0176a39dbd481eccbf.png

                                  图(2-11)

但是这里的this指针却变成了flash90的指针,根据源码(图2-12),我们查看进入虚函数指针前第三个参数的内存区域:

http://p3.qhimg.com/t0160d3299628648115.png

                                    图(2-12)

https://p4.ssl.qhimg.com/t01352048672dbb807b.png

                                图(2-13)

换句话说,在f2函数上所有的一切操作的this指针都不是指向Data5对象的内存区域,而是指向flash90的内存区域,最终引起类型混淆漏洞。由于Data5对象里面有很多属性,占用的内存空间会比flash90对象大很多(图2-14),最终可以对flash90内存区域进行越界读写,只要在flash90对象的下一个相邻位置放入一个byteArray,即可以在f2函数中对这个byteArray的关键数据区域修改,得到一个长度超长的byteArray。

http://p0.qhimg.com/t01cb8f43939a6c6bed.png

                              (图2-14)

0x3 漏洞利用分析

漏洞利用的过程中,主要有几个问题:

3.1 怎样令Data5对应的Vtable对象刚好分配在Data4对应的Vtable对象的下一个相邻位置。

    第一步:Vtable对象的大小是由一个对象的虚函数数量决定的,虚函数越多,MethodEnv数组越大,Vtable对象也越大。目标Vtable对象是0x60,样本中通过添加5个虚函数,加上本来存在的3个虚函数,最后得到0x3c+8*4=0x5c,再经过内存对齐就是0x60:

http://p7.qhimg.com/t0193d0a6018c87ef88.png

                                    图(3-0)

     第二步:增加几个继承Data5的类Data6,Data7等等,然后在运行的过程中new出来,最后在内存中即可分配多个同样大小的Vtable对象。

http://p4.qhimg.com/t01a6884e356206fd85.png

                                    图(3-1)

3.2 怎样令虚函数f2对应的MethodEnv刚好对应0x1c*4+0x3c+Vtable_Add

这个则十分简单,根据在内存中的偏移,在f2虚函数前增加若干个虚函数,样本中是1个。然后f2对应的MethodEnv就会刚好在0x1c*4的位置。

http://p1.qhimg.com/t01405ba3209d8a3a6e.png

                            图(3-2)

3.3 如何令flash90后面紧跟一个byteArray

通过heap feng shui技术:

http://p2.qhimg.com/t0163f2b73b4dfa3623.png

                        图(3-3)

Data3的构造函数如下图:

http://p6.qhimg.com/t01250c5a5bf17539c9.png

                         图(3-4)

最终内存布局如下图:

http://p9.qhimg.com/t01ee8edd700a5a8815.png

                           图(3-5)

进入f2函数后,首先进行一系列内存数据判断,找出byteArray关键数据区域的偏移值,因为版本和环境的不同,这些都可能不同的,因此为了兼容性,这里有必要进行判断:

http://p5.qhimg.com/t01f8580578ebc7c6dc.png

                                图(3-6)

(图解:a48的内存位置计算法方法是flash90_add+0x10(因为Data5在第一个字段前会有0x10的metedata)+48*4=0x66a500=0x123=291,根据上图的内存布局(图3-5),a57就是byteArray的index,a64就是byteArray的this指针,a50是m_buffer的地址。a56和a23只是用于检测是否byteArray对象)

获得与flash90对象相邻的下一个byteArray的引用,将byteArray的m_buffer结构地址修改成我们可以控制的内存区域,把真正m_buffer区域的数据赋值至这个内存区域,然后进入flash20函数。复制利用的技术是把atom伪造成Number类型(图2-2),然后就可以读出指定内存区域的数据。修改前m_buffer地址(这里的版本是0x48偏移)如图所示:

http://p1.qhimg.com/t01c8ef28d7f978a631.png

                                  图(3-7)

修改后的地址:

https://p3.ssl.qhimg.com/t01ad4abe53ea0fae8b.png

                                  图(3-8)

进入flash20函数后,将会修改m_buffer区域的length字段,最终构造超长数组,实现任意内存读写:

http://p2.qhimg.com/t0185349a230737caaa.png

                                   图(3-9)

m_buffer的地址已经修改成我们可以控制的内存区域,因此这里的ba.a0是对应m_buffer的+0x8位置的,从图3-7可以看到,m_buffer的+0x8位置等于0x11223344,而ba.a0的构造函数如图3-4所示。在最新版的flash中,在m_buffer的结构中引入了cookie字段,以防篡改length,例如在+0x20就保存了length的cookie,计算方法是length xor .Data字段的一个cookie值。在修改length的同时也需要把+0x20的length_cookie也要修改,不然在校验的时候就会出错。通过将a1(capacity)与a5(capacity_cookie)异或,可以求出.Data字段中的cookie值,然后将这个cookie值与0xffffffff放进a6(length_cookie),最终完成篡改m_buffer的length字段的所有步骤。至此,我们已经得到了一个超大的byteArray数组,可以进行任意内存读写。样本接下来的利用方法与Hacking Team的CVE-2015-5119基本雷同,公开的分析文章非常多,在这就不再详细叙述:

https://p1.ssl.qhimg.com/t0155b189893cfe279b.png

                                  图(3-10)

查找PE头,然后根据导入表结构查找VirtualProtect地址

http://p7.qhimg.com/t013ad259a61a3a9293.png

                                    图(3-11)

根据functionObject的结构,进行一系列读写操作,最终修改functionObject父对象的虚函数地址至VirtualProtect,在call.apply中会调用这个虚函数,从而调用VirtualProtect绕过DEP。

http://p1.qhimg.com/t01ff10e903f035c4c1.png

                                 图(3-12)

读取functionObject的MethodEnv地址,再读取MethodEnv中的MethodInfo地址,再修改MethodInfo的_implGPR变成shellcode地址,从而绕过CFG检测。在functionObject.call的时候,会跳进shellcode执行,最终实现远程代码执行。


0x4 样本行为分析

和众多样本一样,shellcode的作用就是一个网马下载器。在shellcode执行后,样本会从网上下载一个恶意的exe文件然后执行,运行该exe后,受害者主机将会完全被攻击者控制。

http://p4.qhimg.com/t011da6912a0561b411.png

本文由360天眼安全实验室原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/84311

安全客 - 有思想的安全新媒体

分享到:微信
+10赞
收藏
360天眼安全实验室
分享到:微信

发表评论

内容需知
  • 投稿须知
  • 转载须知
  • 官网QQ群8:819797106
  • 官网QQ群3:830462644(已满)
  • 官网QQ群2:814450983(已满)
  • 官网QQ群1:702511263(已满)
合作单位
  • 安全客
  • 安全客
Copyright © 北京奇虎科技有限公司 360网络攻防实验室 安全客 All Rights Reserved 京ICP备08010314号-66