Category Archives: Windows

GravityRAT malware also targets Android and macOS

Researchers spotted new variants of the Windows GravityRAT spyware that now can also infect Android and macOS devices.

Researchers from Kaspersky Lab have spotted new variants of the GravityRAT malware that now can be also used to infect Android and macOS devices.

GravityRAT is a malware strain known for checking the CPU temperature of Windows computers to avoid being executed in sandboxes and virtual machines.

The GravityRAT malware Access Trojan (RAT) is believed to be the work of Pakistani hacker groups, it is under development at least since 2015.

“Today, Cisco Talos is uncovering a new piece of malware, which has remained under the radar for the past two years [since 2015] while it continues to be developed.” reads an analysis published by Cisco Talos that spotted the malware back in 2017 when it was used by an APT group targeting India.

The sample analyzed by Kaspersky last year is able to infect macOS and Android devices, unlike past variants that were focused on Windows.

Crooks also started using digital signatures to make the apps look more legitimate.

The malware researchers found the new Android GravityRAT sample in 2019, on VirusTotal. The hackers had added a spy module to Travel Mate, an Android app for travelers to India, the source code of which is available on Github.

gravityRAT

The tainted app is able to steal contacts, emails, and documents from the infected device, then send them back to the command-and-control server (nortonupdates[.]online). The C&C server was also associated with other two malicious apps (Enigma and Titanium) targeting the Windows and macOS platforms.

The spyware is able to get information about the system and support multiple features, including:

  • search for files on the computer and removable disks with the extensions .doc, .docx, .ppt, .pptx, .xls, .xlsx, .pdf, .odt, .odp, and .ods, and upload them to the server
  • get a list of running processes
  • intercept keystrokes
  • take screenshots
  • execute arbitrary shell commands
  • record audio (not implemented in this version)
  • scan ports

The malware was distributed via applications that clone legitimate apps that act as downloader for the GravityRAT payloads.

The applications analyzed by Kaspersky were developed in .NET, Python and Electron framework, they achieve persistence by adding a scheduled task.

The researchers reported that the malware was employed in approximately 100 successful attacks between 2015 and 2018. The list of targets includes employees at defense, police, and other departments and organizations.

Threat actors tricked the victims into installing a malicious app disguised as a secure messenger in order to continue the conversation, the attackers contacted the victims through a fake Facebook account. The attackers likely sent to the victims download links.

“It is safe to assume that the current GravityRAT campaign uses similar infection methods — targeted individuals are sent links pointing to malicious apps.” concludes Kaspersky.

“The main modification seen in the new GravityRAT campaign is multiplatformity: besides Windows, there are now versions for Android and macOS. The cybercriminals also started using digital signatures to make the apps look more legitimate.”

Pierluigi Paganini

(SecurityAffairs – hacking, GravityRAT)

The post GravityRAT malware also targets Android and macOS appeared first on Security Affairs.

Microsoft released out-of-band Windows fixes for 2 RCE issues

Microsoft released two out-of-band security updates to address remote code execution (RCE) bugs in the Microsoft Windows Codecs Library and Visual Studio Code.

Microsoft has released two out-of-band security updates to address two remote code execution (RCE) vulnerabilities that affect the Microsoft Windows Codecs Library and Visual Studio Code.

The two vulnerabilities, tracked as CVE-2020-17022 and CVE-2020-17023, have been rated as important severity.

The CVE-2020-17022 is a remote code execution vulnerability that exists in the way that Microsoft Windows Codecs Library handles objects in memory. An attacker can exploit the vulnerability to execute arbitrary code.

“Exploitation of the vulnerability requires that a program process a specially crafted image file.” reads the advisory. “The update addresses the vulnerability by correcting how Microsoft Windows Codecs Library handles objects in memory.”

The CVE-2020-17022 vulnerability affects all devices running Windows 10, version 1709 or later, and a vulnerable library version.

Windows 10 devices are not affected in their default configuration and that “only customers who have installed the optional HEVC or ‘HEVC from Device Manufacturer’ media codecs from Microsoft Store may be vulnerable.”

The CVE-2020-17022 flaw was reported to Microsoft by Dhanesh Kizhakkinan from FireEye.

The CVE-2020-17023 vulnerability is a remote code execution vulnerability that exists in Visual Studio Code. An attacker can trigger the flaw by tricking a user into opening a malicious ‘package.json’ file, then he could run arbitrary code in the context of the current user.

“If the current user is logged on with administrative user rights, an attacker could take control of the affected system. An attacker could then install programs; view, change, or delete data; or create new accounts with full user rights.” reads the advisory.

“To exploit this vulnerability, an attacker would need to convince a target to clone a repository and open it in Visual Studio Code. Attacker-specified code would execute when the target opens the malicious ‘package.json’ file. The update address the vulnerability by modifying the way Visual Studio Code handles JSON files.”

The CVE-2020-17023 vulnerability was reported by Justin Steven.

The IT giant did not provide any mitigating measures or workarounds for the two vulnerabilities.

