In our previous posts in this series, we described various mitigation improvements that attempt to prevent the exploitation of specific classes of memory safety vulnerabilities such as those that involve stack corruption, heap corruption, and unsafe list management and reference count mismanagement. These mitigations are typically associated with a specific developer mistake such as writing beyond the bounds of a stack or heap buffer, failing to correctly track reference counts, and so on. As a result, these mitigations generally attempt to detect side-effects of such mistakes before an attacker can get further along in the exploitation process, e.g. before they gain control of the instruction pointer.
Another approach to mitigating exploitation is to focus on breaking techniques that can apply to many different classes of memory safety vulnerabilities. These mitigations can have a broader impact because they apply to techniques that are used further along in the process of exploiting many vulnerabilities. For example, once an attacker has gained control of the instruction pointer through an arbitrary vulnerability, they will inherently need to know the address of useful executable code to set it to. This is where well-known mitigations like Data Execution Prevention (DEP) and Address Space Layout Randomization (ASLR) come into play – both of which have been supported on Windows for many releases now. When combined, these mitigations have proven that they can make it very difficult to exploit many classes of memory safety vulnerabilities even when an attacker has gained control of the instruction pointer.
In recent years, attackers have been increasingly forced to adapt to exploiting vulnerabilities in applications that make use of a broad range of mitigations, including DEP and ASLR. As our previous blog post explains, there are scenarios where both DEP and ASLR can be bypassed, and it is no surprise that attackers have been increasingly focused on improving their ability to do so. Likewise, attackers have placed greater interest on finding classes of vulnerabilities, such as use after free issues, that can grant them more flexibility when attempting to develop an exploit. In light of these trends, we focused a significant amount of attention in Windows 8 and Windows 8.1 on improving the robustness of mitigations that break exploitation techniques that apply to many classes of vulnerabilities. In particular, this blog post will cover some of the noteworthy improvements that have been made to ASLR, such as eliminating predictable address space mappings, increasing the amount of entropy that exists in the address space, and making it more difficult to disclose address space information where possible.
For compatibility reasons, executable images (DLLs/EXEs) must indicate their desire to be randomized by ASLR through the /DYNAMICBASE flag provided by the Visual C++ linker. If an executable image has not been linked with /DYNAMICBASE, the Windows kernel will attempt to load the image at its preferred base address. This can cause the executable to reliably load at a predictable location in memory. While this limitation of ASLR on Windows is by design, real-world exploits for software vulnerabilities have become increasingly reliant on executable images that have not enabled support for ASLR.
To generically mitigate this issue, an application running on Windows 8 (or Windows 7 with KB 2639308 installed) can elect to enable a security feature known as Force ASLR. When enabled, this feature forces all relocatable images to be randomized when they are loaded by the application, including those images which have not been linked with /DYNAMICBASE. This is designed to prevent executable images from being loaded at a predictable location in memory. If desired, an application can also elect to prevent non-relocatable images from being loaded.
Since the Force ASLR feature will cause executable images to be randomized that have not enabled support for ASLR, there is a risk that a compatibility problem may be encountered. In addition, the method used to forcibly relocate executable images that have not been built with /DYNAMICBASE can have a performance impact due to decreased page sharing. This is because Force ASLR essentially mimics the behavior of a base address collision and thus may incur a memory cost due to copy-on-write. As such, the Force ASLR feature is not enabled by default for applications running on Windows 8. Instead, applications must explicitly enable this feature.
The Force ASLR feature has been enabled by default for critical applications such as Internet Explorer 10+, Microsoft Office 2013, and Windows Store applications. This means an attacker attempting to exploit vulnerabilities accessible through these applications will not be able to rely on non-randomized executable images. For example, our recent security update to enable ASLR for HXDS.DLL would not appreciably impact the security posture of applications that enable Force ASLR because this non-ASLR DLL would already get randomized. Going forward, attackers will most likely need to rely on a vulnerability-specific address space information disclosure when exploiting applications that completely enable ASLR or that make use of Force ASLR.
Virtual memory allocations that are made by an application can have their base address assigned in one of three ways: bottom-up, top-down, or based. The bottom-up method searches for a free region starting from the bottom of the address space (e.g. VirtualAlloc default), the top-down method searches starting from the top of the address space (e.g. VirtualAlloc with MEM_TOP_DOWN), and the based method attempts to allocate memory at a supplied base address (e.g. VirtualAlloc with an explicit base). In practice, the majority of the memory that is allocated by an application will use the bottom-up allocation method, and it is rare to see applications use the based method for allocating memory.
Prior to Windows 8, bottom-up and top-down allocations were not randomized by ASLR. This meant that allocations made through functions like VirtualAlloc and MapViewOfFile had no entropy and could therefore be placed at a predictable location in memory (barring non-deterministic application behavior). While certain memory regions had their own base randomization, such as heaps, stacks, TEBs, and PEBs, all other bottom-up and top-down allocations were not randomized.
Starting with Windows 8, the base address of all bottom-up and top-down allocations is explicitly randomized. This is accomplished by randomizing the address that bottom-up and top-down allocations start from for a given process. In this way, fragmentation within the address space is minimized while also realizing the benefits of randomizing the base address of all memory allocations that are not explicitly based.
For compatibility reasons, applications must indicate that they support bottom-up and top-down randomization. An application can do this by linking their EXE with /DYNAMICBASE.
One of the major differences between 64-bit and 32-bit applications on Windows is the size of the virtual address space that is made available to a process. 64-bit applications whose EXE is linked with the /LARGEADDRESSAWARE flag receive 8 TB in Windows 8 (128 TB in Windows 8.1) of virtual address space whereas 32-bit applications only receive 2 GB by default. The limited amount of address space available to 32-bit applications places practical constraints on the amount of entropy that can be applied by ASLR when randomizing the location of memory mappings. Since 64-bit applications do not suffer from these limitations by default, it is possible to significantly increase the amount of entropy that is used by ASLR. The ASLR implementation in Windows 8 takes full advantage of this opportunity by enabling high degrees of entropy for 64-bit applications. Providing higher degrees of entropy can further decrease the reliability of exploits written by an attacker and also makes it less likely that an attacker will be able to correctly guess or brute force an address.
This feature introduces 1 TB of variance into the address that bottom-up allocations start from. This equates to 24 bits of entropy, or a 1 in 16,777,216 chance of guessing the start address correctly. Since heaps, stacks, and most other memory regions are allocated bottom-up, this has the effect of making traditional address space spraying attacks impractical (such as heap and JIT spraying). This is because systems today do not have enough memory available to spray the amount that would be needed to achieve even small degrees of reliability. In addition, executable images that are randomized by the Force ASLR feature receive high degrees of entropy as a result of the high entropy bottom-up randomization feature being enabled for an application. As a result, exploits for vulnerabilities in 64-bit applications that rely on address space spraying will first need to disclose the address at least one bottom-up allocation in order to determine where data may have been placed relative to that address.
For compatibility reasons, this feature is disabled by default and must be enabled on a per-application basis. This is because some 64-bit applications have latent pointer truncation issues that can surface when dealing with pointers above 4 GB (significant bits set beyond bit 31). 64-bit applications that enable this feature are guaranteed to receive memory addresses that are above 4 GB when allocating bottom-up memory (unless insufficient address space exists above 4 GB). 64-bit applications can enable support for this feature by linking their EXE with the /HIGHENTROPYVA linker flag provided by Visual Studio 2012. This flag is enabled by default for native applications when building with Visual Studio 2012 and beyond.
This feature introduces 8 GB of variance into the address that top-down allocations start from. This equates to 17 bits of entropy, or a 1 in 131,072 chance of guessing the start address correctly. 64-bit processes automatically receive high degrees of entropy for top-down allocations if top-down randomization has been enabled (which is controlled by whether the EXE linked with /DYNAMICBASE).
Prior to Windows 8, 64-bit executable images received the same amount of entropy that was used when randomizing 32-bit executable images (8 bits, or 1 in 256 chance of guessing correctly). The amount of entropy applied to 64-bit images has been significantly increased in most cases starting with Windows 8:
The reason that entropy differences exist due to the base address of an image is again for compatibility reasons. The Windows kernel currently uses the preferred base address of an image as a hint to decide if the image supports being based above 4 GB. Images that are based below 4 GB may not have been tested in scenarios where they are relocated above 4 GB and therefore may have latent pointer truncation issues. As such, the Windows kernel makes a best-effort attempt to ensure that these images load below 4 GB. Because of these constraints, the vast majority of 64-bit EXEs and DLLs in Windows 8 and Windows 8.1 have been based above 4 GB to ensure that they benefit from the highest possible degrees of entropy. 64-bit images produced by the Visual C++ tool chain also base images above 4 GB by default.
The effectiveness of ASLR is inherently dependent on an attacker being unable to discover the location of objects in memory. In some cases, an attacker can leverage a vulnerability in a program to disclose information about the address space layout of a process. For example, an attacker could use a vulnerability to read memory that they would not normally be able to access and thereby discover the address of a DLL in memory. While the mechanics of disclosing address space information are typically dependent on the application and vulnerability that are being exploited, there are some general approaches that attackers have identified. In Windows 8, we have taken steps to eliminate and destabilize known address space information disclosure vectors, although these changes have by no means resolved the general problem posed by address space information disclosures.
Windows uses an internal data structure known as SharedUserData to efficiently communicate certain pieces of information from the kernel to all processes on a system. For efficiency and compatibility reasons, the memory address that SharedUserData is located at is consistent across all processes on a system and across all versions of Windows, including Windows 8 (0x7ffe0000). Since Windows XP Service Pack 2, this memory region has contained pointers into a system DLL (NTDLL.DLL) that have been used to enable efficient system call invocation, among other things. The presence of image pointers at a known-fixed location in memory was noted as being useful in the context of certain types of address space information disclosures. In Windows 8 (and now prior versions with MS13-063 installed), all image pointers have been removed from SharedUserData to mitigate this type of attack. The removal of these pointers effectively mitigated a DEP/ASLR bypass that was later disclosed which affected versions of Windows prior to Windows 8 (involving LdrHotPatchRoutine).
Ensuring that all forms of memory allocation have some base level of entropy has the effect of eliminating what would otherwise be predictable memory mappings in the address space. In some cases, an attacker may be able to leverage a vulnerability to read the contents of arbitrary locations in memory. In these cases, the attacker must be able to predict or discover the address of the object that they wish to read from (typically via heap spraying). The improvements that have been made to ASLR in Windows 8 have made it more difficult for attackers to do this reliably, particularly on 64-bit. As a result, any address space information disclosure that relies on reading from a specified location in memory will generally be more difficult and less reliable on Windows 8. It should be noted, however, that the size of the 32-bit address space places practical constraints on the impact of this, particularly in cases where an attacker is able to fill a large portion of the address space with desired content.
While the previous sections highlighted improvements that were made to ASLR for user mode applications, we also made investments in Windows 8.1 into hardening the Windows kernel against disclosing kernel address space information to lesser privileged user mode processes. The majority of these improvements focused on restricting low integrity processes from accessing certain system and process information classes that intentionally expose kernel address space information. In addition, certain kernel addresses were removed from the shared desktop heap and hypervisor-assisted restrictions were added to limit the exposure of kernel addresses via instructions that can be used to query the GDT/IDT descriptor table base addresses. As a result of these improvements, sandboxed applications such as Internet Explorer 11, Microsoft Office 2013, and Windows Store apps are all prevented from discovering addresses through these interfaces. This means it will be more difficult for attackers to exploit local kernel vulnerabilities as a means of escaping these sandboxes.
The improvements that have been made to ASLR in Windows 8 and Windows 8.1 have addressed various limitations that attackers have been taking advantage when exploiting vulnerabilities. As a result of these improvements, we anticipate that attackers will continue to be increasingly reliant on address space information disclosures as a means of bypassing ASLR. Forcing attackers to rely on information disclosures has the effect of adding another costly check box to the conditions that attackers need to satisfy when exploiting memory safety vulnerabilities in modern applications.
- Matt Miller