Get on-the-go access to the latest insights featured on our Trustworthy Computing blogs.
One of the oldest forms of memory safety exploitation is that of stack corruption vulnerabilities, with several early high-profile exploits being of this type. It seems fitting therefore to kick off this Software Defense series by looking at the status of software defense today with respect to this age-old problem.
Mitigating stack-based corruption vulnerabilities
The most common form of stack corruption – overrun of a buffer beyond the amount of stack space that was allocated for it – has been mitigated to some degree since Windows XP via the compiler-based /GS feature. A copy of a per-module random value, the /GS cookie, is placed between the stack local variables and stack metadata including the return address. Before using the return address, the program verifies the integrity of this local copy of the /GS cookie: if its value does not match the master per-process value then an overrun is assumed to have taken place and the program is terminated.
The limitations of this defensive device have driven a number of refinements over the years; the main ones are summarized in the following table:
Scope of GS protection
For performance reasons only a subset of functions are protected with a GS cookie. The scope of this protection was initially aimed primarily at character arrays, where attackers would supply malicious strings, often over the network that the program did not handle correctly. GS enhancements in Visual Studio 2010 extended the scope of GS protection to functions with a far more general range of data structures. Visual Studio 2013 builds on this further and also protects arrays of pointers.
Protecting a function with /GS has a codesize cost – the prolog code to set up the GS cookie on the stack and the epilog to verify its value - and the runtime cost of actually executing these extra instructions. The cost of extending the scope of GS protection has been partially offset by a new compiler optimization: if usage of the buffers that have led to the GS cookie can be proven safe by the optimizer – i.e. all writes to the memory associated with these variables is within the bounds of their allocated stack space – then the GS cookie is eliminated.
Enabling /GS still typically only inserts a GS check on less than 10% of functions – though clearly this varies considerably depending on usage of local variables in each individual application.
Evading the GS check through exception abuse
An important aspect touched on above deserves further discussion – the cookie check only occurs at the end of the function. This means that there is a window between the stack corruption and the GS check in which an attacker can seek to gain control. One favoured approach has been to trigger an exception and abuse the resulting exception handling process.
On x86, exception metadata associated with SEH is stored on the stack: this includes pointers to the handler code that should be invoked. If the attacker can use the initial stack corruption to modify this SEH metadata – replacing the function pointer with an address of his choosing – then when the exception dispatching code runs, it will transfer control to the attacker-controlled address.
Mitigating exception metadata abuse on x86 platforms
Again compile-time techniques can help - /SAFESEH effectively creates a whitelist of exception handler addresses. In the corrupted SEH metadata scenario above, the attacker’s modified ‘pointer to exception handler’ address is not on the whitelist thus defeating the simple form of this exploitation technique. It is however costly: all code needs to be recompiled to benefit – as any non-SAFESEH code in the program introduces a “code with unknown SAFESEH whitelist” module – which for application compatibility purposes means that any address inside that module is assumed to be on the whitelist.
Windows XP SP2 included a recompile of most OS binaries with /SAFESEH – but even that is not sufficient. Any 3rd party browser plugin (not compiled with /SAFESEH) provided a potential means to evade the /SAFESEH validation.
SEHOP, supported initially in Windows Vista SP1 (off-by-default) and Windows Server 2008 (on-by-default), provides a more robust solution. We previously described SEHOP in detail, but a summary of the basic idea is as follows. When an exception occurs, SEHOP walks the entire list of SEH metadata structures on the stack and verifies that it terminates at a special “known good handler address”. Any attacker corruption of one of these structures overwrites the forward pointer to the next SEH metadata structure and so breaks the integrity of the list, which is therefore detected by the SEHOP check.
Windows 7 added support for per-process opt-in to SEHOP.
SEHOP in Windows 8 and Windows 8.1
Windows 8 raises the bar in that SEHOP is enabled by default for applications that built to run on Windows 8 and above (more precisely for any application built with subsystem version greater or equal to 6.2 – see /SUBSYSTEM for details).
In Windows 8.1 SEHOP is further improved in a couple of ways.
First SEHOP now has a range of 64 possible distinguished FinalExceptionHandler values that may be chosen, instead of just the one handler address within the system DLL that was used previously. The actual distinguished FinalExceptionHandler value used to validate the exception handling frame chain is selected at random during process startup, and differs on a per-process basis. The advantage of this is that SEHOP no longer has a system-wide shared secret, but a per-process secret, such that a local attacker can no longer assume that they know the distinguished value required to pass the frame validation check in a separate process on the local machine. All of the possible FinalExceptionHandler values are also valid SafeSEH handlers.
Secondly, Windows 8.1 adds support for SEHOP in kernel mode and enables this by default. Like the enhanced version of SEHOP for user mode, kernel mode SEHOP has 64 possible FinalExceptionHandler addresses, so just disclosing the kernel base address is not enough to defeat kernel SEHOP; one has to be able to read arbitrary kernel memory to do that.
GS limitations and future work
The effectiveness of the GS design described thus far – even if it were applied to every stack frame and one assumed that the cookie check were always reached after stack memory corruption occurred – is limited to cases where the cookie value is altered. This is what is detected. Important classes of stack-based vulnerability therefore remain that are not mitigated today for example targeted attacker-controlled writes to a stack address, or stack underruns – i.e. the direction of the writes goes towards the start address of the allocation.
Visual Studio 2012 updates /GS to emit range checks to mitigate one of the most common types of targeted write scenario that we were seeing through MSRC.
Recently published trend data shows that the number of successful exploits of stack-based vulnerabilities represents but a handful of the set of issues faced by our customers.
The obstacles posed by GS, SafeSEH and SEHOP and opt-in to these measures by increasing numbers of developers and IT professionals are likely part of the reason behind this.
Investment in static analysis tooling has also likely played a part, some of which is available to developers through compiler warnings such as C4789 and CodeAnalysis warnings such as C6200. The lifetime of a stack-based buffer tends to be shorter than its heap counterpart: it is limited to execution of one function – albeit potentially with many callees – making it more tractable to completely analyse its use across the entire program. By contrast pointers to heap allocations are frequently stored in multiple objects with a more complex usage pattern by the program, making it harder for analysis to track usage of a heap buffer with the same level of precision.
Although some unmitigated stack-based vulnerability classes do occasionally arise in practice – for example MS08-067 which was an underrun of a stack-based array, these are relatively isolated examples. As attacker focus has shifted to the heap, so the priority accorded to improving defensive measures there has increased.
Exploitation of stack-based corruption vulnerabilities is one of the oldest forms of memory safety exploit. History shows a succession of mitigation refinements developed to counter to attacker innovations in this area. We note a series of evolutions:
- Mitigation robustness and completeness has improved over time, including the protection of arrays of pointers by /GS in Visual Studio 2013 and a move to a per-process rather than a system-wide secret for SEHOP in Windows 8.1.
- Counter-measures to thwart exception handler abuse have evolved from expensive (from an engineering perspective) measures such as /SAFESEH that requires recompilation to OS-based SEHOP that can be applied on a per-process basis to existing applications.
- Default settings have evolved over time; e.g. user-mode SEHOP is now on-by-default for applications designed for Windows 8 and Windows 8.1, and kernel mode SEHOP is both new and enabled in Windows 8.1.
A state of relative maturity has been reached with fewer stack-based vulnerabilities being reported or exploited. Does this mean we are “done”? – by no means! Rather attacker attention appears to have turned to other less mature areas. And as attacker focus has shifted, so has defense. The next article takes a closer look at some of the advances related to memory corruption on the heap.
Tim Burrell, MSEC Security Science
Today we released eight security bulletins addressing 25 CVE’s. Four bulletins have a maximum severity rating of Critical while the other four have a maximum severity rating of Important. We hope that the table below helps you prioritize the deployment of the updates appropriately for your environment.
(win32k.sys and OTF font parsing)
Additional attack vector involves victim browsing to a malicious webpage that serves up OTF font file resulting in code execution as SYSTEM.
- Jonathan Ness, MSRC Engineering
Today we released MS13-080 which addresses nine CVEs in Internet Explorer. This bulletin fixes multiple security issues, including two critical vulnerabilities that haven been actively exploited in limited targeted attacks, which we will discuss in details in this blog entry.
CVE-2013-3893: the final patch after Fix it workaround
Previously, Microsoft released Security Advisory 2887505 and made available the Fix it workaround 51001 to provide earlier protection to all customers for an actively exploited security issue that was reported to us. Fix it workarounds are examples of the reactive steps that MSRC can take in order to provide earlier protection solutions for customers during active attacks in combination with technologies such as EMET that help make exploitation more complicated for attackers. We have noticed some appreciation of Fix it workarounds across users given the download numbers and we are glad that users are proactively using this type of protection when possible while waiting for the comprehensive update. Today’s bulletin for Internet Explorer addresses this CVE, so we recommend to all customers (with or without Fix it workaround applied) to prioritize the installation of this security update. Customers who decided to install Fix it workaround 51001 can install MS13-080 bulletin at any moment and then remove the Fix it at any time using the uninstaller 51002 (as usual, we remind users that the presence of Fix it does not interfere with security updates and upcoming bulletins).
We are aware that a Metasploit module has been released recently for this CVE, however from the telemetry received from our partners and sensor feeds, the exploitation activity detected at this moment is still limited in nature and specifically is targeting older IE versions (8 and 9) using an ASLR bypass that requires the presence of Office 2007/2010 on the machine.
CVE-2013-3897: the unexpected use-after-free
We’d like to take this opportunity to thank our valued partners Trustwave, the National Cyber Security Centre of the Netherlands, and Renato Ettisberger from IOprotect GmbH for reporting this vulnerability in a coordinated manner and for collaborating with us. We also decided to provide additional details of the exploit and its payload that will help security vendors and users to strengthen defense against these attacks while the security updates are applied.
NOTE: [x] has been detected being a variable IP range using .153 and .154 values
As observed in both exploits, attackers are able to target previous versions of Internet Explorer on older platforms where all the newest mitigations are not available or not enabled by default. As such, we advise users, to install and use the latest versions of Internet Explorer on modern Windows in order to raise exploitation challenges for attackers and have better defense. For more information about the impact of software mitigations on patterns of vulnerability exploitation, Microsoft released recently a whitepaper that can help to understand the role of software mitigations and exploitation strategies of attackers.
Special thanks to IE team for assembling this fix in record time and Richard van Eeden for help analyzing the root cause of the bugs.
- Elia Florio, MSRC Engineering
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.
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.
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. ). 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  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.
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:
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 . 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.
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
 Ben Hawkes. Attacking the Vista Heap. Black Hat USA. Aug, 2008.
 Ben Hawkes. Attacking the Vista Heap. Ruxcon. Nov, 2008.
 Chris Valasek. Modern Heap Exploitation using the Low Fragmentation Heap. SyScan Taipei. Nov, 2011.
 Chris Valasek. Windows 8 Heap Internals. Black Hat USA. Aug, 2012.
 Zhenhua Liu. Advanced Heap Manipulation in Windows 8. Black Hat Europe, Mar, 2013.