Category Archives: Exploit mitigations

Disarming EMET 5.52: Controlling it all with a single write action

The Enhanced Mitigation Experience Toolkit (EMET) is a software solution from Microsoft that aims at preventing the exploitation of – legacy – software. It is designed to “help prevent attackers from gaining access to computer systems [and] anticipates the most common techniques attackers might use to exploit vulnerabilities in computer systems, and helps protect by diverting, terminating, blocking, and invalidating those actions and techniques.” [1]

This blog post presents a previously undocumented disarming technique that can be used to completely disable EMETs mitigations without requiring large, complicated ROP chains or shellcodes and a way to handle EAF in EMET 5.5x. As a result it will be possible to use a default ROP chain and shellcode generated by Metasploit.

TL;DR: The pointer to the read-only struct that stores all of EMET’s mitigation settings is always retrieved from the writable process heap. Hijacking this pointer only requires read-write access to memory and could enable an adversary to prevent EMET’s anti-ROP mitigations from ever being performed. EAF can optionally be subverted with a single, additional VirtualProtect call.

 

Bypassing tactics

As EMET is probably one of the most well-known exploit mitigation solutions around, it should not come as a surprise that a lot has already been written about EMET or exploit mitigation bypasses in general [3-19]. In general, a few ways can be chosen to bypass EMET:

  • Bypass individual mitigations by crafting a ROP chain and shellcode in such a way that all mitigation checks pass.
  • Jump over hooks or use system calls.
  • Use an implementation flaw to disable one or multiple mitigations.

 

Previous EMET disarming techniques

The third route will be taken in this blog post, as the number of new generic exploit mitigation bypasses is quite limited. Previous research performed by Offensive Security in 2014 gives an idea of what kind of implementation flaws – that could be used to disable EMETs mitigations – plagued it in the past.

EMET 4.1 [3, 4]
EMET relies on a DLL (EMET.dll) that is injected into every protected process to perform its mitigation checks. In EMET 4.1 all mitigation settings were stored as global variables in writable memory in the .data segment of EMET.dll. It should not come as a surprise that these settings could easily be manipulated. Offensive Security disabled the anti-ROP mitigation (ROP-P) switch at offset +0x0007e220 within EMET.dll to prevent all anti-ROP mitigations from ever being performed. Although other variables like the mitigation settings bitmap stored at offset +0x0007e21c could also be manipulated. Disabling EMET 4.1 using this implementation flaw would only require determining the base address of EMET.dll and zero-ing out either the ROP-P switch or the mitigation bitmask.

var emetBase:uint = ...;

// ROP-P switch
pe.writeDword(emetBase+0x7e220, 0x00000000); 

// Mitigation bitmask
//pe.writeDword(emetBase+0x7e21c, 0x00000000);

EMET 5.0 [5]

In EMET 5.0 some things changed. The ROP-P switch would now be stored on the heap instead in a structure that Offensive Security named CONFIG_STRUCT. Moreover, the address of the struct would also be encoded using the EncodePointer API. Offensive Security noticed that the ROP-P switch was still located in writable memory, thus making a new disarming opportunity possible. They obtained the address of the writable CONFIG_STRUCT structure by calling DecodePointer from a ROP chain. Offsec’s technique can be implemented using a ROP chain that calls DecodePointer with the encoded pointer as argument and that zeroes out the CONFIG_STRUCT->ROP-P field.

var ntdllBase:uint = ...;
var emetBase:uint = ...;

var DecodePointer:uint = pe.getProcAddress(ntdllBase, "RtlDecodePointer");
var encodedPtr:uint = pe.readDword(emetBase + 0xaa84c); 

rop[i++] = DecodePointer;     // Call DecodePointer with 'encodedPtr' as argument (CONFIG_STRUCT addr ends in eax)
rop[i++] = add_esp_0c;        // (add esp, 0x0c; ret) Return to 'pop_esi' further on the stack
rop[i++] = encodedPtr;
rop[i++] = 0x41414141;        // Padding
rop[i++] = 0x42424242;
rop[i++] = 0x43434343;
rop[i++] = pop_esi;           // (pop esi; ret) Place ROP-P offset within CONFIG_STRUCT (+0x558) in esi
rop[i++] = 0x558;
rop[i++] = add_eax_esi;       // (add eax, esi; ret) Add +0x558 to the CONFIG_STRUCT address to get the ROP-P swith addr

