首页
社区
课程
招聘
[转载]BlackHat专题:Flash漏洞利用样本逆向分析艺术
发表于: 2017-2-23 14:28 5781

[转载]BlackHat专题:Flash漏洞利用样本逆向分析艺术

2017-2-23 14:28
5781

原文链接:https://www.blackhat.com/docs/us-16/materials/us-16-Oh-The-Art-of-Reverse-Engineering-Flash-Exploits-wp.pdf

原作者:jeongoh@microsoft.com

译:xd0ol1 (知道创宇404实验室)

随着近来Java插件和Web浏览器在安全防护措施上的提升,攻击者们开始重新关注起Adobe Flash Player来,并将其视作主要的漏洞利用攻击目标。

多年来,借助Vector结构的corruption一直是实现Flash漏洞利用的首选方案。Vector是Adobe Flash Player中的一个数据结构,它以非常简洁的形式保存在native空间中,我们可以很容易的操纵此结构而不必担心其它字段会被破坏。而在引入Vector的长度保护后,攻击者们又转向了ByteArray结构的corruption(CVE-2015-7645)。

另一方面,CFG(Control Flow Guard)保护或者又叫CFI(Control Flow Integrity)保护是由Windows 8.1系统引入的,最新的Adobe Flash Player中也用到了此技术。对exploit开发者来说利用对象vftable的corruption已经是很常见的技术了,CFG就是针对此提出的缓解策略,它将在vftable中的虚函数调用前进行有效性的判断,如果调用未被确认则会退出进程。

分析Adobe Flash Player的exploit是一件非常具有挑战性的工作,由于缺少高效的字节码调试工具,这使得漏洞调试对安全研究人员来说简直就是一场噩梦,并且exploit的混淆处理通常都是一个单向的过程,任何试图反编译它们的行为都会产生警告。当然,确实也存在很多好用的反编译器,但它们通常在某些点上会执行失败,而且攻击者经常想出新的混淆方案来保护他们的exploit不被逆向。更糟的是除非你能获取源码,不然你还真没什么好的方法来验证反编译结果的准确性。由于反编译时的这种限制,在逆向过程中通常会用到多种分析工具及技术。

事实上,还是有许多针对SWF文件的商业版和开源版反编译器。其中,JPEXS Free Flash Decompiler是开源中较有用的反编译器之一,而对于商业版来说,Action Script Viewer的反编译结果要好得多。限制这些工具的根本原因在于SWF文件中存在大量的混淆代码,这使得反编译几近不可能或者结果中包含有严重的错误。此外,一些反编译器只给出了它们能生成的最好结果,但对可能的错误却从不提供警告。

下面为其中一款反编译器处理过程中产生的错误,当出现“unresolved jump”错误时,在这附近的反编译结果往往不是那么准确。

下列给出了错误发生处的反汇编结果,可以看到大部分是针对反编译器进行混淆的花指令。对未初始化寄存器用于产生带有垃圾指令代码块的情况,大多数反编译器还不能进行很好的识别。

另一种分析方法是借助反汇编器。RABCDAsm是一款非常强大的反汇编器,它可以从SWF文件中提取出AVM2(ActionScript Virtual Machine 2)中使用的ABC(ActionScript Byte Code)字段,并反汇编其中的字节码。更多有关AVM2的指令信息,请参考ActionScript Virtual Machine 2 Overview from Adobe

不过我们发现最新的Angler攻击包中会通过特定代码来实现SWF文件的反反汇编,例如给lookupswitch指令赋一个很大的case_count值但跳转地址中却不包含实际的代码,此方法就可用来对诸如RABCDasm这样的工具进行反反汇编处理。

下述为readMethodBody函数中针对此特定情况的补丁代码,它会过滤掉case_count值大于0xffff的所有lookupswitch指令。

同时,由于我们也可以通过RABCDAsm来编译AS脚本,所以如果在汇编文件中有发现恶意ABC字段生成的无效lookupswitch指令,我们也应该忽略它们。writeMethodBody函数中的补丁代码如下。

FlashHacker是一个开源的项目,它最初是基于ShmooCon 2012大会上提出的相关概念而开发的原型。在此之上我们进行了二次开发,使之可以对更多的AVM字节码元素进行插桩,并提供了更详细的过滤选项。在进行AVM字节码插桩时,其中的一大挑战是由于CPU密集型计算而导致的性能下降。例如,借助插桩代码进行的堆喷操作通常会由于Flash Player中的超时机制导致漏洞利用的失败。但我们仍然可以通过过滤这些CPU密集型计算的代码来执行精确操作,插桩技术通常适用于RCA(root cause analysis)分析以及我们最近进行的有关保护措施绕过方面的研究。