According to Microsoft, both vulnerabilities are not being exploited in the wild.

Pierluigi Paganini

(SecurityAffairs – hacking, Microsoft Windows)

The post Microsoft released out-of-band Windows fixes for 2 RCE issues appeared first on Security Affairs.

Four npm packages found opening shells and collecting info on Linux, Windows systems

On Thursday, four JavaScript packages have been removed from the npm portal because they have been found containing malicious code.

NPM staff removed four JavaScript packages from the npm portal because were containing malicious code. Npm is the largest package repository for any programming language.

The four packages, which had a total of one thousand of downloads, are:

“Any computer that has this package installed or running should be considered fully compromised. All secrets and keys stored on that computer should be rotated immediately from a different computer,” the npm security team said.

“The package should be removed, but as full control of the computer may have been given to an outside entity, there is no guarantee that removing the package will remove all malicious software resulting from installing it,”

The researcher AX Sharma, who analyzed the packages, revealed that plutov-slack-clientnodetest1010, and nodetest199 share identical code.

Experts warn that systems running applications that imported one of these packages should be potentially compromised because the three JavaScript libraries opened web shells on the computers running them.

web shell is a code, often written in typical web development programming languages (e.g., ASP, PHP, JSP), that attackers implant on web servers to gain remote access and code execution.

The npmpubman, unlike the other packages, was found collecting user data from the environment variables and uploads the gathered info to a remote host.

The malicious code could work on both Windows and *nix operating systems, including major distros, including Linux, FreeBSD, OpenBSD.

One of the packages was uploaded on the npm portal in May, while the remaining ones were uploaded in September 2018.

“It is possible that all four packages were authored by the same attacker(s) despite conflicting data provided in the package.json manifests.” reported Bleeping Computer.

“In a real-world scenario, npmpubman could be used as a part of an attacker’s reconnaissance efforts to collect information about a system, whereas the other packages establish a direct connection between the attacker’s and the victim’s computers.”

In August, the npm security team has removed the JavaScript library “fallguys” from the npm portal because it was containing a malicious code used to steal sensitive files from an infected users’ browser and Discord application.

Pierluigi Paganini

(SecurityAffairs – hacking, malware)

The post Four npm packages found opening shells and collecting info on Linux, Windows systems appeared first on Security Affairs.

Fuzzing Image Parsing in Windows, Part One: Color Profiles

Image parsing and rendering are basic features of any modern operating system (OS). Image parsing is an easily accessible attack surface, and a vulnerability that may lead to remote code execution or information disclosure in such a feature is valuable to attackers. In this multi-part blog series, I am reviewing Windows OS’ built-in image parsers and related file formats: specifically looking at creating a harness, hunting for corpus and fuzzing to find vulnerabilities. In part one of this series I am looking at color profiles—not an image format itself, but something which is regularly embedded within images. 

What is an ICC Color Profile?

Wikipedia provides a more-than-adequate description of ICC color profiles: "In color management, an ICC profile is a set of data that characterizes a color input or output device, or a color space, according to standards promulgated by the International Color Consortium (ICC). Profiles describe the color attributes of a particular device or viewing requirement by defining a mapping between the device source or target color space and a profile connection space (PCS). This PCS is either CIELAB (L*a*b*) or CIEXYZ. Mappings may be specified using tables, to which interpolation is applied, or through a series of parameters for transformations.

In simpler terms, an ICC color profile is a binary file that gets embedded into images and parsed whenever ICC supported software processes the images. 

Specification

The ICC specification is around 100 pages and should be easy to skim through. Reading through specifications gives a better understanding of the file format, different types of color profiles, and math behind the color transformation. Furthermore, understanding of its file format internals provides us with information that can be used to optimize fuzzing, select a good corpus, and prepare fuzzing dictionaries.

History of Color Management in Windows

Windows started to ship Image Color Management (ICM) version 1.0 on Windows 95, and version 2.0 beginning with Windows 98 onwards. A major overhaul to Windows Color System (WCS) 1.0 happened in Windows Vista onwards. While ICC color profiles are binary files, WCS color profiles use XML as its file format. In this blog post, I am going to concentrate on ICC color profiles.

Microsoft has a list of supported Windows APIs. Looking into some of the obviously named APIs, such as OpenColorProfile, we can see that it is implemented in MSCMS.dll. This DLL is a generic entry point and supports loading of Microsoft’s Color Management Module (CMM) and third-party CMMs such as Adobe’s CMM. Microsoft’s CMM—the ICM—can be found as ICM32.dll in system32 directory. 


Figure 1: ICM32

Windows’ CMM was written by a third-party during the Windows 95 era and still ships more or less with the same code (with security fixes over the decades). Seeing such an old module gives me some hope of finding a new vulnerability. But this is also a small module that may have gone through multiple rounds of review and fuzzing: both by internal product security teams and by external researchers, reducing my hopes to a certain degree. Looking for any recent vulnerabilities in ICM32, we can see multiple bugs from 2017-2018 by Project Zero and ZDI researchers, but then relative silence from 2019 onwards.

