CVE-2013-3112: From NULL to Control - Persistence pays off with crashes

Months ago, my fuzzer found a bug that was initially flagged as a NULL pointer dereference. The crash instruction was different from the others, so I decided to minimize the crash and have a closer look. Things got quite interesting, and with some persistence, ended up in control of EIP (Extended Instruction Pointer).  This article walks through the whole analysis process from a null pointer crash to fully controlling execution. 

 

Details of the initial crash:

 

First, I simplified the POC (Proof of Concept) from 2000+ lines to the following:

image1.jpg 

 

Running the POC in Windows 7 x64 would crash Internet Explorer (IE) 9 in 32-bit mode with both PageHeap enabled and disabled. 

image2.jpg

 

The NULL pointer dereference was caused by the following:

image3.jpg

 

I ended up changing and adding some code in the simplified POC, hoping to get something more interesting.  Eventually, I ended up adding "ret.innerHTML=ret.innerHTML" right after the second appendChild() call.

 

Things got very interesting this time when I got something totally different:

image4.jpg

 

Then with PageHeap enabled I got the following:

image5.jpg

 

So, why did "ret.innerHTML=ret.innerHTML" change the game?  To understand what caused this behavior let's start by setting the following break points (BP) and re-run the POC without setting ret.innerHTML.

image6.jpg

This is pretty uninteresting.  Now let's re-run with the same breakpoints but this time with "ret.innerHTML=ret.innerHTML" but without PageHeap.

image7.jpg

 

From the debugger output above, we can draw a conclusion that “ret.innerHTML=ret.innerHTML” influenced applyElement's execution flow and reached MSHTML!CDoc::CreateMarkupFromInfo.

 

The next test would be break pointing on MSHTML!CDoc::CreateMarkupWithElement whenever MSHTML!CElement::EnsureInMarkup is hit, and studying the results both with and without setting “ret.innerHTML”.

 

First, running without "ret.innerHTML=ret.innerHTML", but with the following breakpoints set:

image8.jpg

 

We notice that the second break point never triggers.  Running the POC with ret.innerHTML set we get a different result:

image9.jpg

 

So, I jumped into IDA to understand what's going on within EnsureInMarkup:

EnsureInMarkup_1.jpg

 

If the check succeeds then the execution flow changes and reaches another check:

EnsureInMarkup_2.jpg

 

If this check fails, it gets us where we want to go, which is here:

EnsureInMarkup_3.jpg

 

So what's ESI and what sets the values at [ESI+26] and [ESI+0C]?  To start answering these questions, let's set the following breakpoint for the second test (with PageHeap enabled):

image10.jpg

 

Now our next strategy would be setting a breakpoint on MSHTML!CSemanticElement::CreateElement, to get the new object address and set a memory breakpoint at offset 0x26:

image11.jpg

 

The above shows that when "ret.innerHTML=ret.innerHTML" is run it sets the byte at [ESI+26] to 0.

This behavior would change the execution flow in MSHTML!CElement::EnsureInMarkup leading to the execution of CDoc::CreateCMarkupFromInfo:

image12.jpg

 

Notice “applyElement” gets executed twice.  The first execution creates a CMarkup object via “CDoc::CreateCMarkupFromInfo” through “CDoc::CreateMarkupWithElement”.  The second time applyElement executes, it frees the object then re-uses it, leading to a “potentially” exploitable Use-After-Free (UAF - Use after free errors occur when a program continues to use a pointer after it has been freed.[i]).

 

Analysis of the new crash:

 

With PageHeap enabled I got the following:

image13.jpg

 