要是能获取当前分析程序的源码,那么这无疑很有优势。我们可以在AVMplus项目中查看AVM的开源实现,这对理解一些漏洞利用程序的操作会很有帮助,我们甚至发现一些利用程序直接使用了AVMplus中的代码,比如其中的MMgc实现部分。

此外,除非我们能获取Flash程序的符号信息,否则在native层对Flash漏洞或exploit的调试都将是极富挑战性的。

“read/write primitives”是指exploit中用于实现内存读写的对象或函数,现今的漏洞攻击通常需要借此来绕过ASLR或DEP等保护机制。而从防御者的角度来看,如果能知道exploit中所利用的RW primitives,那么将有助于弄清exploit是采用何种方式来绕过诸如CFG这样的保护措施。

自从CVE-2013-0634中引入Lady Boyle的利用方式后,对Vector结构的corruption事实上就成了Flash漏洞利用的标准,甚至一些IE的漏洞(CVE-2013-3163,CVE-2014-0322和CVE-2014-1776)也用到了此方法。有关IE中Vector结构的利用详情,可以参考Chun Feng和Elia Florio所发的文章

下述的CVE-2015-5122(TextLine的UAF漏洞)利用代码就是通过标准的Vector结构corruption来实现RW primitives,当把Vector.\和TextLine对象布局到内存中的相邻位置后,就可以触发use-after-free了。在此情况下,通过正常的Vector对象赋值操作就可将相邻Vector对象的length字段置为0x40000000。因此,这个corrupt后的Vector结构能被用作RW primitives。

在完成Vector和TextLine对象的喷射操作后,该exploit会将valueOf2赋给自身MyClass类中的prototype对象。

接着,当_mc变量赋给opaqueBackground时valueOf2函数会被调用。

此过程中FlashHacker的日志信息如下所示,可以看到Vector.\.length字段被置成了0x40000000。

在代号为DUBNIUM的行动中,我们发现CVE-2015-8651的利用样本通过对ByteArray.length字段的corruption来实现RW primitives,此技术是为了绕过Vector的长度保护而引入的。

在完成ByteArray.length字段的corrupt后,我们还需要找到受影响的那个ByteArrays元素。

下面给出的是此exploit提供的各个RW primitives方法,基本上能支持各个操作系统中的目标程序。

例如,read32x86方法可用于读取x86平台上任意进程空间的内容。其中,cbIndex变量是bc数组的索引,该数组为ByteArray类型,同时,bc[cbIndex]对应的正是那个corrupt后的ByteArray元素。首先需要通过position成员来设置目标地址,之后便可以使用readUnsignedInt方法读取此内存值。

write32x86方法也是相同的道理,它借助writeUnsignedInt来实现任意内存的写入操作。

基于这些,exploit也就能够完成一些更复杂的操作了,例如可以借助readBytes方法实现多个字节的读取。

CVE-2016-1010这个堆溢出漏洞存在于BitMapData.copyPixel方法中,相应exploit中用到的RW primitives是很有意思的,值得注意的是这些RW primitives功能将用于实现ByteArray对象的RW primitives,后面的内存读写也主要借助这个corrupt后的ByteArray对象。因此,最开始实现的RW primitives功能只起到了一个临时的作用,由之实现的ByteArray对象上的RW primitives功能才是主要的,因为就编程来说操作ByteArray对象会显得更直观些。

实现RW primitives功能的第一步为执行Convolutionfilter对象的喷射操作。

接着由copyPixels方法触发此漏洞后,exploit会通过调用TypeConfuseConvolutionFilter方法来创建一个类型混淆的ConvolutionFilter对象。

对于TypeConfuseConvolutionFilter函数,它将借助DWORD值0x55667788来标识corrupt后的内存区域,并借此定位堆喷对象中那个类型混淆的ConvolutionFilter元素。

而在创建完类型混淆的ConvolutionFilter对象后,exploit将借其来定位类型混淆的TextField对象。

