Angler EK最新CVE-2015-8446 Flash Exploit分析

背景介绍

 

在12月Adobe狂补78个漏洞的一周后,国外安全研究人员kafeine(@kafeine)爆出Angler Exploit Kit开始使用这个月修补的CVE-2015-8446漏洞进行攻击。我们第一时间跟进对该漏洞的原理和样本的利用方式进行了分析,在此与大家分享。

 

 

CVE-2015-8446 – 补了两次的漏洞

 

 

该漏洞可以描述为:Flash在解码mp3文件中的”ID3” tag中的编码数据时,没有能够正确检查所需的decode buffer的大小,从而导致了堆溢出。

 

有趣的是,这个漏洞已经修补过一次,当时的编号是CVE-2015-5560。当时这个漏洞也曾被公开利用:

http://malware.dontneedcoffee.com/2015/08/cve-2015-flash-player-up-to-1800209-and.html

 

我们先来看一下有问题的解码逻辑:

ID3 tag中可以包含各种tag。有些tag可以包含经过编码的文本: http://id3.org/id3v2.3.0

以TIT tag为例,当Flash处理到TIT tag时,会解码里面的文本。在TIT tag中有一个域指明了编码文本的大小,以下面的mp3文件为例:

 

mp3

 

该文件包含两个TIT tag,它们包含的编码文本大小分别是4字节和0x15555556字节。当Flash尝试解压这些数据时,会动态分配buffer来存放解压后的数据,分配算法是(根据编码方式不同会有微小的差别):

decode_buffer_size = (encode_data_size – 1) * 6 + 2

因为可以有多个TIT tag,所以可能需要多次解码过程。每次解码时,都会检查当前的buffer长度是否足够用于解码,如果不够,则重新分配合适大小:

int current_buffer_size

int decoded_buffer_size

if (current_buffer_size  < decoded_buffer_size) {

// reallocate the decode buffer

}

下面我们看这里第一次出的问题,也就是CVE-2015-5560,报告者是google project zero的Natalie Silvanovich (@natashenka):

https://code.google.com/p/google-security-research/issues/detail?id=443&can=1&q=flash&start=100

 

我们来看一下decode buffer大小的计算公式:

decode_buffer_size = (encode_data_size – 1) * 6 + 2

相关代码如下:

 

.text:10024F13 loc_10024F13:                           ; CODE XREF: sub_10024C79+278j

.text:10024F13                 mov     eax, ebx

.text:10024F15                 imul    eax, 6

.text:10024F18                 add     eax, 2

.text:10024F1B                 cmp     [esi+28h], eax

.text:10024F1E                 mov     [ebp+var_20], eax

.text:10024F21                 jge     short loc_10024F4D

 

显然,当encode_data_size > 0x2aaaaaab时,(encode_data_size –1) * 6 + 2将超出32位整数的范围,而得到一个很小的数值,从而导致分配到的解码数组大小不够而产生溢出。

 

Adobe试图在18.0.0.232版本中修复这个漏洞,我们看看他的修补逻辑:

 

.text:10024E3E                 mov     eax, [ebp+var_14]

.text:10024E41                 imul    eax, 6

.text:10024E44                 inc     eax

.text:10024E45                 inc     eax

.text:10024E46                 cmp     eax, [ebp+var_14]

.text:10024E49                 jbe     loc_10024FB8

 

这个patch对encode_data_size进行了限制,限制条件为:

(encode_data_size * 6 + 2) >  encode_data_size

仔细想一下就知道,这个修补并不能解决这个漏洞,甚至连整数溢出的问题都解决不了。

 

最简单的bypass补丁的方法是利用下面这个带符号整数比较:

 

int current_buffer_size

int decoded_text_size

if (current_buffer_size  < decoded_text_size) {  // Signed Compare  !!!
!
// reallocate the decode buffer

}

 

比如当encode_data_size=0x15555580时,

(0x15555580 – 1) * 6 + 2 = 0x800000FC 小于0

于是只要当前buffer size是正数,则buffer不会重新分配,导致最终的溢出。这就是CVE-2015-8446。

 

 

样本的利用方式

 

 

我们看一下样本中的恶意mp3文件:

mp3

 

样本使用的是TIT tag,其中恶意的encode_data_size为0x15555556,而

(0x15555556 – 1) * 6 + 2 = 0x80000000,正好小于0,可以触发这个漏洞。

 

样本mp3包含2个TIT tag,第一个编码数据大小是4,第二个是0x15555556,当处理这两个tag的解码时,decode buffer分配过程如下:

 

第一个tag,size = 0x4:

decode_buffer_size = (4-1) * 6 + 2 = 0x14, 分配0x14 bytes的buffer

 

第二个tag,size=0x15555556:

decode_buffer_size = (0x15555556 – 1) * 6 + 2 = 0x80000000, 与现有的buffer size进行带符号比较:

((int)0x80000000 < (int)0x14),

于是buffer不会重新分配,第二次解码数据拷贝到0x14 bytes buffer中导致溢出。

 

Exploit选择溢出0x14是有意义的,0x14 bytes buffer最后分配到的内存块是0x18 bytes。正好是ByteArray::Buffer的大小:

 

class Buffer : public FixedHeapRCObject

{

public:

virtual void destroy();

virtual ~Buffer();

uint8_t* array;

uint32_t capacity;

uint32_t length;

};

 

 

样本首先通过内存布局,分配一系列的ByteArray对象,然后让解码buffer后面紧跟着ByteArray::Buffer对象:

 

|   Decode Buffer   |   ByteArray:Buffer   |

0x18 bytes                         0x18 bytes

——————————————————————–> Overflow

 

于是溢出后可以覆盖ByteArray::Buffer对象的相关字段。溢出使用的数据如下:

 

0:007> dd 09b38ec0
09b38ec0  40404040 40404040 40404040 40404040
09b38ed0  40404040 40404040 40404040 40404040
09b38ee0  40404040 ffffffff ffffffff 00000000

 

注意这里两个0xffffffff,正好覆盖了Buffer.length和Buffer.capacity,于是溢出后可以得到一个长度为0xffffffff的ByteArray,在32进程中可以用来读写整个进程空间。之后的利用就很简单了。

 

ba

 

12月补丁和ByteArray Length Protection

 

 

 

在Adobe对vector长度加入保护机制之后,exploit开始放弃vector而选用ByteArray作为exploit的利用对象,比如之前的CVE-2015-7645和这次的CVE-2015-8446。好消息是,Adobe在12月补丁之后,对ByteArray长度进行了保护,原理和vector长度保护类似。

 

12月补丁之后,ByteArray::Buffer数据结构进行了扩展,里面保存了3个检验字段:

LengthCookie, CapacityCookie和ArrayCookie,分别对应Buffer里面的length, capacity和array字段。当对ByteArray进行操作时,会检查:

 

if ( Buffer.length ^ key != LengthCookie

|| Buffer.capacity ^ key != CapacityCookie

|| Buffer.array ^ key != ArrayCookie )

{

// throw Error

}

 

在ByteArray保护起来之后,下一个被利用的数据结构是什么,让我们拭目以待。

 

感谢

在分析过程中,由于Angler EK的加密机制,以及身处的地域问题(EK对访问的IP回进行判断,有些区域不会触发。感慨现在在天朝求被Exploit都不得),我们遇到了各种各样得到阻碍。在此我们想感谢包括@ kafeinex(和不愿意透露姓名的友商朋友)在内的国外安全研究者的大力帮助,感谢他们的帮助和分享。