rop[i++] = pop_edx;           // (pop edx; ret) Place 0x00000000 in edx
rop[i++] = 0x00000000;
rop[i++] = mov_dword_eax_edx; // (mov [eax], edx; ret) Zero out the ROP-P switch

EMET 5.1 [6]
It took another release of EMET to fix this issue. Since EMET 5.1 all mitigation settings are stored in read-only memory. Offensive security used the unhooked version of the NtProtectVirtualMemory API to make the CONFIG_STRUCT structure writable again, even though this is more a limitation of exploit mitigation software in general than another disarming flaw.

EMET 5.2 and 5.5x
As far as the author of this blog could determine only two other disarming techniques for EMET were published since the series of publications from Offensive Security. One relying on functionality in EMET that could be used to remove all hooks [7] and one that hijacked the encoded CONFIG_STRUCT pointer that was stored in the writable .data segment of EMET.dll [8]. A review of Offsec’s Advanced Windows Exploitation (AWE) course indicates that Offensive Security applies a variant of their EMET 5.1 disarming technique to later versions of EMET [9].

 

EMET 5.52 Anti-ROP disarm

Since v5.0 EMET uses two sections of read-only heap memory to store two essential structures. The first one is the EMET_SETTINGS struct, which stores the mitigation settings that can be configured in the Application Configuration window of the EMET GUI.

EMET settings configurable per application

The second read-only struct – named CONFIG_STRUCT by Offsec – stores a pointer to the EMET_SETTINGS struct, the addresses of all hooked APIs and the addresses of the hook handlers (each hooked API has one).

struct CONFIG_STRUCT {
    LPVOID lpEMET_SETTINGS;
    ... // List of API / Hook handler addresses
};

In EMET 5.52 memory for the EMET_SETTINGS structure is allocated in a function at offset 0x0002139F within EMET.dll. In this function VirtualAlloc allocates 0x2000 bytes of memory for the EMET_SETTINGS struct. Subsequently, RtlAllocateHeap is used to allocate a block of memory of 0x24 bytes from the process heap for a struct that Offsec named EMETd. The EMETd struct stores the size of the EMET_SETTINGS or CONFIG_STRUCT struct, a pointer to one of these two structs and whether either of these structs is writable or not.

struct EMETd {
    LPCRITICAL_SECTION CriticalSection; // Reserved
    DWORD configSize;
    LPVOID lpConfigPtr;                 // EMET_SETTINGS / CONFIG_STRUCT ptr
    DWORD isWritable;
};

Finally, the pointer to the EMETd struct is encoded using EncodePointer and the memory storing the EMET_SETTINGS struct is marked as PAGE_READONLY. In a different function EMET_SETTINGS is filled with the actual mitigation settings that are stored in the Registry.

// Code simplified for clarity
int *init_emet_settings() {
    ...
    SIZE_T dwSize = 0x2000;
    EMET_SETTINGS *es = (EMET_SETTINGS *)VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    EMETd *emetd = (EMETd *)RtlAllocateHeap(*(HANDLE *)(__readfsdword(0x30u) + 0x18), HEAP_ZERO_MEMORY, 0x24);
    ...
    emetd->lpConfigPtr = EMET_SETTINGS;  // *((DWORD *)emetd + 6)
    emetd->configSize = dwSize           // *((DWORD *)emetd + 7)
    emetd->isWritable = 1;               // *((DWORD *)emetd + 8)

    InitializeCriticalSectionAndSpinCount((LPCRITICAL_SECTION)emetd, 0x8000FA0u);
    dword_100F2B90 = EncodePointer(emetd);
    mark_config_readonly((LPCRITICAL_SECTION)emetd);
    return &dword_100F2B90;
}

In a similar function (at offset 0x00025734 in EMET.dll) 0x1000 bytes of memory is allocated for the CONFIG_STRUCT struct.

// Code simplified for clarity
int *init_config_struct() {
    ...
    SIZE_T dwSize = 0x1000;
    CONFIG_STRUCT *cs = (CONFIG_STRUCT *)VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
    EMETd *emetd = (EMETd *)RtlAllocateHeap(*(HANDLE *)(__readfsdword(0x30u) + 0x18), HEAP_ZERO_MEMORY, 0x24);
    ...
    emetd->lpConfigPtr = CONFIG_STRUCT;  // *((DWORD *)emetd + 6)
    emetd->configSize = dwSize;          // *((DWORD *)emetd + 7)
    emetd->isWritable = 1;               // *((DWORD *)emetd + 8)

    InitializeCriticalSectionAndSpinCount((LPCRITICAL_SECTION)emetd, 0x8000FA0);
    dword_100F2BC8 = EncodePointer(emetd);
    mark_config_readonly((LPCRITICAL_SECTION)emetd);
    return &dword_100F2BC8;
}

