0×00 序言

2015年12月28号,Adobe发布安全公告,一口气修补了19个漏洞。在致谢部分提到了CVE-2015-8651这个漏洞是由华为的安全研究部门提交的.不过很快这部分就被删除。国内安全厂商微步在线发布了<<境外“暗黑客栈”组织对国内企业高管发起APT攻击>>的分析报告。安天追影小组根据微步在线提供的样本哈希找到了相关样本,对该整数溢出漏洞的原理进行了分析.

0×01 相关知识

2012年的时候Adobe在Adobe Flash player中引入了domain memory的特性,该特性允许快速的访问内存。domain memory相关的函数被定义在package avm2.intrinsics.memory中,如下:

package avm2.intrinsics.memory
   {
       public function li8(addr:int): int; // Load Int 8-bit
       public function li16(addr:int): int; // Load Int 16-bit
       public function li32(addr:int): int; // Load Int 32-bit
       public function lf32(addr:int): Number; // Load Float 32-bit (a.k.a. “float”)
       public function lf64(addr:int): Number; // Load Float 64-bit (a.k.a. “double”)
       public function si8(value:int, addr:int): void; // Store Int 8-bit
       public function si16(value:int, addr:int): void; // Store Int 16-bit
       public function si32(value:int, addr:int): void; // Store Int 32-bit
       public function sf32(value:Number, addr:int): void; // Store Float 32-bit (a.k.a. “float”)
       public function sf64(value:Number, addr:int): void; // Store Float 64-bit (a.k.a. “double”)
       public function sxi1(value:int): int; // Sign eXtend 1-bit integer to 32 bits
      public function sxi8(value:int): int; // Sign eXtend 8-bit integer to 32 bits
      public function sxi16(value:int): int; // Sign eXtend 16-bit integer to 32 bits
  }

示例代码如下:

var domainMemory:ByteArray = new ByteArray();
 var BYTE_ARRAY_SIZE:Number = 0x10000000;
 domainMemory.length = BYTE_ARRAY_SIZE;
 ApplicationDomain.currentDomain.domainMemory = domainMemory;
 var index:* = 0;
 var val:* = 0x200;
 for(i=0; i< BYTE_ARRAY_SIZE; i++)
 {
        si8(val, i);
 }

根据官方给出的测试报告显示domain memory访问内存的速度相当快。这里ApplicationDomain.currentDomain.domainMemory是一个全局变量,li*/si*函数会直接访问这个变量。该特性引入后被发现存在不少的问题,比较典型漏洞如编号为CVE-2013-5330和CVE-2014-0497等漏洞. 它们均是domain memory相关的opcode对边界检校不严格导致的。

0×02 漏洞成因分析

使用JPEXS Free Flash Decompiler 打开样本,能看到相应的脚本如下:

经过分析找到溢出的代码位于Bymitis类中,Bymitis在初始化的时候会判断当前是不是在IE浏览器中运行,如果不是IE就退出。接着会判断Flash player的版本,然后根据版本执行相应的代码。本次分析使用的Flash Player的版本是13.0.0.128,因此会进入函数cidkedie中执行,部分代码如下:

发现JPEXS Free Flash Decompiler反编译的结果相比动态调试的结果少了一些内容,经过分析比较发现如下的AS反汇编代码存在可疑的情况:

这段代码可以概括为:

    op_si32(0x3FFFFFFF,loc_2+0x7FFFFFFC)

loc_2相关的代码为:

这个里可以认为loc_2=0×80001004,至此可以理解为将值0x3FFFFFFF写入到domain memory的0×80001004+0x7FFFFFFC位置处。从cidkedie的代码中可以看到domain memory指向的是一个长度为0×1000的ByteArray数组,它的名字是fastmemory.这个数组的前几个字节被填充为m3mory,后面跟随一个计数,记录该函数被调用的次数。因为使用了堆喷射,所以该函数会被反复被调用,以保证能够访问到特定的内存。

上面的AS脚本生成的JIT代码如下:

