The Chakra Exploit and the Limitations of Modern Mitigation Techniques
Last November, Microsoft released a security update for Microsoft Edge which included patches for vulnerabilities CVE-2016-7200 and CVE-2016-7201, which were discovered by Google Project Zero. Earlier this year, a proof-of-concept (POC) exploit was published by Brian Pak, demonstrating that the two vulnerabilities can be used together to gain exploitation. Shortly afterwards, they were included in several popular exploit kits. The exploit kit implementations appear to be largely a cut/paste of the POC.
Microsoft has a steady track record of implementing exploit mitigation technologies, ranging from EMET, at one time the industry standard in exploit prevention tools, to the built-in mitigations incorporated into Windows 10 and Edge. An exploit – even an unweaponized one – that bypasses these technologies is interesting and can help us understand the current state of exploit prevention.
At Endgame, we constantly test our exploit detection and prevention capabilities against the latest threats to ensure we continue to provide the most robust defenses available. We became extremely interested in this POC as it highlighted several areas where further research was required, and has already resulted in stronger protections for our customers.
In this post, I won't be looking at the vulnerabilities in depth, but instead will focus on the weak spots in current exploit mitigation techniques that are highlighted by the minimalist POC. I will walk through a typical exploit attack chain as it applies to the Chakra POC, and illustrate how we must continue to raise the costs for in-the-wild exploits through more innovative approaches to prevention.
The Stages of Exploitation
Most exploits require a preparation step that puts something controlled by the attacker (e.g., ROP, shellcode) in memory in a known location prior to gaining execution. This step can take many forms and is often referred to as heap grooming, or one of its subsets, heap spraying. While most of these techniques are difficult to detect with high confidence, it often offers the first chance for defenders to detect that something malicious is happening.
Heap spray mitigations come in two main flavors – detectors and disruptors. Detectors, like NOZZLE, monitor allocations and inspect memory for indicators of malicious behavior. Disruptors, like BuBBle and DieHarder, attempt to reduce the reliability of heap sprays by increasing allocation randomization, blocking NOP-sleds, or protecting addresses commonly used by exploits (0x0c0c0c0c, etc.).
An entire chain of blog posts can be dedicated to the cat-and-mouse game of preparing memory for exploitation and detecting such actions. Let's leave that for another day and skip to the simplest method to bypass detection at this stage. The solution enabled by CVE-2016-7200, and chosen by any attacker if given the choice – avoids memory preparation altogether.
Technically, CVE-2016-7200 does prepare memory in the broadest sense, but it does so with the precision of a scalpel, making it very unlikely that any current mitigation technique will notice.
The implementation of CVE-2016-7200 can be seen below:
PutDataAndGetAddr() is used once for the memory leak (discussed later):
And again to place shellcode:
As you can see, there are few allocations, no NOP-sled is required, and the data is mostly benign (the shellcode data could be detected as code, but would be very difficult to label as malicious).
No mitigations to speak of in this step, and there isn't much to write about because actions taken here are dependent on the vulnerabilities involved and the path to execution chosen by the attacker.
The Chakra POC uses CVE-2016-7201 at this point to create read/write capabilities that will be used later.
With the introduction of address space layout randomization (ASLR), and now near universal enforcement, attackers need to take one extra step in their path to exploitation. They need to orient themselves by finding the address of their target module in memory.
Since the Chakra POC already has the address of an object and read/write primitives, acquiring the base address of chakra.dll is simply a matter of grabbing the vtable from our object, finding the address of one of its functions, and subtracting the appropriate offset based on the dll version.
In order to be the most effective across a wide range of operating systems and target versions, many exploits dynamically determine the locations of functions necessary for exploitation (e.g, VirtualProtect, VirtualAlloc, etc.). This lookup is most often accomplished by searching the export address table of a target dll. A good example of this is the PE.as module often used in Flash exploits:
Mitigations, such as Microsoft EMET's EAF/EAF+, exist to detect this behavior by blocking access to DLL headers from unauthorized locations.
The Chakra POC avoids this check by using hard-coded addresses where a lookup would traditionally be required.
There is an inherent weakness in this approach. The use of hard-coded offsets in an exploit is useful in defeating some existing exploit mitigations, but by and large, this practice is detrimental to widespread usage and effectiveness. Hard-coded addresses increase exploit development time and require constant updating to survive in a dynamic target environment.
Next up, an attacker must interrupt the intended flow of execution and begin executing his/her own agenda. Traditionally, this has been done by pivoting the stack pointer to attacker-controlled memory. A common mitigation at this point is for security products to occasionally verify that the stack pointer actually points to the current thread's stack.
The Chakra POC passes these checks by placing its ROP directly on the stack.
Endgame employs HA-CFI, which helps ensure the integrity of code execution, and has been very successful in preventing most exploits. HA-CFI can detect unusual control flow and stops most exploits before the stack pointer has been modified. Often, the stack pivot is the first gadget in the ROP (discussed next) and is usually the first abnormality in the flow of execution.
Microsoft utilizes Control Flow Guard(CFG) with similar goals. CFG adds an additional check to indirect calls to ensure they are being directed to valid targets.
The Chakra POC, thanks in large part to hard-coded addresses, hijacks the return address for ScriptEngine::ExecutePendingScripts and gains control of the stack when the exploit script completes. No indirect calls are required, and unusual code flow is avoided, thus bypassing HA-CFI and CFG.
Return Oriented Programming (ROP)
ROP is a standard method of transitioning to full control of execution. This step is most often required when the attacker's shellcode resides in non-executable memory.
A lot of research has been conducted in detecting ROP, which has resulted in several techniques being deployed in security products.
1. Sanity checks on critical functions. The thought here is that if an attacker is forced to use ROP, he/she likely will need to use one of a short list of functions, such as CreateProcess or VirtualProtect to move on to payload execution. Whenever these functions are called, a series of checks can be performed to detect badness.
a. stack pointer integrity
c. return address is call-preceded
d. VirtualProtect does not attempt to make stack executable
2. Code simulation. Mitigations employed by Microsoft's EMET and ROPGuard simulate execution beyond the current critical function for a short period to detect ROP-like behavior.
3. Hardware. kBouncer makes use of hardware performance monitors to detect ROP-like behavior.
Chakra POC ROP
The Chakra POC does use a ROP (shown above), but it has several characteristics that make it very difficult for any of these techniques to detect. First, the ROP contains only two gadgets plus a return to shellcode. This eliminates HW-assisted gadget-chaining detection, like kBouncer, which requires longer ROP chains to reach high enough confidence in malicious activity. Second, the VirtualProtect call is made through a legitimate function that wraps VirtualProtect. This makes the stack appear normal when it reaches any checks at VirtualProtect.
The combination of the second ROP gadget and APIHook_VirtualProtect provide plenty of legitimate code for simulation techniques to inspect and feel good about as they inspect code following VirtualProtect.
The Chakra POC is a good case study in exploit mitigation evasion techniques, and I really only scratched the surface. The techniques used in this POC are not new, and in fact, most have been around for quite some time. They were used for two reasons: 1) The vulnerabilities themselves allowed their use; 2) The mitigations built into Windows 10 and Edge necessitated their use. We know that vulnerabilities will continue to exist, which leads the defensive focus to exploit detection and prevention. Software vendors like Microsoft, Google, Adobe, and many others have invested significant resources in the detection/prevention fight. Hardware vendors like Intel are joining the struggle as well. And security companies like Endgame offer even more capabilities to stop adversaries. Every mitigation potentially adds some cost to an attacker. Our job is to make that cost as high as possible.
The Chakra POC and the ongoing discovery of in-the-wild exploits highlights that the cost is not yet high enough, and the attackers are still getting through. It demonstrates that Windows 10 and Edge do not provide the safe haven so many have claimed. At Endgame, we continue to develop innovative exploit detection/prevention capabilities to raise the cost by analyzing the latest exploits and integrating prevention techniques to stop even the most creative attackers at the earliest stage of the attack chain.