Headline
Software Defense: mitigating heap corruption vulnerabilities
Heap corruption vulnerabilities are the most common type of vulnerability that Microsoft addresses through security updates today. These vulnerabilities typically occur as a result of programming mistakes that make it possible to write beyond the bounds of a heap buffer (a spatial issue) or to place a heap allocated object in an unexpected state such as by using the object after it has been freed (a temporal issue).
Heap corruption vulnerabilities are the most common type of vulnerability that Microsoft addresses through security updates today. These vulnerabilities typically occur as a result of programming mistakes that make it possible to write beyond the bounds of a heap buffer (a spatial issue) or to place a heap allocated object in an unexpected state such as by using the object after it has been freed (a temporal issue). Over time, attackers have developed a number of techniques to help them exploit various types of heap corruption vulnerabilities. Starting with Windows XP Service Pack 2, Microsoft began introducing hardening changes to the Windows heap manager that were designed to make it more difficult to exploit heap corruption vulnerabilities. In this blog post, we will review some of the general methods that have been used to exploit and mitigate heap corruption vulnerabilities and highlight hardening changes that have been made in Windows 8 and Windows 8.1 to further complicate exploitation. For more background on the Windows 8 heap architecture, please refer to the Channel 9 interview on the Windows 8 heap manager.
Heap corruption exploitation, then and now Heap corruption exploitation, then and now
In a previous blog post, we covered the history of heap-based exploitation and mitigation techniques from Windows XP through Windows 7. This blog post showed that prior to Windows Vista, most of the research on heap corruption exploitation techniques focused on corrupting heap metadata in order to achieve more powerful exploitation primitives (such as the ability to write an arbitrary value to any location in memory). One of the reasons attackers focused on corrupting heap metadata is because it was always present and therefore could enable application-independent (generic) exploitation techniques. The release of Windows Vista changed the landscape of heap exploitation through numerous heap hardening changes that addressed nearly all of the heap metadata corruption exploitation techniques that were known at the time.
As a consequence of the hardening changes in Windows Vista, attackers have largely shifted their focus toward exploitation techniques that rely on corrupting application-specific data stored on the heap. For example, attackers will attempt to use a heap corruption vulnerability to corrupt the C++ virtual table pointer of an object on the heap or to corrupt the base or length field of a heap-allocated array to achieve the ability to read or write to any location in memory. There has been additional research on heap metadata corruption post-Windows Vista and there are a small number of known real-world exploits that have relied on these metadata corruption techniques[1,2,3,4], but as this blog post will show, all of the publicly known exploitation techniques that rely on metadata corruption have been addressed in Windows 8.1.
Heap corruption mitigations Heap corruption mitigations
The heap manager in Windows 8 and Windows 8.1 builds on the hardening changes of previous Windows releases by incorporating new security features that mitigate not only metadata corruption techniques but also less generic techniques that rely on corrupting application-specific data. These new security features can be broken down into the following threat categories: heap integrity checks, guard pages, and allocation order randomization. All of the security features introduced in Windows 8 have been inherited by Windows 8.1.
Heap integrity checks
The heap manager in Windows 8 and Windows 8.1 includes a number of new integrity checks that are designed to detect heap metadata corruption and terminate an application safely if corruption is detected. This section describes some of the noteworthy integrity checks that have been added.
Catch-all exception handling blocks have been removed
Previous versions of the Windows heap made use of catch-all exception handling blocks in certain cases where exceptions were considered non-fatal. This had the potential to make it easier for attackers to exploit heap corruption issues in certain cases, in particular by allowing an attacker multiple attack attempts. Therefore, these catch-all blocks have been removed from the heap in Windows 8, meaning such exceptions now lead to safe termination of the application.
HEAP handle can no longer be freed
The HEAP handle is an internal data structure that is used to maintain the state associated with a given heap. Prior to Windows 8, an attacker could use a heap-based memory corruption vulnerability to coerce the heap into freeing the HEAP handle data structure. After doing this, the attacker could force the heap to reallocate the memory that previously stored the HEAP handle state. This in turn allowed an attacker to corrupt internal heap metadata, including certain function pointer fields. The Windows 8 heap mitigates this attack by preventing a HEAP handle from being freed.
HEAP CommitRoutine encoded by a global key
The HEAP handle data structure includes a function pointer field called CommitRoutine that is called when memory regions within the heap are committed. Starting with Windows Vista, this field was encoded using a random value that was also stored as a field in the HEAP handle data structure. While this mitigated trivial corruption of only the CommitRoutine function pointer, it did not mitigate the case where an attacker could corrupt both the CommitRoutine and the field that stored the encoding key. The Windows 8 heap mitigates this attack by using a global key to encode the CommitRoutine function pointer rather than a key that is stored within the HEAP handle data structure.
Extended block header validation
Each heap allocation returned by the Windows heap has a header that describes the allocation’s size, flags, and other attributes. In some cases, the Windows heap may flag an allocation as having an extended block header which informs the heap that there is additional metadata associated with the allocation. In previous versions of Windows, an attacker could corrupt the header of an allocation and make it appear as if the allocation had an extended block header. This could then be used by an attacker to force the heap to free another allocation that is currently in use by the program. The Windows 8 heap mitigates this attack by performing additional validation on extended block headers to ensure that they are correct.
Blocks cannot be allocated if they are already busy
Some of the attacks that have been proposed by security researchers rely on reallocating memory that is already in use by the program (e.g. [3]). This can allow an attacker to corrupt the state of an in-use heap allocation, such as a C++ object, and thereby gain control of the instruction pointer. The Windows 8 heap mitigates this attack by verifying that an allocation is not already flagged as in-use (“busy”) when it is about to be allocated. If a block is flagged as in-use, the heap takes steps to safely terminate the process.
Encoded FirstAllocationOffset and BlockStride
One of the exploitation techniques proposed in [4] involved corrupting heap metadata (FirstAllocationOffset and BlockStride) that is used by the Low Fragmentation Heap (LFH) to calculate the address of an allocation within a subsegment. By corrupting these fields, an attacker can trick the heap into returning an address that is outside the bounds of a subsegment and potentially enable corruption of other in-use heap allocations. The heap manager in Windows 8.1 addresses this attack by encoding the FirstAllocationOffset and BlockStride fields in order to limit an attacker’s ability to deterministically control the calculation of allocation addresses by the LFH.
Guard pages
One of the ways that the Windows 8 heap better protects application data and heap metadata is through the use of guard pages. In this context, a guard page is an inaccessible page of memory that will cause an access violation if an application attempts to read from it or write to it. Placing a guard page between certain types of sub-regions within the heap helps to partition the heap and localize any memory corruptions that may occur.
In an ideal setting, the Windows heap would encapsulate all allocations in guard pages in a manner that is similar to full-page heap verification. Unfortunately, this type of protection is not feasible for performance reasons. Instead, the Windows 8 heap uses guard pages to isolate certain types of sub-regions within the heap. In particular, guard pages are enabled for the following types of sub-regions:
- Large allocations. In cases where an application attempts to allocate memory that is larger than 512K (on 32-bit) or 1MB (on 64-bit), the memory allocation request is passed directly to the virtual memory allocator and the size is updated to allocate extra space for a guard page. This ensures that all large allocations have a trailing guard page.
- Heap segments. The Windows heap allocates large chunks of memory, known as heap segments, which are divided up as an application allocates memory. The Windows 8 heap adds a trailing guard page to all heap segments when they are allocated.
- Maximally-sized subsegments. Each heap segment may contain one or more subsegment that is used by the frontend allocator (the Low Fragmentation Heap, or LFH) to allocate blocks of the same size. Once a certain threshold has been reached for allocating blocks of a given size, the LFH will begin allocating maximally-sized subsegments, which are subsegments that contain the maximum number of blocks possible for a given size. The Windows 8 heap adds a trailing guard page to maximally-sized subsegments. For 32-bit applications, guard pages are inserted probabilistically to minimize the amount of virtual address space that is consumed.
Allocation order randomization
One of the behaviors that attackers rely on when exploiting heap buffer overruns is that there must be a way to reliably position certain heap allocations adjacent to one another. This requirement stems from the fact that an attacker needs to know how many bytes must be written in order to corrupt a target allocation on the heap (while minimizing collateral damage to the heap that could cause the application and hence the attack to be terminated). Attackers typically try to ensure that allocations are immediately adjacent to each other through techniques that are often referred to as heap massaging or heap normalization. These techniques attempt to bring the heap into a state where new allocations are placed at a desired location with respect to one another.
In Windows 8, a new security feature has been added to the LFH which randomizes the order of allocations. This means that allocations that are made through the LFH are no longer guaranteed to be placed immediately adjacent to one another even after an attacker has attempted to normalize the heap. This has the effect of preventing an attacker from reliably assuming that an allocation containing a target object will be positioned after the allocation that they are able to overflow. While an attacker may attempt to increase the reliability of their attack by corrupting more data or allocating more target objects, they run the risk of destabilizing the process by corrupting other heap state or causing the process to terminate by accessing a guard page as described in the previous section. This is a good example of several mitigations working together: neither is foolproof on its own, but combined they result in increasingly complex requirements for a successful attack.
Although allocation order randomization helps make the internal layout of the heap nondeterministic, there are limitations to how far it goes. First and foremost, the performance of the Windows heap is critical as it is used as a general purpose memory allocator by the vast majority of the applications that run on Windows. As a side effect of this, allocation order randomization is currently limited to randomizing allocations within individual LFH subsegments (which accounts for the majority of allocations made by applications). This means backend allocations have no inherent entropy and therefore may be subject to deterministic allocation patterns, as noted in [5]. In addition to performance, there are also inherent limits to the effectiveness of allocation order randomization. If an attacker can read the contents of heap memory, they may be able to overcome the effects of randomization. Similarly, allocation order randomization is not designed to strongly mitigate heap vulnerabilities that are related to object lifetime issues, such as use after free vulnerabilities. This is because an attacker will generally be able to allocate a sufficient number of replacement objects to overcome the effects of allocation order randomization. We’ll discuss some other mitigations that are targeted at addressing use after free issues, which are increasingly preferred by exploit writers, later in this series.
Conclusion Conclusion
The hardening changes that have been made to the Windows heap manager in Windows 8 and Windows 8.1 have been designed to make it more difficult and costly to exploit heap corruption vulnerabilities. This has been accomplished by adding additional integrity checks to metadata that is used by the heap, by protecting application data stored on the heap through the use of guard pages, and by randomizing the order of allocations. These mitigations do not make heap corruption vulnerabilities impossible to exploit, but they do have an impact on the time it takes to develop an exploit and how reliable an exploit will be. Both of these factors play a role in determining whether or not an attacker will develop an exploit for a vulnerability. With that being said, the fact that heap corruption vulnerabilities are the most common vulnerability class that we address through security updates means it is likely that we will continue to see additional research into new exploitation techniques for heap vulnerabilities in the future. As such, we will continue to look for ways to harden the Windows heap to further increase the difficulty of developing reliable exploits for heap corruption vulnerabilities.
- Matt Miller
References References
[1] Ben Hawkes. Attacking the Vista Heap. Black Hat USA. Aug, 2008.
[2] Ben Hawkes. Attacking the Vista Heap. Ruxcon. Nov, 2008.
[3] Chris Valasek. Modern Heap Exploitation using the Low Fragmentation Heap. SyScan Taipei. Nov, 2011.
[4] Chris Valasek. Windows 8 Heap Internals. Black Hat USA. Aug, 2012.
[5] Zhenhua Liu. Advanced Heap Manipulation in Windows 8. Black Hat Europe, Mar, 2013.