In the end the address of the EMET_SETTINGS struct is only retrieved in one of the following two ways in EMET.dll:
1.

EMETd *emetd = (EMETd *)DecodePointer(EMET.dll+0x000f2bc8);
CONFIG_STRUCT *cs = (CONFIG_STRUCT *)emetd->lpConfigPtr;
EMET_SETTINGS *es = (EMET_SETTINGS *)cs->lpEMET_SETTINGS;

2.

EMETd *emetd = (EMETd *)DecodePointer(EMET.dll+0x000f2b90);
EMET_SETTINGS *es = (EMET_SETTINGS *)emetd->lpConfigPtr;

Both the init_emet_settings and the init_config_struct function form the basis of a previously undocumented disarming condition, due to the fact that the two EMETd structs are stored on the process heap. As the process heap has a PAGE_READWRITE protection, any adversary could control the contents of the two EMETd structs. The two structs store a pointer to either the EMET_SETTINGS or the CONFIG_STRUCT struct, making it is possible to control the mitigation settings in one way or another.

Breaking at a DecodePointer(EMET.dll+0x000f2bc8) call in EMET.dll also demonstrates the fact that the EMETd struct is stored in writable heap memory.

Disarming flaw visualized with a WinDbg breakpoint.

Preventing anti-ROP checks

One place where this disarming flaw can be (ab)used is in the function at offset 0x00060d50 within EMET.dll that determines which mitigation checks have to be performed. It should not come as an surprise that the mitigation_handler function relies on data from the CONFIG_STRUCT and EMET_SETTINGS structs.

The function obtains the address of the CONFIG_STRUCT structure by first decoding the address of the second EMETd struct and looking up the CONFIG_STRUCT address stored in the EMETd->lpConfigPtr field. Subsequently, an if-statement checks whether CONFIG_STRUCT->lpEMET_SETTINGS is zero. If it is not zero, the pointer should store the address of the EMET_SETTINGS structure. Finally, EMET_SETTINGS is consulted to find out which mitigation checks have to be performed.

// Code simplified for clarity
int mitigation_handler(int a1) {
    EMETd *emetd = (EMETd *)DecodePointer(dword_100F2BC8);
    CONFIG_STRUCT *cs = (CONFIG_STRUCT *)emetd->lpConfigPtr;

    /*
     * Make this check fail and no anti-ROP mitigation will be performed.
     * This can be done by making sure ((CONFIG_STRUCT *)EMETd->lpConfigPtr)->lpEMET_SETTINGS is zero
     */
    if ( cs->lpEMET_SETTINGS ) {
        EMET_SETTINGS *es = (EMET_SETTINGS *)cs->lpEMET_SETTINGS;
        int mitigation_bitmask = dword_100CC0D8[3 * DecodePointer(*(DWORD *)(a1 + 0x30))];
        if ( !(mitigation_bitmask & 0x100000) || es->MandatoryASLR != 1 || ... ) {
            ...
            if (...) {
                ...
                if ( mitigation_bitmask & 0x40 & es->StackPivot )
                    stackpivot_mitigation(...);
                if ( mitigation_bitmask & 0x10 & es->MemProt )
                    memprot_mitigation(...);
                if ( mitigation_bitmask & 0x100 & es->BannedFunctions )
                    bannedfunctions_mitigation(...);
                if ( *(BYTE *)(es + 0x44) ) {
                    if ( mitigation_bitmask & 0x400 & es->Caller )
                        caller_mitigation(...);
                    if ( mitigation_bitmask & 0x1000 & es->SimExecFlow )
                        simexecflow_mitigation(...);
                }
                if ( mitigation_bitmask & 0x4 & es->LoadLib )
                    loadlib_mitigation(...);
                if ( mitigation_bitmask & 0x4000 & es->ASR & !asr_mitigation(...) )
                    ...
            }
            ...
            if ( mitigation_bitmask & 0x10000 & es->EAF+ )
                eafplus_rop_mitigation(...);
            ...
        } else { ... }
    } else { ... }
    return ...;
}