0612B480    B8 C6352F09     mov eax,0x92F35C6
0612B485    35 39CAD036     xor eax,0x36D0CA39
0612B48A    8B75 90         mov esi,dword ptr ss:[ebp-0x70]
0612B48D    8B5F 14         mov ebx,dword ptr ds:[edi+0x14]
0612B490    8B4F 18         mov ecx,dword ptr ds:[edi+0x18]
0612B493    8DBE 00F0FF7F   lea edi,dword ptr ds:[esi+0x7FFFF000]
0612B499    83E9 04         sub ecx,0x4
0612B49C    3BF9            cmp edi,ecx
0612B49E    0F87 230D0000    ja 0612C1C7
0612B4A4    03DE            add ebx,esi
0612B4A6    B9 FCFFFF7F      mov ecx,0x7FFFFFFC
0612B4AB    89040B          mov dword ptr ds:[ebx+ecx],eax
0612B4AE    B8 00F0FF7F     mov eax,0x7FFFF000
0612B4B3    8B1C03          mov ebx,dword ptr ds:[ebx+eax]
0612B4B6    B8 419CE424     mov eax,0x24E49C41
0612B4BB    35 39CAD036     xor eax,0x36D0CA39
0612B4C0    3BD8            cmp ebx,eax
0612B4C2    75 00           jnz X0612B4C4

关键部分已经被标红,相关指令解释如下:

(1)

  0612B480    B8 C6352F09     mov eax,0x92F35C6
  0612B485    35 39CAD036     xor eax,0x36D0CA39

这两条指令执行完后eax=0x3FFFFFFF

(2)

mov esi,dword ptr ss:[ebp-0x70]

这条指令执行完后ESi=0×80001004.

(3)

0612B48D    8B5F 14         mov ebx,dword ptr ds:[edi+0x14]
0612B490    8B4F 18         mov ecx,dword ptr ds:[edi+0x18]

这两条指令执行完后:ebx为ApplicationDomain.currentDomain.domainMemory指向的内存地址,ecx为ApplicationDomain.currentDomain.domainMemory的大小即0×1000.

(4)

0612B4A4    03DE            add ebx,esi
0612B4A6    B9 FCFFFF7F      mov ecx,0x7FFFFFFC
0612B4AB    89040B          mov dword ptr ds:[ebx+ecx],eax

这段代码可以描述为: 

*((DWORD*)(domainMemory+0x80001004+0x7FFFFFFC))= 0x3FFFFFFF

很显然0×80001004+0x7FFFFFFC整数溢出为0×1000.这个值就写入到了domainMemory的内存区之外。接下来就是flash漏洞的常用技巧了,相关代码如下:

_loc7_ = 0;
while(_loc7_ < 1280)
{
    vc[_loc7_] = new <uint>[305419896];
    vc[_loc7_].length = 1022;
    _loc7_++;
}

这里创建1280个uint对象,然后将它们的长度改为1022.每个uint对象的内存第一个DWORD的值为1022,如果uint对象的内存刚好位于domainMemory内存的后面就可以修改这个值,然后就可以访问任意内存,接着可以在可访问的内存范围内创建object对象,找到object的虚函数表,修改函数指针,执行shellcode.

在本次分析的机器上,cidkedie函数被调用两次后就会发现domainMemory的后面紧跟着一个uint对象。domainMemory的内存如下:

0x9c28000向下0×1000处的内存如下:

第一个值为0x03FE(1022),第三个值为0×12345678(305419896),显然这是上面创建的uint对象。漏洞触发后的内存如下:

前后对比后就可以发现uint对象的长度被替换为0x3FFFFFFF,这时候uint对象的内存变得很大,基本上可以访问任意内存了。

0×03总结

CVE-2015-8651是对domain  memory执行相关操作的opcode在执行过程中没有很好的对访问地址的范围进行判断从而导致整数溢出漏洞,它的漏洞形成原理和CVE-2013-5330以及CVE-2014-0497都是相似的。最后感谢微步在线威胁情报给出的样本哈希,使安天追影分析小组能够完成这份分析报告。

0×4参考

[1] 境外“暗黑客栈”组织对国内企业高管发起APT攻击

[2] 深入剖析某国外组织针对中国企业的APT攻击(CVE-2015-8651)

UBIQUITOUS FLASH, UBIQUITOUS EXPLOITS, UBIQUITOUS MITIGATION

* 作者:安天追影(企业账号),转载请注明来自FreeBuf黑客与极客(FreeBuf.COM)

源链接

Hacking more

...