Exploiting CVE-2015-0311, Part II: Bypassing Control Flow Guard on Windows 8.1 Update 3

At the beginning of March we published a blog post analyzing CVE-2015-0311, a Use-After-Free vulnerability in Adobe Flash Player, and we outlined how to exploit it on Windows 7 SP1 machines. As we mentioned at the end of that article, the exploitation process explained there doesn't apply to more recent versions of Windows like Windows 8.1 with Update 3, because of a new exploit mitigation technology called Control Flow Guard (CFG).

CFG – which was introduced by Microsoft in Windows 8.1 Update 3 in November 2014 – adds a check before every indirect call in the code in order to verify that the destination address of that call is one of the locations identified as “safe” at compile time. If that check fails at runtime, the program detects an attempt to subvert the normal execution flow and exits immediately. Prior to the existence of Control Flow Guard, leveraging a vulnerability like a Use-After-Free into arbitrary memory read & write primitives – as we did with CVE-2015-0311 – used to mean circumvention of ASLR and DEP and ultimately reliable code execution.

The interesting thing about Control Flow guard is that now, even having those powerful read & write primitives, being able to gain code execution may require a noteworthy additional effort. It turns out that the Flash version for Internet Explorer 11 that is integrated into Windows 8.1 Update 3 is compiled with Control Flow Guard enabled, so the well-known technique of overwriting the vtable of an object with a pointer to attacker-controlled data and then calling a virtual function on that object will not work.

Let's find look at how we can bypass Control Flow Guard when exploiting Adobe Flash Player on Internet Explorer 11 running on Windows 8.1 Update 3. This post assumes that you have already read part one, so we are going straight to the point where we are ready to hijack the execution flow of the browser process.

A brief overview of Control Flow Guard

On non-CFG versions of Flash, this is the code that dereferences the overwritten vtable when calling the toString() method on a Vector object, as shown in the previous blog post:

Image
code that dereferences the overwritten vtable when calling the toString() method on a Vector object
 

Text

And this is the same part of the code in Flash 16.0.0.287 for Windows 8.1 Update 3 compiled with Control Flow Guard:

Image
control flow guard 81 update

Text

The ___guard_check_icall_fptr function pointer points to ntdll!LdrpValidateUserCallTarget, which is in charge of checking if the destination address for the indirect call is legitimate. When reaching this CFG check with a fake vtable, an attempt to subvert the normal execution flow of the program will be detected and execution will ultimately go to the ntdll!RtlpHandleInvalidUserCallTarget function, which will terminate the process immediately by issuing an INT 29h:

Image
int29a-rtlfailfast

 

Text

If you want to learn more about the internals of Control Flow Guard I recommend checking out the slides from the talk "Windows 10 Control Flow Guard Internals" [pdf] presented last year by researcher MJ011 at the Power Of Community security conference.

Approaches

You should note that Control Flow Guard adds a massive number of calls to the guard function in order to protect every indirect call that could be identified at compile time. Flash Player 16.0.0.287 for Windows 8.1 Update 3 includes 29238 calls to the guard function:

Image
xrefs_to_guard_check_icall_fptr

Text

A few different approaches to modify the execution flow when CFG is present:

  • Overwrite a return address.
  • Take advantage of a non-CFG module loaded in the same process.
  • Find an indirect call that was not guarded by CFG for some reason.

The first approach requires you to obtain a stack address, which I wasn't able to do having a pointer to an object in the heap as the starting point, despite having arbitrary read and write primitives after corrupting the length of the Vector. The second approach could introduce new dependencies on an exploit beyond the affected software, so it may not be ideal. I did find some indirect calls that are not protected by CFG, like the one below:

Image
non-protected-indirect-call

Text

That function pointer is located in the .data section which has read/write permissions, so overwriting it is definitely possible. However, the main problem with this approach is that you must be able to influence the execution flow of the program (while still not having gained code execution) so the overwritten function pointer is actually called. What if the function pointer is called only in uncommon situations like rare corner cases? What if the function pointer is called only early on process initialization? An additional obstacle in this approach is that if you have no CPU registers pointing nearby your data at the moment the overwritten function pointer is called, pivoting the stack in order to start a ROP chain can become tricky. So far CFG is doing a great job protecting more than 29,000 indirect calls in this Adobe Flash Player binary. So the question is: can we find any indirect calls in Flash Player that are not protected by CFG, and then influence the execution flow of the program in a straightforward way in order to make it call an overwritten function pointer? Well, as stated before, Control Flow Guard protects indirect calls that could be identified at compile time. So the next question arises: are there any indirect calls in Flash Player that are not generated at compile time? And the answer is: yes! Flash Player includes a Just-In-Time (JIT) compiler, which translates ActionScript Virtual Machine bytecode to native code in order to improve its execution speed. The code generated by the JIT compiler includes indirect calls, and since this code is generated at runtime that means that indirect calls in it are not protected by Control Flow Guard.

Finding an unguarded indirect call

So let's go back in the exploitation process to the point where we can modify the metadata of the Vector object. We can set the length of the Vector to 0xffffffff by doing si32(0xffffffff, 0x24). This new length will allow us to read/write from/to any memory address within the address space of the process. Also remember that we have a ByteArray object with our ROP chain stored as the first element of the Vector. The dword stored at this ByteArray object + 8 is a pointer to a VTable object (yes, that's the actual name of the class, as defined in core/VTable.h):