Making a Harness

Although there is a list of ICM APIs in MSDN, we need to find an API sequence used by Windows for any ICC related operations. One of the ways to find our API sequence is to search a disassembly of Windows DLLs and EXEs in hope to find the color profile APIs being used. Another approach is to find a harness for open source Color Management Systems such as Little CMS (LCMS). Both of these end up pointing to very small set of APIs with functionality to open color profiles and create color transformations.

Given this information, a simple initial harness was written: 

#include <stdio.h>
#include <Windows.h>
#include <Icm.h>

#pragma comment(lib, "mscms.lib")

int main(int argc, char** argv)
{
    char dstProfilePath[] = "sRGB Color Space Profile.icm";
    tagPROFILE destinationProfile;
    HPROFILE   hDstProfile = nullptr;   

    destinationProfile.dwType = PROFILE_FILENAME;
    destinationProfile.pProfileData = dstProfilePath;
    destinationProfile.cbDataSize = (strlen(dstProfilePath) + 1);

    hDstProfile = OpenColorProfileA(&destinationProfile, PROFILE_READ,
        FILE_SHARE_READ, OPEN_EXISTING);
    if (nullptr == hDstProfile)
    {
        return -1;
    }   

    tagPROFILE sourceProfile;
    HPROFILE   hSrcProfile = nullptr;
    HTRANSFORM hColorTransform = nullptr;     

    DWORD dwIntent[] = { INTENT_PERCEPTUAL, INTENT_PERCEPTUAL };
    HPROFILE hProfileList[2];   

    sourceProfile.dwType = PROFILE_FILENAME;
    sourceProfile.pProfileData = argv[1];
    sourceProfile.cbDataSize = (strlen(argv[1]) + 1);

    hSrcProfile = OpenColorProfileA(&sourceProfile, PROFILE_READ,
        FILE_SHARE_READ, OPEN_EXISTING);
    if (nullptr == hSrcProfile)
    {
        return -1;
    }   

    hProfileList[0] = hSrcProfile;
    hProfileList[1] = hDstProfile;

    hColorTransform = CreateMultiProfileTransform(
        hProfileList,
        2,
        dwIntent,
        2,
        USE_RELATIVE_COLORIMETRIC | BEST_MODE,
        INDEX_DONT_CARE
    );

    if (nullptr == hColorTransform)
    {
        return -1;
    }   

    DeleteColorTransform(hColorTransform);
    CloseColorProfile(hSrcProfile);
    CloseColorProfile(hDstProfile);
    return 0;
}

Listing 1: Harness

Hunting for Corpus and Dictionary

Sites offering multiple color profiles can be found all over the internet. One of the other main source of color profile is images; many image files contain a color profile but require some programming/tools to dump their color profile to stand-alone files.

Simply skimming through the specification, we can also make sure the corpus contains at least one sample from all of the seven different color profiles. This along with the code coverage information can be used to prepare the first set of corpuses for fuzzing.

A dictionary, which helps the fuzzer to find additional code paths, can be prepared by combing through specifications and creating a list of unique tag names and values. One can also find dictionaries from open source fuzzing attempts on LCMS, etc.

Fuzzing

I used a 16-core machine to fuzz the harness with my first set of corpuses. Code coverage information from MSCMS.dll and ICM32.dll was used as feedback for my fuzzer. Crashes started to appear within a couple of days.

CVE-2020-1117 — Heap Overflow in InitNamedColorProfileData

The following crash happens in icm32!SwapShortOffset while trying to read out of bounds:

0:000> r
rax=0000023690497000 rbx=0000000000000000 rcx=00000000000000ff
rdx=000000000000ffff rsi=0000023690496f00 rdi=0000023690496fee
rip=00007ffa46bf3790 rsp=000000c2a56ff5a8 rbp=0000000000000001
 r8=0000000000000014  r9=0000023690497002 r10=0000000000000014
r11=0000000000000014 r12=000000c2a56ff688 r13=0000023690492de0
r14=000000000000000a r15=000000004c616220
iopl=0         nv up ei ng nz ac pe cy
cs=0033  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000293
icm32!SwapShortOffset+0x10:
00007ffa`46bf3790 0fb610          movzx   edx,byte ptr [rax] ds:00000236`90497000=??