As mentioned earlier, everything stored in one of the two EMETd structs, can be controlled from an exploit, if we make sure that ((CONFIG_STRUCT *)EMETd->lpConfigPtr)->lpEMET_SETTINGS is zero, then no single anti-ROP mitigation check will ever be performed.

This can be achieved by writing the address of an empty dword to EMETd->lpConfigPtr. If 0x00000000 is for example stored at the address 0x0c2c1200, then writing 0x0c2c1200 to EMETd->lpConfigPtr on the process heap, ensures that all anti-ROP mitigations will be skipped.

 

Exploitation

Locating the EMETd struct on the process heap is the trickiest part of the exploitation process. The location of the process heap cannot be guessed due to the randomized allocation of heap memory. Two routes can be taken to locate the EMETd struct:

  • Invoke DecodePointer(EMET.dll+0x000f2bc8) using a ROP chain
  • Perform a series of read operations

Simply calling DecodePointer(EMET.dll+0x000f2bc8) would require multiple ROP gadgets, therefore I will only focus on the route that relies on a series of read operations. This approach requires full read access to the process’ virtual address space, although nowadays most (browser) exploits will already have this ability to bypass ASLR.

Scanning the process heap

As seen in the figure below, the values 0x1000, 0x0 and 0x0 can be found on the process heap right after the EMET->lpConfigPtr field.

EMETd content (highlighted in red: EMETd->lpConfigPtr field)

These values can function as markers when scanning the process heap. When the location of the EMETd->lpConfigPtr field has been determined, it can be replaced with the address of a location that stores zero. Although this all first requires knowing the location of the process heap.

Locating the process heap
The location of the process heap does not appear to be reliably guessable due to the randomized allocation of heap memory, a reference to the process heap can still be obtained from the PEB via _PEB->ProcessHeap.

Locating the PEB
Searching for references of the PEB reveals that, at least on 32 and 64 bit versions of Windows 7, the .data of ntdll.dll stores the addresses of the following fields from the PEB:
⦁ _PEB->TlsBitmapBits (PEB+0x44)
⦁ _PEB->TlsExpansionBitmapBits (PEB+0x154)
⦁ _PEB->FlsBitmapBits (PEB+0x21c)

Addresses of several PEB fields stored in the .data segment of ntdll.dll

Summary
Combining the steps mentioned above results in a disarming solution that only requires read-write access to memory, that does not touch EMET.dll and that only relies on writing a single dword to memory. To summarize, this flaw can be used to prevent most of EMETs mitigations in the following way:

  1. Determine the location of the PEB using addresses of several fields of the PEB that are stored in the .data segment of ntdll.dll
  2. Get the address of the process heap from _PEB->ProcessHeap
  3. Scan the process heap for the EMETd struct
  4. Overwrite the EMETd->lpConfigPtr field on the process heap with with the address of a location that stores zero.

After the final write action, it has been ensured that all anti-ROP mitigations will be skipped. How about the other mitigations?

 

Heap Spray Allocation

By default, EMET pre-allocates several regions of memory based on a specified list of addresses (like 0x0a0a0a0a and 0x0c0c0c0c) that might be used by off-the-shelf exploits that contain a heap spray. This mitigation does not form a barrier as a ROP chain and shellcode can be stored at other addresses on the heap.

 

EAF+

As just about all modern browsers should be properly ASLR’ed at this point in time, bypassing ASLR would require some kind an info leak. This could either require an additional out-of-bounds (OOB) read vulnerability or a vulnerability that can be used to create a read-write primitive to the virtual address space of the exploited process. The second way seems to have become the standard way. Creating this RW primitive can for example be achieved by corrupting the length field of an array object [2]. After this RW primitive has been created, the content of the process’ dlls can be parsed by the exploit in search of APIs like VirtualProtect and ROP gadgets. EAF+ is designed to complicate this type of memory read-outs [1]. EAF+ does not prevent the corruption of the length field of an array object, but should make it harder for an attacker to obtain useful information from loaded dlls. Of the four sub-mitigations of EAF+, the following two are most relevant:

  • [Detection of] memory read access to export table pointers of KERNEL32, NTDLL and KERNELBASE originated from specific modules
  • [Detection of] memory read accesses to the MZ/PE header of specific modules [1]