最后看一下Read4方法的实现,如果存在corrupt后的ByteArray对象,那么将会优先通过它来读取内存,同时此方法中也可以借助类型混淆的ConvolutionFilter和TextField对象进行内存的读取,其中目标地址由ConvolutionFilter对象来传递,然后通过textFormat.tabStops[0]来读取内存数据。

自从Adobe Flash Player中引入CFG保护后,代码执行对于exploit开发者来说就成了一个很艰巨的任务,我们总结了他们近来使用的各项技术,发现CFG还是非常强大的,它使得exploit的开发成本大幅提高了。事实上,在过去两年中,坊间并未出现针对微软Windows 8.1+系统中Internet Explorer 11的远程代码执行0day漏洞,这些系统都是有CFG保护的。

在引入CFG保护之前,如果exploit能够获取目标进程空间的读写特权,那么代码执行就变得很容易了,大部分情况下只需corrupt目标对象的vftable表,然后就可以调用自身代码了,其中FileReference和Sound是最常利用的目标对象。以下CVE-2015-0336的exploit代码给出了一个通过FileReference.cancel方法进行代码执行的例子。

下述为此exploit借助FileReference对象执行shellcode的日志信息。

随着CFG保护的引入,攻击者们又转而在MMgc中查找能够利用的目标,以便完成接下去的代码执行。对MMgc来说,它在许多内部结构的分配上具有可预测的行为,这有助于攻击者们解析MMgc中的对象结构从而找出可利用的目标。

坊间发现的CVE-2016-1010利用样本会通过解析MMgc的内部结构来达成多种目的,此过程需要先泄露对象的内存地址,在此样本中,泄漏的地址来自于一个类型混淆的ConvolutionFilter对象。

下述代码给出的是EnumerateFixedBlocks(hhh222)函数的起始部分。

由分析可知,EnumerateFixedBlocks(hhh222)首先会调用ParseFixedAllocHeaderBySize(ghfgfh23), 而ParseFixedAllocHeaderBySize(ghfgfh23)又会通过LocateFixedAllocAddrBySize(jjj34fdfg)和ParseFixedAllocHeader(cvb45)函数来获取并解析那些具有特定大小的对象。

LocateFixedAllocAddrBySize(jjj34fdfg)函数会通过arg_1参数来获取堆的大小,其返回值是相应堆块的内存起始地址。

下面这部分代码会基于Flash的版本号和运行平台计算出地址的长度以及FixedAllocSafe结构的大小。

而DetermineMMgcLocations(hgjdhjjd134134)函数则用于确定MMgc中相关的位置信息。

DetermineMMgcLocations(hgjdhjjd134134)函数会将对象泄露后得到的相关地址信息交由SearchDword3F8处理,而后SearchDword3F8函数会在内存中搜索DWORD值0x3F8 ,这个值似乎是MMgc结构中一个非常重要的标识。

接着LocateFixedAllocAddrBySize(jjj34fdfg)函数会借助GetSizeClassIndex方法来获取索引值,并与前面得到的跟Flash版本及平台相关的大小信息一起用于计算FixedAlloc结构头的偏移量。

下述代码为exploit中的GetSizeClassIndex实现:

可以发现这和AVMPlus开源项目中的FixedMalloc::FindAllocatorForSize函数实现是相似的。

下述为AVMplus项目中定义的kSizeClassIndex数组,可以看到它们具有相同的索引值。

FixedAlloc类的定义中包含有指向FixedBlock链表的指针,那些具有相同大小的内存块会被添加到同一链表中。

而ParseFixedAllocHeader(cvb45)函数将用于解析FixedAlloc对象,它会通过ReadPointer(ghgfhf12341)函数实现的RW primitive功能来读取内存中相应位置的数据。

来看下面的例子,ParseFixedAllocHeaderBySize(ghfgfh23)函数中给定的堆大小为0x7f0,它将返回解析好的堆块结构。

返回结果中包含有堆块的首部结构,其中偏移0xc处的DWORD值正好为要查找的大小0x7f0。

在EnumerateFixedBlocks(hhh222)函数中会调用ParseFixedBlock(vcb4)来遍历FixedBlock链表。

其中,结构体FixedBlock的定义如下。

ParseFixedBlock(vcb4)函数将基于上述定义对FixedBlock进行解析。

在CVE-2016-1010的利用样本中还用到了ByteArray对象的地址泄露技术。