0:000> !heap -p -a @rax
    address 0000023690497000 found in
    _DPH_HEAP_ROOT @ 23690411000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                             23690412b60:      23690496f00              100 -      23690496000             2000
    00007ffa51644807 ntdll!RtlDebugAllocateHeap+0x000000000000003f
    00007ffa515f49d6 ntdll!RtlpAllocateHeap+0x0000000000077ae6
    00007ffa5157babb ntdll!RtlpAllocateHeapInternal+0x00000000000001cb
    00007ffa51479da0 msvcrt!malloc+0x0000000000000070
    00007ffa46bf3805 icm32!SmartNewPtr+0x0000000000000011
    00007ffa46bf37c8 icm32!SmartNewPtrClear+0x0000000000000014
    00007ffa46c02d05 icm32!InitNamedColorProfileData+0x0000000000000085
    00007ffa46bf6e39 icm32!Create_LH_ProfileSet+0x0000000000004e15
    00007ffa46bf1973 icm32!PrepareCombiLUTs+0x0000000000000117
    00007ffa46bf1814 icm32!CMMConcatInitPrivate+0x00000000000001f4
    00007ffa46bf12a1 icm32!CWConcatColorWorld4MS+0x0000000000000075
    00007ffa46bf11f4 icm32!CMCreateMultiProfileTransformInternal+0x00000000000000e8
    00007ffa46bf1039 icm32!CMCreateMultiProfileTransform+0x0000000000000029
    00007ffa48f16e6c mscms!CreateMultiProfileTransform+0x000000000000024c
    00007ff774651191 ldr+0x0000000000001191
    00007ff7746514b4 ldr+0x00000000000014b4
    00007ffa505a7bd4 KERNEL32!BaseThreadInitThunk+0x0000000000000014
    00007ffa515aced1 ntdll!RtlUserThreadStart+0x0000000000000021

Listing 2: Crash info

icm32!SwapShortOffset reads unsigned short values, bswaps them and stores at the same location, giving this crash both read and write primitives.

unsigned __int16 *__fastcall SwapShortOffset(void *sourceBuff, unsigned int offset, unsigned int len)
{
  unsigned __int16 *endBuff; // r9
  unsigned __int16 *result; // rax

  endBuff = (sourceBuff + len);
  for ( result = (sourceBuff + offset); result < endBuff; ++result )
    *result = _byteswap_ushort(*result);        // read, bswap and write
  return result;
}

Listing 3: SwapShortOffset decompiled

The crashing function icm32!SwapShortOffset doesn’t immediately point to the root cause of the bug. For that, we need to go one call up to icm32!InitNamedColorProfileData.

