Efficacy of MemoryProtection against use-after-free vulnerabilities

As of the July 2014 patch of Internet Explorer, Microsoft has taken a major step in the evolution of exploit mitigations built into its browser. The new mitigation technology is called MemoryProtection (or MemProtect, for short) and has been shown to be quite effective against a range of use-after-free (UAF) vulnerabilities. Not all UAFs are equally affected, however. Here we’ll discuss what MemoryProtection is and how it operates, and evaluate its effectiveness against various types of UAFs.

 

High-Level Description of MemoryProtection


The heart of MemoryProtection is a new strategy for freeing memory on the heap.

When typical C++ code compiled for the Windows platform wishes to free a block of memory on the heap, the operation is implemented by a call to HeapFree. This is a call to the Windows heap manager, and it immediately makes the memory block available for reuse in future allocations.
 
With MemoryProtection, when code wishes to free heap memory, the memory is not immediately freed at the heap manager level. Consequently, the memory is not made available for future allocations – at least not for a very important window in time. Instead of returning the memory to the Windows heap manager, MemoryProtection keeps the memory in an allocated state (as far as the heap manager is concerned), fills the memory with zeroes, and tracks the memory block on a list that MemoryProtection maintains of memory blocks waiting to be freed at the heap manager level. MemoryProtection will ultimately free the block at the heap manager level when it performs a periodic memory reclamation sweep, but only if the stack contains no outstanding references to the block.

Since application-level frees and heap manager-level frees no longer occur at the same time, there is room for confusion whenever a “free” is mentioned. For the purposes of this post, a “free” will mean an application-level free unless specified otherwise. Also, when speaking of a free at the heap manager level we will sometimes speak of the memory being “released” or “returned” to the heap manager.

Detailed Description of MemoryProtection

MemoryProtection maintains a per-thread list of freed memory blocks that are still waiting to be freed at the heap manager level. We will call this the “wait list”. When code in Internet Explorer wishes to free a block of memory on the heap, it calls the function MSHTML!MemoryProtection::CMemoryProtector::ProtectedFree. (Note, however, that in many places this function is inlined, which means that its body is copied in its entirety into the compiled code of other functions.) This method performs the following steps:

1.    Check if the wait list for the current thread contains at least 100,000 total bytes of memory waiting to be freed at the heap manager level. If so, perform a reclamation sweep (see below).
2.    Add an entry to the current thread’s wait list, recording the block’s address, its size, and whether the block resides on the regular process heap or on the isolated heap.
3.    Fill the memory block with zeroes.

To perform a reclamation sweep, the steps are as follows. These steps are implemented by MemoryProtection::CMemoryProtector::MarkBlocks and  MemoryProtection::CMemoryProtector:: ReclaimUnmarkedBlocks. All operations apply to the current thread’s wait list only.

1.    Sort the wait list in increasing order of block address.
2.    Place a mark on every wait list entry whose block is still pointed to by a pointer residing on the current thread’s stack. The pointer could be either a pointer to the start of the block or a pointer to any memory location falling within the block’s address range.
3.    Iterate over the wait list. For each wait list entry encountered that is not marked, release the memory block at the heap manager level via an actual call to HeapFree, and remove the block from the wait list. Remove all marks from marked entries.

In addition, an unconditional reclamation step is performed periodically. This occurs each time Internet Explorer’s WndProc is entered to service a message for the thread’s main window. At that time, the entire wait list is emptied and all waiting blocks are returned to the heap manager. The function performing this action is MemoryProtection::CMemoryProtector::ReclaimMemoryWithoutProtection.

For efficiency, the wait list is rearranged in certain ways while performing the iteration in step 3 above. Therefore blocks are not necessarily freed in order of increasing address, nor are they always freed in the same order in which they are placed on the wait list.

For research and debugging purposes it is often useful to be able to observe the behavior of Internet Explorer without MemoryProtection. In particular, MemoryProtection will interfere with the ability to use the heap manager’s call stack database to capture the call stack at the time of an application-level free. (By the same token, MemoryProtection actually makes it easier to capture the call stack at the time of allocation.) MemoryProtection can effectively be turned off via a manual procedure by writing the value 0xffffffff to the variable MSHTML!MemoryProtection::CMemoryProtector::tlsSlotForInstance. In WinDbg, this can be done via the following command:

