Microsoft’s June Patch Kills Potential CFG Bypass

By on Jun 16, 2016

After applying Microsoft’s June patch, we noticed some interesting changes that prevent a security bypass of Windows’ Control Flow Guard (CFG). The changes are in the Shader JIT compiler of the Windows Advanced Rasterization Platform (WARP) module (d3d10warp.dll). The Shader JIT compiler could formerly be used to create a CFG bypass. CFG is known to have problems with dynamic code, such as that created by JavaScript JIT and ActionScript JIT compilers. With this update, CFG becomes more robust, and especially more resistant to bypass attacks involving JIT techniques.

WARP is a software-based rasterizer that contains a high-performance JIT code generator which can convert High-Level Shader Language byte code to optimized native machine code (SSE, x86/x64). Moreover, WARP is accessible in the context of the browser by defining Shaders with WebGL (JavaScript-based API), so WARP can be easily employed in browser-based exploit development.

The changes were made in a function that is related to WARP Shader JIT code page protection. This particular fix uses new operating system features— VirtualAlloc with the “PAGE_TARGETS_INVALID” flag and the SetProcessValidCallTargets API—to make sure only the starting address of a Shader JIT function is the valid call target. (We will demonstrate this later through a live debugging session.)

20160616 CFG 1

20160616 CFG 2

A look at live debugging before the June Patch (d3d10warp.dll v10.0.10586.0):

20160616 CFG 3

Live debugging after the June Patch (d3d10warp.dll v10.0.10586.420):

20160616 CFG 4

From the preceding screens, we can clearly see that after applying the June patch most bits on the CFG map corresponding to the Shader JIT code block are cleared except for the first (corresponding to the JIT function entry point), which indicates the function entry is the only valid call target and jumping to any other place will be caught by a CFG check.

Looking even deeper into the WARP Shader JIT mechanism, we found some other interesting facts:

  • Unlike with JavaScript/Flash ActionScript JIT, the data and code for the WARP Shader JIT are stored separately (in their dedicated blocks, respectively), which makes it very difficult to control the generation of arbitrary instructions. However, under certain circumstances a specially crafted Shader can still generate a desired ROP gadget that is good enough to bypass CFG. (In most cases the sequence “\x94\xc3” is no longer usable because a CFG check will alter the value in the EAX register.)
  • Some cautious readers may have noticed that all JIT code blocks appear in some continuous and repeated pattern. That is true; WARP Shader JIT does not take randomization into consideration when allocating code pages and generating instructions. As a result, one can generate a desired instruction at some predictable location (as shown in the preceding screen), which is similar to a heap-spray However, to achieve stable WARP Shader JIT spraying, some obstacles need to be overcome. As of now, this issue has not been fixed (and perhaps never will be). I have verified that this flaw can be leveraged in combination with a certain vulnerability to leak the system module’s address.

I plan to write a paper to offer more detail on these aspects. Stay tuned!

I would like to thank my colleague Haifei Li for his help in creating this post.

About the Author

Bing Sun

Bing Sun is a senior security researcher, and now he is leading the IPS security research team of McAfee. He has extensive experiences in operating system kernel and information security technique R&D, with especially deep diving in advanced vulnerability exploitation and detection, Rootkits detection, firmware security and virtualization technology. Moreover, Bing is also a regular ...

Read more posts from Bing Sun

  1. how to trigger the Microsoft Edge load d3d10warp.dll module? I know in the vmware workstation and remote desktop Microsoft Edge load d3d10warp.dll. There are other circumstances ?

    • Basically WARP will come into play when the underlying display hardware doesn’t fully support Direct3D 10, therefore it’s advisable to verify the issue in a virtual machine where the display support is low (such as VMware Fusion, Hyper-v). When running the test PoC inside VMware, if d3dwarp10.dll module is not loaded, please try disabling “Accelerate 3D Graphics” from VM display setting to force it use software based rasterization.
      Interestingly, I tested the issue with the latest MS Hyper-v (both Hyper-v shipped with Windows 8.1 and Windows Hyper-v server 2012), and it turns out that the issue is much easier to reproduce than with VMware. It seems to me that the display device that Hyper-v emulates doesn't fully support D3D 10, such that WARP will be chosen to do the rasterization by default.

Similar Blogs

Subscribe to McAfee Securing Tomorrow Blogs