In this article we are going to talk about the following WDEG features: Code Integrity Guard, Arbitrary Code Guard and "Do not allow child processes" policy.
Generall, there are two results that can be achieved with exploitation: arbitrary code execution and data-only corruption.
The arbitrary code execution can happen through a number of venues:
- Code Reuse (with control flow integrity violation like ROP/COP/JOP)
- Code Generation or Modification in Memory (W^X violation, JIT engines)
- Code Loading from local or remote storage (DLL planting, path redirection, and symlink attacks)
To mitigate against these attacks, we can either completely block them (CIG) or analyze code for certain malicious attributes (AIG).
Code Integrity Guard
Code Integrity Guard (CIG) mitigates the Code Loading issue by requiring code to be digitally signed. CIG was part of Device Guard, and Microsoft used this mechanism to prevent tampering of OS drivers loaded in Windows 10. Following its launch, Microsoft allowed software developers to deploy CIG with their own applications.
To enable signature testing, the image must be compiled with
/INTEGRITYCHECK option. When the option is set, the PE header of the DLL file or executable will contain a flag that tells the memory manager to check for a digital signature in order to load the image in Windows. The flag is called
IMAGE_DLLCHARACTERISTICS_FORCE_INTEGRITY and will only allow the to load images/DLLs signed by Microsoft, Microsoft Store, or WHQL signed DLLs.
If DLL/EXE is not signed, the kernel will fail attempts to load it by a process during process creation time rather than during process initialization to eliminate a process launch time gap where local injection of improperly signed DLLs. This was achieved by taking advantage of the
UpdateProcThreadAttribute API to specify the code signing policy for the process being launched.
BOOL WINAPI UpdateProcThreadAttribute( _Inout_ LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, _In_ DWORD dwFlags, _In_ DWORD_PTR Attribute, _In_ PVOID lpValue, _In_ SIZE_T cbSize, _Out_opt_ PVOID lpPreviousValue, _In_opt_ PSIZE_T lpReturnSize );
The binary signature policy requires EXEs/DLLs to be properly signed. The following mitigation options are available for the binary signature policy:
As this policy is applied per-process, it is also important to prevent an attacker from spawning a new process with a weaker or non-existent CIG policy by setting the "Do not allow child processes" policy in WDEG. This policy is currently enforced as a property of the token for a content process which ensures both direct (e.g. calling WinExec) and indirect (e.g. out-of-process COM server) process launches are blocked.
Usually CIG is bypassed with reflective memory based injection - where a malicious DLLs injects and mapsitself inside of the target process without using standard API like
LoadLibrary to do that.
CIGslip is a new method (March, 2018) which can be exploited by attackers to bypass Microsoft’s Code Integrity Guard (CIG) and load malicious libraries into protected processes such as Microsoft Edge without using reflective injection techniques.
CIGSlip depends on our ability to execute a non-CIG image on the target system, which is usually possible. Then, we hook into
ZwCreateSection method within the target process so that it will not go down to Kernel and will return the duplicated section handle.
NTSYSAPI NTSTATUS ZwCreateSection( PHANDLE SectionHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PLARGE_INTEGER MaximumSize, ULONG SectionPageProtection, ULONG AllocationAttributes, HANDLE FileHandle );
Since section handles are global objects managed by Kernel, handles could be duplicated between processes. Therefore, a section that correlated to a non-signed DLL could be created within the context of the malicious process and then duplicated into the target process.
Following that, the targeted process will map the dll code page into its memory as part of a regular dll loading process, assuming that the section went through the appropriate verification process. Which it did, since createsection now returns an already existing verified section handle!
Arbitrary Code Guard aka Memory Protection Check
ACG then complements this by ensuring that signed code pages are immutable and that new unsigned code pages cannot be created.
While CIG provides strong guarantees that only properly signed DLLs can be loaded from disk, it does not provide any guarantees about the state of image code pages after they are mapped into memory or dynamically generated code pages. This means an attacker can load malicious code by creating new code pages or modifying existing ones even when CIG is enabled. In practice, most modern web browser exploits eventually rely on invoking APIs like
VirtualProtect to do just this. Once an attacker has created new code pages, they then copy their native code payload into memory and execute it.
With ACG enabled, the Windows kernel prevents a content process from creating and modifying code pages in memory by enforcing the following policy:
Code pages are immutable.
Existing code pages cannot be made writable and therefore always have their intended content. This is enforced with additional checks in the memory manager that prevent code pages from becoming writable or otherwise be modified by the process itself. For example, it is no longer possible to use
VirtualProtect to make an image code page become
New, unsigned code pages cannot be created. For example, it is no longer possible to use
VirtualAlloc to create a new
PAGE_EXECUTE_READWRITE code page.
Supporting Just-in-Time (JIT) Compilers.
In order to be able to write JITted (executable) data into the Content Process, JIT Process does the following:
- It creates a shared memory object using
- It maps it into Content Process as
PAGE_EXECUTE_READand in the JIT proces as
MapViewOfFile2. At this point the memory is reserved, but not yet committed.
- When individual pages need to be written to they are first allocated from the region in step 2 using
VirtualAllocEx. This also marks the memory as committed
Limitations and bypasses
One of the limitations of CIG and ACG is that they don’t prevent an attacker from leveraging valid signed code pages in an unintended way. For Example, this means attackers could still use well-known techniques like return-oriented programming (ROP) to construct a full payload that doesn’t rely on loading malicious code into memory.
For example, in 2017, Ivan Fratric, a security engineer with Google's Project Zero team, has discovered a way to bypass ACG and allow an attacker to load unsigned code in memory, allowing attackers a way into Windows boxes via malicious websites loaded via Edge.
The issue lies in a predictability of the address that will be allocated with
VirtualAllocEx and the fact that the memory is originally allocated with
MEM_RESERVE and not
If a content process is compromised and the content process can predict on which address JIT process is going to call
VirtualAllocEx next (note: it is fairly predictable), content process can:
- Unmap the shared memory mapped above above using
BOOL WINAPI UnmapViewOfFile( _In_ LPCVOID lpBaseAddress );
- Allocate a writable memory region on the same address JIT server is going to write wit VirtualAlloc with
MEM_RESERVE | MEM_COMMITflags and write an soon-to-be-executable payload there.
LPVOID WINAPI VirtualAlloc( _In_opt_ LPVOID lpAddress, _In_ SIZE_T dwSize, _In_ DWORD flAllocationType, _In_ DWORD flProtect );
- When JIT process calls
VirtualAllocEx, even though the memory is already allocated, the call is going to succeed and the memory protection is going to be set to
This gives Content Process a block of memory that could be first written in and then executed. Which means the ACG is bypassed.
- Microsoft Edge: ACG bypass using UnmapViewOfFile
- Morphisec Uncovers New Attack Vector Named CIGslip That Bypasses Microsoft Code Integrity Guard (CIG)
- Weston Miller's talk at CSW2017
Part 1 - Windows Defender Exploit Guard for Pentesters - Validate Exception Chains (SEHOP)
Part 2 - Windows Defender Exploit Guard for Pentesters - ASLR
Part 3 - Windows Defender Exploit Guard for Pentesters - DEP
Part 4 - Windows Defender Exploit Guard for Pentesters - CFG
Part 5 - Windows Defender Exploit Guard for Pentesters - CIG & ACG