Unfortunately, EAF+ appears to be malfunctioning since EMET 5.5, therefore there is no need to bypass it. If it would have worked properly, it would have been a powerful mitigation. The author of this blog post is not aware of any previous publicly documented EAF+ bypasses that do not rely on hardcoding offsets.

 

Export Address Table Access Filtering (EAF)

The task of EAF is to disrupt shellcodes that parse the export table of certain modules in search of certain APIs [1]. In earlier versions of EMET, hardware breakpoints placed on the IMAGE_EXPORT_DIRECTORY.AddressOfFunctions field in kernel32.dll, kernelbase.dll and ntdll.dll would be used to detect these actions. In EMET 5.5x, EAF appears to use a guard page instead (even though this is not documented anywhere).

In EMET 5.5 and 5.51 this guard page is placed on the Export Directory of ntdll.dll.

Guard page on Exports Directory of ntdll.dll under EMET 5.5 / 5.51

Whereas in EMET 5.52 it can be found on the MZ/PE header of ntdll.dll.

Guard page on PE/MZ header of ntdll.dll under EMET 5.52

As no single anti-ROP mitigations will be performed at this point, VirtualProtect can be called from a standard ROP chain to change the protection of the guard page. This subverts the guard page and effectively prevents EAF from functioning, making it is possible to execute a standard Metasploit shellcode.

// NTDLL.dll MZ/PE header protection: PAGE_READONLY + PAGE_GUARD --> PAGE_READONLY
rop[i++] = VirtualProtect; // Call VirtualProtect
rop[i++] = ...;            // Return address

// VirtualProtect() arguments
rop[i++] = ntdllBase       // lpAddress
rop[i++] = 0x1000;         // dwSize
rop[i++] = 0x2;            // flNewProtect (PAGE_READONLY)
rop[i++] = evAddr + 0x20;  // lpflOldProtect

The choice was made to use an unmodified shellcode generated by Metasploit, this has as disadvantage that it will be caught by EAF unless the mitigation gets disabled. Alternatively, a shellcode may be used that extracts addresses of APIs from the Import Address Table (IAT) instead of the Export Address Table (EAT). Previous research indicates that EAF will not prevent the execution of this kind of shellcodes [15, 18], although this has not been tested by the author of this blog.

 

Reference list

[1] “EMET 5.52 User Guide” by Microsoft, https://www.microsoft.com/en-us/download/details.aspx?id=54265
[2] “ASLR Bypass Apocalypse in Recent Zero-Day Exploits” by Xiaobo Chen (FireEye), https://www.fireeye.com/blog/threat-research/2013/10/aslr-bypass-apocalypse-in-lately-zero-day-exploits.html

Previous EMET disarming flaws
[3] “Fun with Dr. Brown” by Spencer McIntyre (SecureState), http://www.slideshare.net/zeroSteiner/fun-with-dr-brown
[4] “Disarming Enhanced Mitigation Experience Toolkit (EMET)” by Offensive Security, https://www.offensive-security.com/vulndev/disarming-enhanced-mitigation-experience-toolkit-emet/
[5] “Disarming EMET v5.0” by Offensive Security, https://www.offensive-security.com/vulndev/disarming-emet-v5-0/
[6] “Disarming and Bypassing EMET 5.1” by Offensive Security, https://www.offensive-security.com/vulndev/disarming-and-bypassing-emet-5-1/
[7] “Using EMET to disable EMET” by Abdulellah Alsaheel (Mandiant) and Raghav Pande (FireEye), https://www.fireeye.com/blog/threat-research/2016/02/using_emet_to_disabl.html
[8] “Look Mom, I don’t use shellcode” by Moritz Jodeit (Blue Frost Security Research Lab),
https://labs.bluefrostsecurity.de/files/Look_Mom_I_Dont_Use_Shellcode-WP.pdf
[9] “Our pentester on tour: Advanced Windows Exploitation course review” by Wesley Neelen (DearBytes), https://www.dearbytes.com/blog/advanced-windows-exploitation-review/

