Use After Free (UAF) vulnerabilities are a class of memory corruption bug that have been very successful in the world of browser exploitation. In recent browser versions a number of controls have been introduced that make exploitation of these vulnerabilities much harder. Despite this, they still seem to persist.
This blog post is intended as an introduction to this class of vulnerability and will only address the fundamentals of exploitation. I will not be writing about bypassing the latest memory protections (I’m still figuring this out myself) but will be looking at the root cause of these particular issues.
I have chosen to use MS14-035 (https://technet.microsoft.com/library/security/MS14-035) as an example to walk through. I chose this particular vulnerability for a number of reasons; Firstly, there was a nice POC on exploit DB (https://www.exploit-db.com/exploits/33860/) that states it crashes on IE 8 through to 10. Secondly, I could not find an existing working exploit for this vulnerability on exploit DB.
Despite the lack of exploits on exploit DB, I have managed to find a few other blog posts related to this particular issue. So if you find me boring or nonsensical maybe try one of these:
For my environment I am using Windows 7 SP1 32 bit with IE 8.0.7601.17514. This is the default version of IE 8 that comes on a fresh install of Windows 7 SP1 32 bit. The only other software that you will need is windbg and gflags which come in WDK8 and you can download that from here https://developer.microsoft.com/en-us/windows/hardware/windows-driver-kit.
Assuming you have the above environment set up, open the POC in Internet Explorer and attach windbg to the process. You will probably need to set up the windbg symbols path. Information on how to do this can be found here https://msdn.microsoft.com/en-us/library/windows/desktop/ee416588(v=vs.85).aspx. After setting the symbols path hit g for go, and in the browser click allow blocked content. You should get a crash in windbg that looks something like this:
EIP -> 6557e08
Taking a look at this crash we can see that eip is pointing at invalid memory. We now need to find out where the value in eip came from.
Having a look at the call stack with the kb command indicates that the crash happens during execution of the CFormElement::DoReset function from the mshtml library. This is nice to know as it gives us a good starting point as to where things went wrong (or right for our intended purposes). We are going to need a little more information in order to make this work, however.
The next step is to enable gflags with user-mode stack tracing and page heap, then run the POC again. gflags is a tool that comes with the WDK and greatly helps with debugging a number of issues. You can read all about it here https://msdn.microsoft.com/en-us/library/windows/hardware/ff549609(v=vs.85).aspx. The following command will turn on page heap and user-mode stack tracing:
gflags.exe /i iexplore.exe +ust +hpa (+hpa enables full page heap this may be memory intensive, you can enable normal page heap with /p /enable).
With page heap and user-mode stack tracing enabled, we run the POC again and get the following windbg output:
Page heap does a couple of things in order to identify memory corruption. One of these things is that it uses the pattern f0f0f0f0 to identify a freed heap allocation, i.e., whenever a heap allocation is freed by the process the page heap flag forces the allocation to be overwritten with f0f0f0f0. With this in mind, and looking at the figure above, we get the first indication (besides the POC name) that this is a UAF issue. We can see that the program crashes at call dword ptr [eax+1CCh]. The program tried to call a function thats address should be stored at [eax+0x1cc]. However eax is now storing the value 0xf0f0f0f0 which came from a block of heap memory that has recently been freed.
To confirm this we first need to find where the value currently in eax came from. In order to do this we should investigate the CFormElement::DoReset function that was identified in figure 1.
The figure below shows part of the output of the windbg command uf mshtml!CFormElement::DoReset. I had to snip it as the output is quite long and we are only really interested in a small block of it for now:
A section of the mshtml!CFormElement::DoReset function assembly code
The highlighted block of code is where the program crashes. The last line of that block you should recognise from our previous analysis of the crash in figure 2. Looking at the block we can now see that the value in eax came from the mov eax, dword ptr [esi] instruction. So whatever the value esi is pointing at gets moved into eax and then the program tries to call an offset of eax. This is typical of virtual function calls, so let’s take a look at how virtual functions work.
Virtual functions are member functions that can be overridden in a derived class. The way C++ achieves this is by the compiler creating a vtable for each class. The vtable is essentially a table of function pointers that each point to a different virtual function. When an object is created the first member variable is a pointer to the vtable for that particular class, called the VPTR. When a virtual function is called the process will walk the VPTR and call the virtual function at the appropriate offset.
n the code block highlighted in figure 3 the program copies the VPTR located at the address in esi into the eax register with the mov eax, dword ptr [esi] instruction. eax now contains the address of the start of the vtable. The call dword ptr [eax+1CCh] instruction then attempts to call the virtual function at the offset 0x1CC from the start of the vtable.
In order to find out a bit more about the freed object at the address in esi we can use the windbg !heap command with user-mode stack tracing enabled. The output of this command is shown in figure 5:
Figure 5 shows the heap stack trace of the heap allocation that contains the memory address in esi. First we can see that the size of the object is 0x60 - this will become important later. Additionally we can see the mshtml!CTextArea::`scalar deleting destructor' function call in the stack trace. This indicates again that the object was freed, as it is a call to the class’s destructor function. It also tells us that the object was a CTextArea object derived from the CFormElement class. At this point it is pretty clear that this is in fact a Use After Free vulnerability and the crash has resulted from a virtual function call.
Alright, so far we have spent a lot of time in the debugger looking at the result of the crash. We’ve reached the conclusion that this is a UAF bug with the crash occurring when the program attempts to call a virtual function from the freed CTextArea object. Cool. It’s probably worth taking a look at the POC trigger file to see what this looks like and how we are getting to this code path.
Taking a look at figure 6, and keeping in mind the previous analysis we have done, we can draw a few conclusions about what is going on. We could test these conclusions in windbg, but I might leave that to the reader as this is dragging on a bit already.
First a form is created with id ‘testfm’. This form consists of four elements, two of which are textareas, and from our previous debugging we know that the freed object is a CTextArea object.
The next area of interest is the changer function declaration in the script tags. This function clears the form contents and is where the CTextArea object gets freed.
Finally at the bottom of the script tag starting at line 36 we can see that the ‘child2’ element of the ‘testfm’ form is checked (this element is a checkbox). Then the changer function is set to execute using the onpropertychange event attribute. After this the function reset is called on the form.
When reset is called on the form it is actually calling the CFormElement::DoReset function. This function loops through each element and resets its properties. When the loop hits the ‘child2’ element the changer function is called which clears the form and frees the form elements objects. When the next iteration of the loop grabs the ‘child3’ element, which was freed in the changer function, the process will crash on the virtual function call.
In order to exploit this we will need to create a fake object on the heap that will occupy the freed region of memory. The fake object will need a fake VPTR to a fake vtable that has an entry at offset 0x1CC to code that we want to execute.
Ok, I think this is as good a place as any to wind up this first part of this series. It was a little longer than I had hoped it would be. The next part I will look at how heap allocations work and how we can abuse some of these feature to gain control of eip. If you want to jump ahead then have a read of the links I posted at the top in the recommended reading section.