This article is about information leaks in form of memory disclosures created in Internet Explorer 10 32-bit on Windows 7 64-bit. They are used to bypass full ASLR/DEP to gain remote code execution. While the software containing the bug might not be that popular, it’s quite nice what can be done with the bug.
Hope you enjoy it.
The vulnerability is a simple buffer overflow occuring in the data section of the VideoPlayer.ocx module when using the plugin’s exposed SetText method (sub_10002930). By supplying a string to that method, the code at .text:1000298A and .text:10002991 copies our string to a variable in the data section at .data:100916A8 without bound checks:
From Data Buffer Overflow to Arbitrary Write
While there are no immediate control flow pointers like on the stack, maybe other pointers can be overwritten to achieve interesting things towards program manipulation and remote code execution. Exploitation on Windows XP may seem straightforward due to the absence of ASLR, but what if we want to target some Internet Explorer on Windows 7 or 8? At the end I decided to take that route.
To bypass ASLR, we need an information leak in order to disclose some interesting memory usable for further steps. After some experimentation with calling the SetText method and calling other plugin methods subsequently, some pointers catched my attention.
For example, the content at address .data:10092068 can be controlled via our buffer overflow. This pointer is used in sub_10058BAA which in turn is executed when the plugin’s exposed SetFontName method is dispatched.
When we call SetFontName with a string of size smaller or equal to 0x40 the following happens:
1. A function goes evil
We hit the function sub_10058DAB which retrieves the string’s length and calls sub_10058BAA with the length as 1st argument:
2. Use of controlled content
In function sub_10058BAA the address .data:10092068 of our controlled content is moved to ECX at .text:10058BC7 and function sub_1000F775 is called. As the address is passed via ECX to the function, it most likely holds an object’s this pointer:
In sub_1000F775 the object pointer is moved into ESI (.text:1000F784). The object’s 4th DWORD [ESI+0xC] (which we control) is compared to 0, and when it is not 0, program flow continues at .text:1000F7CE. Afterwards, the 4th DWORD is moved to EAX and the function returns. So we now control the return value passed in EAX:
We return into sub_10058BAA from sub_10058DAB and we control EAX. Thus, we can already control WHERE we want to write, but not really WHAT we want to write. Our controlled value is used as pointer and the values 0x40, 0x1, 0x0 and the string length are written. Also, the controlled value is increased by 0xC and then written to memory pointed to by EBX:
Back in sub_10058DAB, the string length in EDI is pushed as 3rd argument, the string pointer in EBX as 2nd, and our controlled value in [ESI] as 1st argument before _memcpy is called:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
We can attach WinDbg to our running Internet Explorer and view the modified memory starting from 0x10101020 which was previously filled with 0x22222222:
Prepare leaks: One array to access ‘em all
(Typed) array heap spray
Instead of spraying the heap with strings we use arrays. We create blocks of memory with a size of 0x10000 bytes which become aligned to 0xXXXX0000. The first 0xf000 bytes are filled with a generic array and typed array headers (objects) follow which fill the remaining page. As each typed array header has a size of 0x30 bytes, they become aligned after the generic array data to 0xXXXXF000, 0xXXXXF030, 0xXXXXF060 and so on:
There’s an awesome plugin tool for WinDbg out there called mona. Recently it got the ability to dump objects in a detailed way. We can see the different elements of the typed array header. Amongst other fields, each typed array header has a vtable pointer, a length field, a pointer to its arraybuffer object and to its arraybuffer. We enter !py mona do -a 0x1111f000 -s 0x30 to dump the typed array header at 0x1111F000:
Modify typed array headers
We now trigger the vulnerability such that we overwrite a pointer to an arraybuffer with a desired value. We choose a value of 0x1111F030 and overwrite the pointer residing at 0x1111F010. Thus, we let it point to the subsequent typed array header at 0x1111F030. Additionally, we overwrite the length field of the typed array header with one of our “side effect” values (0x00000040).
Finding the modified typed array is easy: We iterate over all typed arrays and check if their first elements are unequal to zero. It is successfull when we hit the modified array, as its first element points to a typed array vtable. Then we use the modifed array to change the subsequent typed array header: We set the length at 0x1111F048 to 0x7fffffff and the arraybuffer pointer to the start of process memory, namely 0x0. And we can do this with array element writes ( arr[k][i] = 0x7fffffff and arr[k][i] = 0x0 ).
After the vulnerability and the subsequent manipulation has taken place, we can view the typed array headers in WinDbg:
Access arbitrary memory
As we have a memory readwrite interface we can use it via array accesses to read and write arbitrary memory.
We know that there’s a vtable at 0x1111F060. So let’s read at that address by supplying the value 0x1111F060 to the prompt box:
A message box should pop up showing the resulting content interpreted as DWORD:
This is consistent with the WinDbg output we saw before.
Set and leak objects
As the heap layout is predictable we can set any object as element into a generic array and leak its address. For example, we can put the ActiveX object as first element into the generic array residing below the page with the manipulated typed array headers. As the array is aligned to 0x11120000, we know that the object’s pointer is located at 0x11120020 (20 bytes are occupied by allocation and array metadata). We simply supply 0x11120020/4 as index to our memory interface array and get the object’s address. You can test it by uncommenting line #102 in the leaking script and supplying 0x11120020 to the prompt box. To verify it with WinDbg, enter dd 0x11120020 .
Dig deep into objects
When we leak content at a specified address and know that the content is a pointer itself, we can use the retrieved content as an index into our memory interface array again. This way, we can subsequently dereference object fields in order to read and rewrite them.
Finally it’s time to pop some calculators. So there’s of course a PoC which achieves code execution and runs calc.exe.
Just a short description what is happening there:
- We first subsequently leak memory from the ActiveX plugin object and obtain the base of VideoPlayer.ocx without touching any import/export tables (line #162 to #179).
- The location of VirtualAlloc and _memcpy is obtained (line #181/183)
- We then put shellcode into an arraybuffer and leak its address dynamically (#210 - #215).
- In line #231 to #249 we build a ROP chain and fill necessary fields of it. We leak it’s buffer address as well. At runtime the ROP chain will allocate executable memory, copy the shellcode to it and will jump to it. The chain solely uses gadgets from the VideoPlayer.ocx module.
- Afterwards we overwrite a field of the ActiveX object with the address of the ROP chain to be able to get control of EIP (#252/253)
E voila! We’ve bypassed full ASLR and DEP and got remote code execution with a buffer overflow in the data section. Fun!