Previous EMET mitigation bypasses
[10] “Defeating EMET 5.2 & 5.5” by Raghav Pande, https://casual-scrutiny.blogspot.com/2015/03/defeating-emet-52.html
[11] “Defeating EMET Protections (2)” by Raghav Pande, https://casual-scrutiny.blogspot.com/2015/03/defeating-emet-52-protections-2.html
[12] “WoW64 and So Can You – Bypassing EMET With a Single Instruction” by Darren Kemp and Mikhail Davidov (Duo Security), https://duo.com/assets/pdf/wow-64-and-so-can-you.pdf
[13] “Bypassing EMET 4.1” by Jared DeMott (Bromium), https://bromiumlabs.files.wordpress.com/2014/02/bypassing-emet-4-1.pdf
[14] “Mitigating Wow64 Exploit Attacks” by SurfRight, https://hitmanpro.wordpress.com/2015/11/10/mitigating-wow64-exploit-attacks/
[15] “Teaching Old Shellcode New Tricks” by Josh Pitts, https://recon.cx/2017/brussels/resources/slides/RECON-BRX-2017-Teaching_Old_Shellcode_New_Tricks.pdf

Generic (partial) ITW exploit mitigation bypasses
[16] “Deep Dive into ROP Payload Analysis” by Sudeep Singh, https://dl.packetstormsecurity.net/papers/general/rop-deepdive.pdf
[17] “How the Wolf attacked and outsmarted defenses with CVE-2015-3113” by SurfRight, https://hitmanpro.wordpress.com/2015/07/02/how-apt3-evaded-anti-exploits-with-cve-2015-3113/
[18] “CVE-2015-2545 ITW EMET Evasion” by Raghav Pande, https://casual-scrutiny.blogspot.com/2016/02/cve-2015-2545-itw-emet-evasion.html
[19] “Angler Exploit Kit evading EMET” by Raghav Pande and Amit Malik (FireEye), https://www.fireeye.com/blog/threat-research/2016/06/angler_exploit_kite.html

 

Appendix – Timeline

May 2016 Disarming flaw reported to MSRC
1 August 2016 EMET 5.51 released (flaw unfixed)
14 November 2016 EMET 5.52 released (flaw unfixed)
January 2017 Last contact with MSRC (“This issue should be addressed in [the next] version whenever it is finally released”)

 

Appendix – Pre-allocated heap pages

Specified address Pre-allocated range
0x04040404 0x04040000 – 0x04042000
0x05050505 0x05050000 – 0x05052000
0x06060606 0x06060000 – 0x06062000
0x07070707 0x07070000 – 0x07072000
0x08080808 0x08080000 – 0x08082000
0x09090909 0x09090000 – 0x09092000
0x0a040a04 0x0a040000 – 0x0a042000
0x0a0a0a0a 0x0a0a0000 – 0x0a0a2000
0x0b0b0b0b 0x0b0b0000 – 0x0b0b2000
0x0c0c0c0c 0x0c0c0000 – 0x0c0c2000
0x0d0d0d0d 0x0d0d0000 – 0x0d0d2000
0x0e0e0e0e 0x0e0e0000 – 0x0e0e2000
0x14141414 0x14140000 – 0x14143000
0x20202020 0x20200000 – 0x20204000

 

Appendix – Hooked functions

kernel32!CreateFileA
kernel32!CreateFileMappingA
kernel32!CreateFileMappingWStub
kernel32!CreateFileWImplementation
kernel32!CreateProcessA
kernel32!CreateProcessInternalA
kernel32!CreateProcessInternalW
kernel32!CreateProcessW
kernel32!CreateRemoteThreadStub
kernel32!GetProcessDEPPolicy
kernel32!HeapCreateStub
kernel32!LoadLibraryA
kernel32!LoadLibraryExAStub
kernel32!LoadLibraryExWStub
kernel32!LoadLibraryW
kernel32!MapViewOfFileExStub
kernel32!MapViewOfFileStub
kernel32!SetProcessDEPPolicy
kernel32!VirtualAllocExStub
kernel32!VirtualAllocStub
kernel32!VirtualProtectExStub
kernel32!VirtualProtectStub
kernel32!WinExec
kernel32!WriteProcessMemoryStub
KERNELBASE!CreateFileMappingNumaW
KERNELBASE!CreateFileMappingW
KERNELBASE!CreateFileW
KERNELBASE!CreateRemoteThreadEx
KERNELBASE!CreateRemoteThreadEx
KERNELBASE!HeapCreate
KERNELBASE!LoadLibraryExA
KERNELBASE!LoadLibraryExW
KERNELBASE!MapViewOfFile
KERNELBASE!MapViewOfFileEx
KERNELBASE!VirtualAlloc
KERNELBASE!VirtualAllocEx
KERNELBASE!VirtualProtect
KERNELBASE!VirtualProtectEx
KERNELBASE!WriteProcessMemory
ntdll!LdrHotPatchRoutine
ntdll!LdrLoadDll
ntdll!NtCreateFile
ntdll!NtCreateUserProcess
ntdll!NtProtectVirtualMemory
ntdll!NtUnmapViewOfSection
ntdll!RtlAddVectoredExceptionHandler
ntdll!RtlCreateHeap
ntdll!ZwAllocateVirtualMemory
ntdll!ZwCreateProcess
ntdll!ZwCreateProcessEx
ntdll!ZwCreateSection
ntdll!ZwCreateThreadEx
ntdll!ZwMapViewOfSection
ntdll!ZwWriteVirtualMemory