__int64 __fastcall InitNamedColorProfileData(__int64 a1, void *hProfile, int a3, _DWORD *a4)
{
  ...
  ...
  errCode = CMGetPartialProfileElement(hProfile, 'ncl2', 0, pBuffSize, 0i64);      // getting size of ncl2 element
  if ( errCode )
    return errCode;
  minSize = pBuffSize[0];
  if ( pBuffSize[0] < 0x55 )
    minSize = 0x55;
  pBuffSize[0] = minSize;
  outBuff = SmartNewPtrClear(minSize, &errCode);                                    // allocating the buffer for ncl2
  ...
  ...
  errCode = CMGetPartialProfileElement(hProfile, 'ncl2', 0, pBuffSize, outBuff);    // reading ncl2 elements to buffer
  if ( !errCode )
  {
    ...
    ...
    totalSizeToRead = count * totalDeviceCoord;
    if ( totalSizeToRead < 0xFFFFFFFFFFFFFFAEui64 && totalSizeToRead + 0x51 <= pBuffSize[0] )  // totalSizeToRead + 0x51 <= element size?
    {
      currPtr = outBuff + 0x54;            // wrong offset of 0x54 is used
      ...
      ...
      do
      {   
        SwapShortOffset((currPtr + 0x20), 0, 6u);
        ...
        --count;
      }while(count)

Listing 4: InitNamedColorProfileData decompiled

Here the code tries to read the ‘ncl2’ tag/element and get the size of the stream from file. A buffer is allocated and the same call is made once again to read the complete content of the element ‘ncl2’. This buffer is parsed to find the count and number of device coordinates, and the values are verified by making sure read/write ends up with in the buffer size. The vulnerability here is that the offset (0x51) used for verification is smaller than the offset (0x54) used to advance the buffer pointer. This error provides a 3 byte out of bound read and write.

The fix for this was pretty straight forward—change the verification offset to 0x54, which is how Microsoft fixed this bug.

Additional Vulnerabilities

While looking at the previous vulnerability, one can see a pattern of using the CMGetPartialProfileElement function for reading the size, allocation, and reading content. This sort of pattern can introduce bugs such as unconstrained size or integer overflow while adding an offset to the size, etc. I decided to pursue this function and see if such instances are present within ICM32.dll.

I found three instances which had an unchecked offset access: CMConvIndexToNameProfile, CMConvNameToIndexProfile and CMGetNamedProfileInfoProfile. All of these functions are accessible through exported and documented MSCMS functions: ConvertIndexToColorName, CMConvertColorNameToIndex, and GetNamedProfileInfo respectively.

__int64 __fastcall CMConvIndexToNameProfile(HPROFILE hProfile, __int64 a2, __int64 a3, unsigned int a4)
{
  ...
  ...
  errCode = CMGetPartialProfileElement(hProfile, 'ncl2', 0, pBuffSize, 0i64);    // read size
  if ( !errCode )
  {
    allocBuff = SmartNewPtr(pBuffSize[0], &errCode);
    if ( !errCode )
    {
      errCode = CMGetPartialProfileElement(hProfile, 'ncl2', 0, pBuffSize, allocBuff);    // read to buffer
      if ( !errCode )
      {
        SwapLongOffset((allocBuff + 12), 0, 4u);         // 12 > *pBuffSize ?
        SwapLongOffset((allocBuff + 16), v12, v13);

Listing 5: CMConvIndexToNameProfile decompiled

The bug discovered in CMConvIndexToNameProfile and the other two functions is that there is no minimum length check for ‘ncl2’ elements and offsets 12 and 16 are directly accessed for both read and write—providing out of bound read/write to allocBuffer, if the size of allocBuffer is smaller than 12.

Microsoft decided not to immediately fix these three vulnerabilities due to the fact that none of the Windows binaries use these functions. Independently, we did not find any Windows or third-party software using these APIs.

Conclusion

In part one of this blog series, we looked into color profiles, wrote a harness, hunted for corpus and successfully found multiple vulnerabilities. Stay tuned for part two, where we will be looking at a relatively less talked about vulnerability class: uninitialized memory.

Six Facts about Address Space Layout Randomization on Windows

Overcoming address space layout randomization (ASLR) is a precondition of virtually all modern memory corruption vulnerabilities. Breaking ASLR is an area of active research and can get incredibly complicated. This blog post presents some basic facts about ASLR, focusing on the Windows implementation. In addition to covering what ASLR accomplishes to improve security posture, we aim to give defenders advice on how to improve the security of their software, and to give researchers more insight into how ASLR works and ideas for investigating its limitations.

Memory corruption vulnerabilities occur when a program mistakenly writes attacker-controlled data outside of an intended memory region or outside intended memory’s scope. This may crash the program, or worse, provide the attacker full control over the system. Memory corruption vulnerabilities have plagued software for decades, despite efforts by large companies like Apple, Google, and Microsoft to eradicate them.

Since these bugs are hard to find and just one can compromise a system, security professionals have designed failsafe mechanisms to thwart software exploitation and limit the damage should a memory corruption bug be exploited. A “silver bullet” would be a mechanism to make exploits so tricky and unreliable that buggy code can be left in place, giving developers the years they need to fix or rewrite code in memory-safe languages. Unfortunately, nothing is perfect, but address space layout randomization (ASLR) is one of the best mitigations available.

ASLR works by breaking assumptions that developers could otherwise make about where programs and libraries would lie in memory at runtime. A common example is the locations of gadgets used in return-oriented programming (ROP), which is often used to defeat the defense of data execution prevention (DEP). ASLR mixes up the address space of the vulnerable process—the main program, its dynamic libraries, the stack and heap, memory-mapped files, and so on—so that exploit payloads must be uniquely tailored to however the address space of the victim process is laid out at the time. Writing a worm that propagates by blindly sending a memory corruption exploit with hard-coded memory addresses to every machine it can find is bound to fail. So long as the target process has ASLR enabled, the exploit’s memory offsets will be different than what ASLR has selected. This crashes the vulnerable program rather than exploiting it.

Fact 1: ASLR was introduced in Windows Vista. Pre-Vista versions of Windows lacked ASLR; worse, they went to great lengths to maintain a consistent address space across all processes and machines.

Windows Vista and Windows Server 2008 were the first releases to feature support for ASLR for compatible executables and libraries. One might assume that prior versions simply didn’t randomize the address space, and instead simply loaded DLLs at whatever location was convenient at the time—perhaps a predictable one, but not necessarily the same between two processes or machines. Unfortunately, these old Windows versions instead went out of their way to achieve what we’ll call “Address Space Layout Consistency”. Table 1 shows the “preferred base address” of some core DLLs of Windows XP Service Pack 3.

DLL

Preferred Base Address

ntdll

0x7c900000

kernel32

0x7c800000

user32

0x7e410000

gdi32

0x77f10000

Table 1: Windows DLLs contain a preferred base address used whenever possible if ASLR is not in place

When creating a process, pre-Vista Windows loads each of the program’s needed DLLs at its preferred base address if possible. If an attacker finds a useful ROP gadget in ntdll at 0x7c90beef, for example, the attacker can assume that it will always be available at that address until a future service pack or security patch requires the DLLs to be reorganized. This means that attacks on pre-Vista Windows can chain together ROP gadgets from common DLLs to disable DEP, the lone memory corruption defense on those releases.

Why did Windows need to support preferred base addresses? The answer lies in performance and in trade-offs made in the design of Windows DLLs versus other designs like ELF shared libraries. Windows DLLs are not position independent. Especially on 32-bit machines, if Windows DLL code needs to reference a global variable, the runtime address of that variable gets hardcoded into the machine code. If the DLL gets loaded at a different address than was expected, relocation is performed to fix up such hardcoded references. If the DLL instead gets loaded as its preferred base address, no relocation is necessary, and the DLL’s code can be directly mapped into memory from the file system.

Directly mapping the DLL file into memory is a small performance benefit since it avoids reading any of the DLL’s pages into physical memory until they are needed. A better reason for preferred base addresses is to ensure that only one copy of a DLL needs to be in memory. Without them, if three programs run that share a common DLL, but each loads that DLL at a different address, there would be three DLL copies in memory, each relocated to a different base. That would counteract a main benefit of using shared libraries in the first place. Aside from its security benefits, ASLR accomplishes the same thing—ensuring that the address spaces of loaded DLLs won’t overlap and loading only a single copy of a DLL into memory—in a more elegant way. Because ASLR does a better job of avoiding overlap between address spaces than statically-assigned preferred load addresses ever could, manually assigning preferred base addresses provides no optimization on an ASLR-capable OS, and is not needed any longer in the development lifecycle.

Takeaway 1.1: Windows XP and Windows Server 2003 and earlier do not support ASLR.

Clearly, these versions have been out of support for years and should be long gone from production use. The more important observation relates to software developers who support both legacy and modern Windows versions. They may not realize that the exact same program can be more secure or less secure depending on what OS version is running. Developers who (still!) have a customer base of mixed ASLR and non-ASLR supporting Windows versions should respond to CVE reports accordingly. The exact same bug might appear non-exploitable on Windows 10 but be trivially exploitable on Windows XP. The same applies to Windows 10 versus Windows 8.1 or 7, as ASLR has become more capable with each version.

Takeaway 1.2: Audit legacy software code bases for misguided ideas about preferred load addresses. 

Legacy software may still be maintained with old tools such as Microsoft Visual C++ 6. These development tools contain outdated documentation about the role and importance of preferred load addresses. Since these old tools cannot mark images as ASLR-compatible, a “lazy” developer who doesn’t bother to change the default DLL address is actually better off since a conflict will force the image to be rebased to an unpredictable location!

Fact 2: Windows loads multiple instances of images at the same location across processes and even across users; only rebooting can guarantee a fresh random base address for all images.

ELF images, as used in the Linux implementation of ASLR, can use position-independent executables and position-independent code in shared libraries to supply a freshly randomized address space for the main program and all its libraries on each launch—sharing the same machine code between multiple processes even where it is loaded at different addresses. Windows ASLR does not work this way. Instead, each DLL or EXE image gets assigned a random load address by the kernel the first time it is used, and as additional instances of the DLL or EXE are loaded, they receive the same load address. If all instances of an image are unloaded and that image is subsequently loaded again, the image may or may not receive the same base address; see Fact 4. Only rebooting can guarantee fresh base addresses for all images systemwide.

Since Windows DLLs do not use position-independent code, the only way their code can be shared between processes is to always be loaded at the same address. To accomplish this, the kernel picks an address (0x78000000 for example on 32-bit system) and begins loading DLLs at randomized addresses just below it. If a process loads a DLL that was used recently, the system may just re-use the previously chosen address and therefore re-use the previous copy of that DLL in memory. The implementation solves the issues of providing each DLL a random address and ensuring DLLs don’t overlap at the same time.

For EXEs, there is no concern about two EXEs overlapping since they would never be loaded into the same process. There would be nothing wrong with loading the first instance of an EXE at 0x400000 and the second instance at 0x500000, even if the image is larger than 0x100000 bytes. Windows just chooses to share code among multiple instances of a given EXE.

Takeaway 2.1: Any Windows program that automatically restarts after crashing is especially susceptible to brute force attacks to overcome ASLR. 

Consider a program that a remote attacker can execute on demand, such as a CGI program, or a connection handler that executes only when needed by a super-server (as in inetd, for example). A Windows service paired with a watchdog that restarts the service when it crashes is another possibility. An attacker can use knowledge of how Windows ASLR works to exhaust the possible base addresses where the EXE could be loaded. If the program crashes and (1) another copy of the program remains in memory, or (2) the program restarts quickly and, as is sometimes possible, receives the same ASLR base address, the attacker can assume that the new instance will still be loaded at the same address, and the attacker will eventually try that same address.

Takeaway 2.2: If an attacker can discover where a DLL is loaded in any process, the attacker knows where it is loaded in all processes. 

Consider a system running two buggy network services—one that leaks pointer values in a debug message but has no buffer overflows, and one that has a buffer overflow but does not leak pointers. If the leaky program reveals the base address of kernel32.dll and the attacker knows some useful ROP gadgets in that DLL, then the same memory offsets can be used to attack the program containing the overflow. Thus, seemingly unrelated vulnerable programs can be chained together to first overcome ASLR and then launch an exploit.

Takeaway 2.3: A low-privileged account can be used to overcome ASLR as the first step of a privilege escalation exploit. 

Suppose a background service exposes a named pipe only accessible to local users and has a buffer overflow. To determine the base address of the main program and DLLs for that process, an attacker can simply launch another copy in a debugger. The offsets determined from the debugger can then be used to develop a payload to exploit the high-privileged process. This occurs because Windows does not attempt to isolate users from each other when it comes to protecting random base addresses of EXEs and DLLs.

Fact 3: Recompiling a 32-bit program to a 64-bit one makes ASLR more effective.

Even though 64-bit releases of Windows have been mainstream for a decade or more, 32-bit user space applications remain common. Some programs have a true need to maintain compatibility with third-party plugins, as in the case of web browsers. Other times, development teams have a belief that a program needs far less than 4 GB of memory and 32-bit code could therefore be more space efficient. Even Visual Studio remained a 32-bit application for some time after it supported building 64-bit applications.

In fact, switching from 32-bit to 64-bit code produces a small but observable security benefit. The reason is that the ability to randomize 32-bit addresses is limited. To understand why, observe how a 32-bit x86 memory address is broken down in Figure 1. More details are explained at Physical Address Extension.


Figure 1: Memory addresses are divided into components, only some of which can be easily randomized at runtime

The operating system cannot simply randomize arbitrary bits of the address. Randomizing the offset within a page portion (bits 0 through 11) would break assumptions the program makes about data alignment. The page directory pointer (bits 30 and 31) cannot change because bit 31 is reserved for the kernel, and bit 30 is used by Physical Address Extension as a bank switching technique to address more than 2GB of RAM. This leaves 14 bits of the 32-bit address off-limits for randomization.

In fact, Windows only attempts to randomize 8 bits of a 32-bit address. Those are bits 16 through 23, affecting only the page directory entry and page table entry portion of the address. As a result, in a brute force situation, an attacker can potentially guess the base address of an EXE in 256 guesses.

When applying ASLR to a 64-bit binary, Windows is able to randomize 17-19 bits of the address (depending on whether it is a DLL or EXE). Figure 2 shows how the number of possible base addresses, and accordingly the number of brute force guesses needed, increases dramatically for 64-bit code. This could allow endpoint protection software or a system administrator to detect an attack before it succeeds.


Figure 2: Recompiling 32-bit code as 64-bit dramatically increases the number of possible base addresses for selection by ASLR

Takeaway 3.1: Software that must process untrusted data should always be compiled as 64-bit, even if it does not need to use a lot of memory, to take maximum advantage of ASLR.

In a brute force attack, ASLR makes attacking a 64-bit program at least 512 times harder than attacking the 32-bit version of the exact same program.

Takeaway 3.2: Even 64-bit ASLR is susceptible to brute force attacks, and defenders must focus on detecting brute force attacks or avoiding situations where they are feasible.

Suppose an attacker can make ten brute force attempts per second against a vulnerable system. In the common case of the target process remaining at the same address because multiple instances are running, the attacker would discover the base address of a 32-bit program in less than one minute, and of a 64-bit program in a few hours. A 64-bit brute force attack would produce much more noise, but the administrator or security software would need to notice and act on it. In addition to using 64-bit software to make ASLR more effective, systems should avoid re-spawning a crashing process (to avoid giving the attacker a “second bite at the apple” to discover the base address) or force a reboot and therefore guaranteed fresh address space after a process crashes more than a handful of times.

Takeaway 3.3: Researchers developing a proof of concept attack against a program available in both 32-bit and 64-bit versions should focus on the 32-bit one first.

As long as 32-bit software remains relevant, a proof-of-concept attack against the 32-bit variant of a program is likely easier and quicker to develop. The resulting attack could be more feasible and convincing, leading the vendor to patch the program sooner.

Fact 4: Windows 10 reuses randomized base addresses more aggressively than Windows 7, and this could make it weaker in some situations.

Observe that even if a Windows system must ensure that multiple instances of one DLL or EXE all get loaded at the same base address, the system need not keep track of the base address once the last instance of the DLL or EXE is unloaded. If the DLL or EXE is loaded again, it can get a fresh base address.

This is the behavior we observed in working with Windows 7. Windows 10 can work differently. Even after the last instance of a DLL or EXE unloads, it may maintain the same base address at least for a short period of time—more so for EXEs than DLLs. This can be observed when repeatedly launching a command-line utility under a multi-process debugger. However, if the utility is copied to a new filename and then launched, it receives a fresh base address. Likewise, if a sufficient duration has passed, the utility will load at a different base address. Rebooting, of course, generates fresh base addresses for all DLLs and EXEs.

Takeaway 4.1: Make no assumptions about Windows ASLR guarantees beyond per-boot randomization.

In particular, do not rely on the behavior of Windows 7 in randomizing a fresh address space whenever the first instance of a given EXE or DLL loads. Do not assume that Windows inherently protects against brute force attacks against ASLR in any way, especially for 32-bit processes where brute force attacks can take 256 or fewer guesses.

Fact 5: Windows 10 is more aggressive at applying ASLR, and even to EXEs and DLLs not marked as ASLR-compatible, and this could make ASLR stronger.

Windows Vista and 7 were the first two releases to support ASLR, and therefore made some trade-offs in favor of compatibility. Specifically, these older implementations would not apply ASLR to an image not marked as ASLR-compatible and would not allow ASLR to push addresses above the 4 GB boundary. If an image did not opt in to ASLR, these Windows versions would continue to use the preferred base address.

It is possible to further harden Windows 7 using Microsoft’s Enhanced Mitigation Experience Toolkit (commonly known as EMET) to more aggressively apply ASLR even to images not marked as ASLR-compatible. Windows 8 introduced more features to apply ASLR to non-ASLR-compatible images, to better randomize heap allocations, and to increase the number of bits of entropy for 64-bit images.

Takeaway 5.1: Ensure software projects are using the correct linker flags to opt in to the most aggressive implementation of ASLR, and that they are not using any linker flags that weaken ASLR.

See Table 2. Linker flags can affect how ASLR is applied to an image. Note that for Visual Studio 2012 and later, the ✔️flags are already enabled by default and the best ASLR implementation will be used so long as no 🚫flags are used. Developers using Visual Studio 2010 or earlier, presumably for compatibility reasons, need to check which flags the linker supports and which it enables by default.

Secure?

Linker Flag

Effect

✔️

/DYNAMICBASE

Marks the image as ASLR-compatible

✔️

/LARGEADDRESSAWARE /HIGHENTROPYVA

Marks the 64-bit image as free of pointer truncation bugs and therefore allows ASLR to randomize addresses beyond 4 GB

🚫

/DYNAMICBASE:NO

“Politely requests” that ASLR not be applied by not marking the image as ASLR-compatible. Depending on the Windows version and hardening settings, Windows might apply ASLR anyway.

🚫

/HIGHENTROPYVA:NO

Opts out 64-bit images from ASLR randomizing addresses beyond 4 GB on Windows 8 and later (to avoid compatibility issues).

🚫

/FIXED

Removes information from the image that Windows needs in order to apply ASLR, blocking ASLR from ever being applied.

Table 2: Linker flags can affect how ASLR is applied to an image

Takeaway 5.2: Enable mandatory ASLR and bottom-up randomization.

Windows 8 and 10 contain optional features to forcibly enable ASLR on images not marked as ASLR compatible, and to randomize virtual memory allocations so that rebased images obtain a random base address. This is useful in the case where an EXE is ASLR compatible, but one of the DLLs it uses is not. Defenders should enable these features to apply ASLR more broadly, and importantly, to help discover any remaining non-ASLR-compatible software so it can be upgraded or replaced.

Fact 6: ASLR relocates entire executable images as a unit.

ASLR relocates executable images by picking a random offset and applying it to all addresses within an image that would otherwise be relative to its base address. That is to say:

  • If two functions in an EXE are at addresses 0x401000 and 0x401100, they will remain 0x100 bytes apart even after the image is relocated. Clearly this is important due to the prevalence of relative call and jmp instructions in x86 code. Similarly, the function at 0x401000 will remain 0x1000 bytes from the base address of the image, wherever it may be.
  • Likewise, if two static or global variables are adjacent in the image, they will remain adjacent after ASLR is applied.
  • Conversely, stack and heap variables and memory-mapped files are not part of the image and can be randomized at will without regard to what base address was picked.

Takeaway 6.1: A leak of just one pointer within an executable image can expose the randomized addresses of the entire image.

One of the biggest limitations and annoyances of ASLR is that seemingly innocuous features such as a debug log message or stack trace that leak a pointer in the image become security bugs.  If the attacker has a copy of the same program or DLL and can trigger it to produce the same leak, they can calculate the difference between the ASLR and pre-ASLR pointer to determine the ASLR offset. Then, the attacker can apply that offset to every pointer in their attack payload in order to overcome ASLR. Defenders should train software developers about pointer disclosure vulnerabilities so that they realize the gravity of this issue, and also regularly assess software for these vulnerabilities as part of the software development lifecycle.

Takeaway 6.2: Some types of memory corruption vulnerabilities simply lie outside the bounds of what ASLR can protect.

Not all memory corruption vulnerabilities need to directly achieve remote code execution. Consider a program that contains a buffer variable to receive untrusted data from the network, and a flag variable that lies immediately after it in memory. The flag variable contains bits specifying whether a user is logged in and whether the user is an administrator. If the program writes data beyond the end of the receive buffer, the “flags” variable gets overwritten and an attacker could set both the logged-in and is-admin flags. Because the attacker does not need to know or write any memory addresses, ASLR does not thwart the attack. Only if another hardening technique (such as compiler hardening flags) reordered variables, or better, moved the location of every variable in the program independently, would such attacks be blocked.

Conclusion

Address space layout randomization is a core defense against memory corruption exploits. This post covers some history of ASLR as implemented on Windows, and also explores some capabilities and limitations of the Windows implementation. In reviewing this post, defenders gain insight on how to build a program to best take advantage of ASLR and other features available in Windows to more aggressively apply it. Attackers can leverage ASLR limitations, such as address space randomization applying only per boot and randomization relocating the entire image as one unit, to overcome ASLR using brute force and pointer leak attacks.