Image
bytearray_8_2

Text

Now, if we examine that VTable_object we can see that it contains a lot of pointers:

Image
VTable_object

Text

If we follow any of those pointers (in the image below I followed the pointer stored at VTable_object + 0xD4) we can see that they all look pretty much the same. These ones are MethodEnv objects (defined in core/MethodEnv.h):

Image
MethodEnv_object

Text

The first dword of this MethodEnv object is a pointer to its vtable, while the second dword is a function pointer (0x601C0A70 in this case). It turns out that the pointer to a MethodEnv object stored at VTable_object + 0xD4 is dereferenced in order to make an indirect call to the function pointer stored at MethodEnv_object + 4 (0x601C0A70), and this indirect call comes from code generated by the Flash JIT compiler, so it isn't protected by CFG! This unguarded indirect call can be reliably triggered by calling the toString() method on the ByteArray object. We can put a hardware breakpoint on read at VTable_object + 0xD4, and when our ActionScript code calls this.the_vector[0].toString(), our hardware breakpoint will be hit when the following JIT-generated code reads the pointer stored at VTable_object + 0xD4. Note how a function pointer is grabbed from ECX+4 and then it's blindly called without first calling the CFG guard function:

Image
jit-indirect-call_2

Text

That JIT-generated code, which is located on the heap, is reached through the following execution path: method BaseExecMgr::invokeGeneric (defined in core/exec.cpp) calls BaseExecMgr::endCoerce:

// Invoker for native or jit code used before we have jit-compiled,
// or after JIT compilation of the invoker has failed.
Atom BaseExecMgr::invokeGeneric(MethodEnv *env, int32_t argc, Atom* atomv)
{
    MethodSignaturep ms = env->get_ms();
    const size_t extra_sz = startCoerce(env, argc, ms);
    MMgc::GC::AllocaAutoPtr _ap;
    uint32_t *ap = (uint32_t *)avmStackAlloc(env->core(), _ap, extra_sz);
    unboxCoerceArgs(env, argc, atomv, ap, ms);
    return endCoerce(env, argc, ap, ms);
}

And BaseExecMgr::endCoerce calls the JIT-generated function shown before that contains the unguarded indirect call:

Atom BaseExecMgr::endCoerce(MethodEnv* env, int32_t argc, uint32_t *ap, MethodSignaturep ms)
{
    [...]
    switch(bt){
    [...]
    default:
    {
        STACKADJUST(); // align stack for 32-bit Windows and MSVC compiler
        const Atom i = (*env->method->_implGPR)(env, argc, ap);
        [...]

This is BaseExecMgr::endCoerce at the binary level:

Image
endCoerce-baseexecmgr

Text

Exploitation

Now that we know how to easily trigger an indirect call that isn't guarded by CFG, we need to put a fake MethodEnv object at VTable_object + 0xD4. They say that a picture is worth a thousand words, so let's see this in a graphical way. This is the original state:

Image
expected_state_2

Text

And this is the state we need to achieve in order to bypass CFG and finally gain code execution:

Image
modified_state_2

Text

In the ActionScript exploit code we'll read the pointer stored at ByteArray object + 8 by using Nicolas Joly's read primitive based on Number objects, as explained in the previous blog post:

var vtable_object:uint = leak_8_bytes(bytearray_object_pointer + 8);

Then we calculate the target address we need to overwrite by adding 0xD4 to that pointer, and right after that we calculate the index we need to use within the Vector object (whose length we have overwritten with 0xffffffff) in order to write to the target address:

var target_address:uint = vtable_object + 0xd4;
/* 0x28: offset of the first element within the Vector object */
var idx: uint = (target_address - (address_of_vector + 0x28)) / 4;
this.the_vector[idx] = address_of_rop_chain >> 3;

We are overwriting the pointer stored at VTable_object + 0xD4 with the address of our ROP chain (stored in the address_of_rop_chain variable in the ActionScript snippet above). Note that I'm storing address_of_rop_chain shifted 3 times to the right; that is because address_of_rop_chain has type uint, and the ActionScript Virtual Machine internally stores uint values shifted 3 times to the left and OR'ed with 6 (6 is the 'Integer' tag as shown in the previous blog post). Finally, we just need to invoke the toString() method on the ByteArray object , which is stored at this.the_vector[0]. This will trigger the unguarded indirect call from JIT-generated code that will start our ROP chain.

new Number(this.the_vector[0].toString());

Note that at the moment the unguarded CALL EAX indirect call invokes our controlled function pointer, the ECX register points to our ROP chain, so pivoting the stack and starting the ROP chain is a trivial task.

Conclusion

Control Flow Guard is a promising mitigation technology that will increase the difficulty and the costs of writing exploits. As with every known exploitation mitigation, there are ways to bypass it if certain conditions are met. The approach presented here, which consists of taking advantage of unguarded indirect calls present in JIT-generated code, could be possibly applied not only to Adobe Flash Player, but potentially to any software that makes use of a Just-In-Time compiler, since the code generated by it at runtime doesn't benefit from CFG, unless developers make an special effort to harden the code emitted by the JIT compiler. Note that Flash Player's JIT compiler has been abused in the past more than once in order to bypass mitigation mechanisms; see the "Pointer inference and JIT spraying" presentation by Dion Blazakis (2010) and the "Flash JIT - Spraying info leak gadgets" work by Fermín Serna (2013).