ed MSHTML!MemoryProtection::CMemoryProtector::tlsSlotForInstance 0xffffffff

Effect of MemoryProtection on UAFs

When an IE exploit leveraging a UAF is run unmodified against the newly MemoryProtection-enabled IE, the result will often resemble a null-pointer dereference. At the point in time when the exploit attempts to perform an allocation reusing memory from the freed block, the original memory block has not yet been returned to the heap manager; as a result, the exploit code fails to cause a new allocation at that location. Ultimately, when IE makes its improper use of the freed memory, any data read consists of zeroes. Often this will cause a quick and harmless termination of the IE process when the zero value is interpreted as a pointer and is dereferenced. It’s important to realize that even when the symptom appears to indicate that the bug is a null-pointer dereference, the underlying cause actually may be a UAF and a potential security risk.

Let’s consider how an attacker might be able to modify the exploit in order to evade MemoryProtection. For a successful attack, the exploit must be able to trigger a memory sweep in between the free and the reallocation. Furthermore, at the time of this sweep, the stack must not contain any outstanding references to the freed block.

It follows that there are some UAFs that are impossible to exploit under MemoryProtection. Specifically: If an outstanding stack reference exists for the entire period of time between the free and the later use, then it is impossible to cause reclamation of the freed block. This is a common situation that accounts for a large percentage of UAFs. Many UAFs are the result of a situation in which a free occurring at a deeper level in the call stack leaves a dangling pointer (sometimes a “this” pointer) at a higher level in the call stack. The crash occurs when the stack is popped to the level at which the dangling pointer resides. This very common type of UAF is now non-exploitable.

As for UAFs not falling into the above category, however, the situation is quite different.

Consider the case of a UAF in which a certain script action results in a dangling pointer being stored in heap memory, more or less permanently – by which we mean that the dangling pointer remains in place even after the return of all currently executing functions. The malicious script can then trigger the use of the dangling pointer at a much later point in time. We call these “long-lived” UAFs. In the case of a long-lived UAF, MemoryProtection offers no real defense. An attacker can ensure that the memory block is freed at the heap manager level by allowing Internet Explorer’s message loop to execute, as this will trigger an unconditional release of all waiting blocks. Though it may seem that the additional blocks released during this sweep have the potential to disrupt the exploit by causing various heap coalesces that are hard to predict, this problem can largely be eliminated by properly preparing the wait list so it will contain a minimum of entries at the time of the sweep.

For UAFs not falling into the above categories MemoryProtection may provide partial or full mitigation. Consider the common case of a UAF in which the free and the use occur within the same script method. In the past, some of these have been exploitable conditions. MemoryProtection adds some additional hurdles that an attacker must clear. By referring to the ProtectedFree algorithm above, you can see that when IE calls ProtectedFree on a block of memory, the memory is never returned to the Windows heap manager at that time – not even if the wait list is very full. The return of the memory to the heap manager will always be delayed at least until the next call to ProtectedFree, at which point a sweep could occur. Therefore, an additional hurdle for an attacker in this situation is to find a way to force an additional free in between the free and the reallocation. This additional free must be performed on the same thread as the original free, since wait lists are maintained on a per-thread basis. For certain bugs this additional requirement will make exploitation impossible or too unreliable for use by an adversary. In other cases, when it is easy to force an additional free, MemoryProtection provides a weak form of protection by making it a bit more complex to arrange for a desired sequence of frees at the heap manager level. Typically it is possible to evade this protection through the addition of additional script actions that prepare the wait list, forcing it into a known desired state containing only a small number of entries.

In Summary

MemoryProtection is a significant new mitigation that makes Internet Explorer more secure by eliminating entire classes of UAFs and rendering them non-exploitable. For other classes of UAFs MemoryProtection is ineffective or only partially effective. Researchers should be aware that some IE crashes that appear to be null-pointer dereferences actually mask an underlying exploitable bug.

 

Simon Zuckerbraun

Security Researcher, HP ZDI

Labels: IE| MemoryProtection| UAF| ZDI
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
Security Researcher, Zero Day Initiative
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.