Advertisement

Circumventing Malwarebytes Anti-Exploit 1.08 with a single write to memory

This blog post has been written based  on Malwarebytes Anti-Exploit 1.08.1.1189. The described limitation of Anti-Exploit has been fixed in v1.09

One of the ways in which computers get infected by adversaries is by using remote code execution exploits. Where there are attack vectors, there are defenses. A well-known piece of software that can tackle most common exploits is Malwarebytes Anti-Exploit. Just like EMET Anti-Exploit consists of a series of exploit mitigations that should stop most off-the-shelf exploits. However, this does not mean that with enough time spent into researching a security solution there is no way to bypass it. No single security product can have a definitive advantage in the cat-and-mouse game between security researchers and exploit developers at one side and the defenders at the other side.

If an attacker would want to bypass Anti-Exploit two paths can be followed:

  1. Bypass each individual mitigation.
  2. Find a single technique to disable most (or all) of MBAE’s mitigations.

In this blog post a technique will be discussed that takes the second route.

Disarming Anti-Exploit

Just like EMET Anti-Exploit stores its configuration in a section of read-only memory (from now on called MITIGATION_CONFIG). Based on this configuration it is determined whether a mitigation check should be performed or not.

config_blob

Figure 1: Read-only configuration (MITIGATION_CONFIG)

(Almost) all mitigations that Anti-Exploit implements to appear check in the same way whether a mitigation is enabled or not. Let’s for example take the function that implements the stack pivot mitigation in Anti-Exploit 1.08.1.1189. As shown in the figure below, Anti-Exploit assesses whether the mitigation is enabled before checking whether a given address lies within the stack boundaries as defined in the TIB.

stackpivot_renamed

Figure 2: Decompilation of function that implements the stack pivot mitigation in Anti-Exploit 1.08.1.1189

Anti-Exploit checks whether a mitigation is enabled by performing an AND operation on a global boolean variable stored at mbae.dll+0x53b00 and the mitigation setting of the stack pivot mitigation in the read-only config (MITIGATION_CONFIG+0x3c). When the stack pivot mitigation is enabled 0x35f is stored at MITIGATION_CONFIG+0x3c, when the mitigation disabled 0x35e is stored at MITIGATION_CONFIG+0x3c. Under normal conditions one of the two following AND operations will be performed:

Stack pivot mitigation enabled 0x1 & 0x35f = 1
Stack pivot mitigation disabled 0x1 & 0x35e = 0

If the mitigation is not enabled the function responsible for the stack pivot mitigation (mbae.dll+0x6980) will return zero as expected. There is one catch though with this AND operation, the global boolean variable (mbae.dll+0x53b00) is stored in writable memory.

global_switch_protection

Figure 3: Protection of global boolean variable stored at mbae.dll+0x53b00

When mbae.dll+0x53b00 is overwritten with zero, the AND operation will always yield zero. This type of check seems to be used in about all mitigations, therefore it is possible to disable Anti-Exploit by only overwriting one value in memory. The table below shows the effect of overwriting the boolean variable with zero on the stack pivot mitigation:

disarm + stack pivot enabled 0x0 & 0x35f = 0
disarm + stack pivot disabled 0x0 & 0x35e = 0

Writing a POC for this disarming flaw would only require the following two steps:

  1. Obtain the base address of mbae.dll
  2. Overwrite mbae.dll+0x53b00 with NULL (For MBAE 1.08.1.1189)

This disarming flaw discussed in this blog post has been fixed in MBAE version 1.09 by marking the memory that stores the boolean variable as read-only.