This blog post was a submission to Microsoft Bypass Bug Bounty program, but was not eligible to the scope of the program. Thus I am releasing a blog post on the technique which is based on leaking the stack address and overwriting the structured exception handler, thus turning a use-after-free into a structured exception handler overwrite.
To facilitate this bypass, I have again chosen to use vulnerability for Internet Explorer 11 which was patched in MS16-063 in June of 2016 and written about by the company Theori and I’ve previously used in CFG bypass posts here and here.
Leaking the Stack
Picking up where Theori left off I have attached the Clean_Poc.html proof of concept file, which exploits the vulnerability to gain a read/write primitive, but does nothing further. In the next proof of concept file called Leaking_Stack.html the stack limits for the current thread is leaked, this is done using the GetCurrentThreadStackLimits API in kernelbase.dll. The way it is executed is through virtual function table (vtable) overwrite of the TypedArray object, and more specificly by using the call listed below:
It takes two parameters and returns into them the stack base address and the maximum reserved address of the stack. The address of GetCurrentThreadStackLimits may be found through two steps, first leaking a pointer into kernelbase.dll and then locating the function in the DLL. The first part is done through first locating the function Segment::Initialize in jscript9 since it uses kernel32!VirtualAllocStub, which in turn uses kernelbase!VirtualAlloc. The way I find the function is by scanning through jscript9 from the address of the vtable and calculating a hash, this is done using the read primitive. The algorithm looks like shown below:
The hash is found by adding 5 DWORDs and going one byte forward each time until the correct hash is found. The very simple hashing function is actually quite collision free. The dereference call to kernel32!VirtualAlloc is then at offset 0x37 in the Segment::Initialize function as shown below:
This pointer can be read out to get:
Which then contains the dereference jump to kernelbase!VirtualAlloc at offset 0x6:
Now we have a pointer into kernelbase.dll, then we find the address of GetCurrentThreadStackLimits using the same method as with Segment::Initialize, this is seen below:
We may now create a fake vtable just as Theori did in their original exploit and overwrite the vtable entry at offset 0x188 with this function pointer, while remembering to increase the size parameter of the TypedArray, the code then becomes:
And breaking on GetCurrentThreadStackLimits while running it gives the following:
Which shows the stack lower and upper limits. To get instruction pointer control from here I locate the structured exception handler chain on the stack and overwrite an entry and then cause an exception. While doing this, it is important to remember that Windows 10 has SEHOP enabled. This will bypass both CFG and RFG since the SEH pointers are not protected by CFG and I am not returning anywhere. This is all implemented in the file Getting_Control.html.
To perform this I need to locate the SEH chain on the stack, after leaking the stack limits the structured exception handler chain looks like this:
The remaining problem of this leak is to find the address of ntdll!_except_handler4, but this is quite easy since a finding a pointer into ntdll.dll can be done from any function which is protected by CFG and contains an indirect call. The CFG validating contains a call to ntdll!LdrpValidateUserCallTarget, and since jscript9.dll is protected by CFG any function with an indirect call contains a pointer directly into ntdll.dll. One such function is at offset 0x10 in the vtable of the TypedArray object:
Using the read primitive, the pointer to ntdll.dll may then be found through the following function:
Going from a pointer into ntdll.dll to the address of _except_handler4 may be accomplished by using the read primitive to search for a signature or hash. _except_handler4 looks like this:
The first 0x10 bytes always stay the same and are pretty unique, so they may be used as a collision free hash when added together as before:
Where the function takes a pointer into ntdll.dll as argument. Once we have the function pointer, we can search the stack for it:
At this address, we have:
Which means that the DWORD before that may be read and contains:
Which reveals the final exception handler function pointer. Then it is time for the second stage of the leak, this time the exception handler we are looking from comes from jscript9.dll, so its function pointer must lay inside the code section of the PE, these addresses are found from the PE header of the DLL:
Now having this, the stack is searched from the top and down, looking through all content of the stack. The algorithm is shown below, and works as follows:
- If a DWORD is below 0x10000000 it is not inside jscript9.dll, so we move on to the next DWORD.
- If it is above 0x10000000 check if it is inside the code section of jscript9.dll
- If it is then if the DWORD 4 bytes lower on the stack is a pointer to the stack.
- If the above two are true, this might be the structured exception handler we are looking for, so we try and follow the stack pointer and check if we end up at the final exception handler.
- If one of the pointers no longer point to the stack or it takes more than 8 dereferences it is not the SEH chain.
In my tests the first time a pointer to jscript9.dll is found where the DWORD just before it is a stack pointer, it is the SEH chain, so the algorithm runs fast.
Having this algorithm in place means that it is possible to overwrite a structured exception handler record with precision and not disrupting the pointer the next exception handler, thus bypassing SEHOP.
Finally, it is a matter of triggering an exception to gain instruction pointer control:
Which when run gives:
This first shows the exception being caught by the debugger, and continuing from that the instruction pointer is overwritten with 0x42424242 as wanted. This illustrates how this technique bypasses CFG to gain execution control. It would also bypass the Return Flow Guard implementation which is now no longer supported in Creators Update. The code is on GitHub here