GetByteArrayAddress(hgfh342)函数会将获取到的第一个参数作为期望对象的大小,并枚举MMgc内存空间中具有此大小的对象,最终会返回所有找到的内存块相应的解析结果。

这里GetByteArrayAddress(hgfh342)函数返回的是pairs类型([ByteArray::Buffer,ByteArray::Buffer.array])的数组,其中,exploit可以在ByteArray::Buffer.array地址上放置想要的数据。 此外,GetByteArrayAddress(hgfh342)函数需要调用EnumerateFixedBlocks(hhh222)来定位ByteArray对象的堆地址,所给的期望对象大小为40或24,这取决于具体运行的Flash版本。

正如上面所说,GetByteArrayAddress(hgfh342)函数通过EnumerateFixedBlocks(hhh222)调用来获取具有特定大小的堆块,即ByteArray对象,之后会在这些对象中查找特殊的标记值。

此外,在发现的CVE-2015-8446利用样本中则借助内存的可预测性来访问Flash Player的内部结构。此例中,在完成堆喷操作后,GCBlock对象会被分配到地址0x1a000000上,而地址0x1a000008中的内容正是exploit要寻找的GC对象基址。

下述为GCBlockHeader结构体的定义。

其中,0x1a000008处的值是在获取GC结构体指针后通过GCAlloc::CreateChunk方法写入的,此GC结构随后会用于实现JIT内部数据的corruption,而作为迈出代码执行的第一步,ROP链在最开始会选择调用VirtualAlloc函数。

另一方面,由于CFG保护的存在,攻击者们也逐渐移步到Flash的JIT(just-in-time)运行时,相关攻击理念早前已由Francisco Falcon提出过了,而以JIT方式执行CFG代码则可缓解此类利用。在实际获取的CVE-2016-1010和CVE-2015-8446利用样本中我们还观察到了更巧妙的攻击手法,其中一个方法通过已知的CFG保护缺陷来破坏栈上的返回地址,相关细节我们将在未来进行讨论。在这里,我们分享一些关于freelists结构滥用以及MethodInfo._implGPR函数指针corruption的具体细节。

在CVE-2016-1010的利用样本中,shellcode所保存的位置非常有意思,其中就涉及到了如何操控freelists结构。下面开始分析,可以看到,StartExploit(hgfghfgj2)函数首先调用了AllocateByteArrays(jhgjhj22222)方法,而后会通过名为shellcode_bytearray的ByteArray对象将shellcode写入堆空间。

此exploit通过GetByteArrayAddress(hgfh342)方法获取用于存放freelists元素的内存地址,下述给出的结果中该地址为0x16893000。

这里注意一点,和正常情况相同,AllocateByteArrays(jhgjhj22222)方法分配的ByteArray对象所在内存的页面属性也为可读写,相应的两块堆空间将分别用于保存用到的freelists元素以及shellcode代码,为了方便,将这两个ByteArray对象命名为shellcode_bytearray和freelists_bytearray。

在GCHeap类中有一个声明为freelists的变量,它是HeapBlock类型的数组,此数组包含那些已经释放掉的或保留的内存块来供程序后面的分配调度。

exploit会将0x16893000处伪造的freelists元素链接到该数组中。

此操作将通过修改HeapBlock结构体中的前驱及后继指针来实现。

0x6fb7bba4指向的内容为freelists中的元素,它是HeapBlock类型的结构体,可以通过dump内存来查看exploit是如何对其进行corrupt的。

另外,shellcode代码会被写入0x16dc3000处相应的ByteArray对象中,此地址同样可以通过GetByteArrayAddress(hgfh342)函数来获取。

exploit将在0x16893000处写入该shellcode对应的内存地址值。


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 1
支持
分享
最新回复 (6)
雪    币: 210
活跃值: (641)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
2
顶一下~
2017-2-23 17:33
0
雪    币: 222
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
Mark,内容好多,慢慢看,感谢分享!
2017-2-23 17:35
0
雪    币: 292
活跃值: (810)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
4
感谢分享
2017-2-23 20:31
0
雪    币: 3561
活跃值: (541)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
2017-2-24 12:11
0
雪    币: 3
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
感谢分享!
2017-2-26 22:41
0
雪    币: 1491
活跃值: (985)
能力值: (RANK:860 )
在线值:
发帖
回帖
粉丝
8
这篇文章非常有深度,建议多读几遍
2017-2-27 15:51
0
游客
登录 | 注册 方可回帖
返回
//