The above clearly shows that a CMarkup object has been freed via MSHTML!CMarkup::`vector deleting destructor'.  The next step would be finding exactly where the object has been allocated and the exact size.

image14.jpg

 

The following breakpoint would show the allocation (PageHeap enabled).  Some output has been truncated or edited for readability.

image15.jpg

 

Apparently the object has been allocated from MSHTML!CDoc::CreateMarkupFromInfo with size 0x190.  Verifying this in IDA:

HeapAlloc.jpg

 

The next step would be checking where the freed object has been referenced. Assuming we have the following callstack:

image16.jpg

 

If we set a breakpoint on MSHTML!CElement::PrivateEnterTree, re-run the PoC, and trace a bit through:

image17_part1.jpg

image17_part2.jpg

 

The CPhraseElement object contains a reference to the freed object at offset 0x2C.  To verify this, we can set a breakpoint on CPhraseElement::CreateElement, grab the address of the newly created object, then finally set a memory breakpoint at offset 0x2C.

image18.jpg

 

ESI below shows the address of the freed object.

image19.jpg

 

If we go back to where the crash happened and try to understand what should have been called in a perfect world. This is what we would get:

image20.jpg

 

Then if we examine this:

image21.jpg

 

The method that should have been called is AddRef.  Apparently the object has been freed and J it fails to run AddRef leading to potential code execution.

 

To summarize:

1. The CMarkup object is freed.

2. A reference is kept at offset 0x2C of the CPhraseElement object.

3. Failure to call AddRef leads to potential remote code execution (RCE)

 

Controlling the freed object - To LFH or not to LFH (Low Fragmentation Heap):

 

Usually researchers would go with the option of activating LFH and then try to fill up the freed memory.  Here's what happens when choosing the LFH path:

 

image22_part1.jpg

image22_part2.jpg

 

Almost everything was filled up.  However, the object that we need to control was not.  The reason is applyElement was called twice.  The object is created the first time applyElement is called.  The second time it's called the free/re-use happens, making it very difficult to win the race condition with LFH enabled.  To verify this:

image23_part1.jpg

image23_part2.jpg

 

If we check the memory around ESI, we would see that it has been filled right after the first applyElement was called:

image24.jpg

 

The current situation makes it hard to win a race condition and have that object overwritten.  So, I decided to play around without LFH enabled and here's what happens:

image25_part1.jpg

image25_part2.jpg

 

Notice the two forward and backward link pointers (Flink and Blink) at offset 0 and 4.  Things are getting more interesting now.  Increasing the allocations and being lucky enough to write the adjacent chunk would eventually give you the following result (not 100 percent stable):

image26_part1.jpg

image27.jpg

 

Conclusion:

 

A lot of crashes may seem pretty much useless.  However minor modifications can completely change the game. In our case, you can take a crash manifesting as a null pointer in an entirely different direction. What may seem like a useless call instruction can suddenly get very interesting if you are persistent. Sometimes you just have to flex your reverse engineering skills, and sometimes you just get plain lucky.  Finally, take advantage of how the memory manager handles the free lists, this may turn hard-to-control Use-After-Free bugs into exploitable ones.

 

Step it up!  Take your 0x00000000 to 0-Day. 

 

-- Abdul-Aziz Hariri, HP Security Research

 

Note: The vulnerability used for this demonstration is described by the following references:

http://technet.microsoft.com/en-us/security/bulletin/ms13-047

http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2013-3112

Labels: crashes| security
Comments
Francis Provencher(anon) | ‎09-26-2013 06:05 PM

Clearly explain, Nice work Abed!

 

 

Leave a Comment

We encourage you to share your comments on this post. Comments are moderated and will be reviewed
and posted as promptly as possible during regular business hours

To ensure your comment is published, be sure to follow the Community Guidelines.

Be sure to enter a unique name. You can't reuse a name that's already in use.
Be sure to enter a unique email address. You can't reuse an email address that's already in use.
Type the characters you see in the picture above.Type the words you hear.
Search
Showing results for 
Search instead for 
Do you mean 
About the Author
Featured


Follow Us
The opinions expressed above are the personal opinions of the authors, not of HP. By using this site, you accept the Terms of Use and Rules of Participation.