Exploits & Vulnerabilities
Analyzing CVE-2016-7255 Exploit In The Wild
We examined this vulnerability to understand the techniques used by the attacker. By changing one bit, the attacker can elevate the privileges of a thread, giving administrator access to a process that would not have it under normal circumstances.
Figure 1. Summary of the vulnerability (Click to enlarge)This vulnerability gives an attacker the capability to set one bit to an arbitrary kernel address. We obtained in-the-wild exploit code of this vulnerability, and here is how the exploit worked:
- Attacker suspends all threads in the current process (the browser, iexplore.exe), except the thread which is running the exploit shellcode.
- Attacker creates a new thread (let's call this thread A). Thread A does the following:
- Sleep 400 milliseconds
- Make multiple keybd_event calls to simulate the user pressing Ctrl+Tab
- Detect the current running Windows version. The exploit code works in situations where the underlying Windows version is 64-bit, but the Internet Explorer process is 32-bit. (This is the typical default configuration of IE on 64-bit systems.)
The shell code works on multiple Windows versions because the attacker included settings specific to each Windows version. These include:
- syscall id for the function NtUserSetWindowLongPtr. On 64-bit versions of Windows 7, this is 0x133a.
- offset of data structure tagTHREADINFO's field ppi. On 64-bit versions of Windows 7, this is 0x158.
- offset of data structure EProcess's field ActiveProcessLinks. On 64-bit versions of Windows 7, this is 0x188.
- offset of data structure EProcess's field Token. This is 0x208.
- Get the offset of the tagWnd field cbwndExtra. This represents the size of the window object's extra memory. This extra memory is adjacent to the tagWnd object. The SetWindowLong API can be used to set the content of the extra memory.
Figure 2. Offset of window object cbwndExtraThe attacker used a clever method to get both the offset of the tagWnd field cbwndExtra within the tagWnd data structure, and offset extra memory from the start of the tagWnd object:
- The attacker creates a window (called Window A), then calls SetWindowLong(handle of the window, 0, 0x31323334) in order to write the magic number 0x31323334 at offset 0 in the extra memory.
- The attacker calls the HMValidateHandle function in user32.dll. The function is not exported to programmer to call. To access it, the attacker traverses an exported API - the user32!IsMenu function's body (because user32!IsMenu calls USER32!HMValidateHandle). This allows it to get the address of USER32!HMValidateHandle. This function has a dangerous characteristic: it leaks kernel information. It maps tagWnd object and extra memory to the user mode memory space. The function’s return value is the user mode mapping address. Using this function, attacker can access both tagWnd's object and extra memory with user mode code,
- Attacker traverses the memory from the tagWnd object user mode mapping address to the address which contains the magic number placed in step a. Because this is located at the beginning of the extra memory, the attacker can get the offset of the extra memory from the tagWnd object's beginning.
- Destroy Window A.
- Attacker calls the RegiterClassEx API twice to register two window classes (ExtraWnd1 and ExtraWnd2). The ExtraWnd1 class's cbWndExtra is 0x118. For ExtraWnd2, the comparable value is 0x130. A window created with either of the above classes would have the corresponding value for tagWnd as well.
- Create two windows, one for each class in the previous step. Let us call these ExtraWnd1 and ExtraWnd2.
- Attacker calls HMValidateHandle for the two windows.
- Attacker traverses memory starting from the mapping address of ExtraWnd1 in user mode to the address which was calculated during step b. If it finds 0x118, use the same offset for the mapping address of ExtraWnd2. If the value there is 0x130, the offset is correct for the tagWnd object's cbWndExtra field.
- Destroy the two windows.
- Create a thread. This thread will contain the main exploit steps, and let's call it the exploit thread.
- In the exploit thread, the attacker attempts to use the vulnerability to corrupt one tagWnd object's field. If this is successful, the attacker can corrupt more memory after the tagWnd object by using the win32k NtUserSetWindowLongPtr service call from user mode. Here's how this would work:
- Create 0x100 windows using the CreateWindowEx API call. Let's call these windows MyExtraWnd, whose tagWnd object field cbWndExtra is 0.
- Traverse each handle in the 0x100 MyExtraWnd windows. We need to find two windows whose tagWnd object kernel address's distance is below 0x3fd00. How do we get the tagWnd object's kernel address? The HMValidateHandle function is problematic because it leaks too much kernel information. It maps the tagWnd object and extra memory to user mode. The field tagWnd.head.pSelf contains the tagWnd object's kernel address. An attacker can get the kernel address of these tagWnd objects, and find two whose kernel address's distance is below 0x3fd00. Let's call these two windows TargetWnd1 and TargetWnd2.
- Destroy the other windows.
- The attacker creates a window named MyMainWnd and sets the window style WS_CHILD by using the API call SetWindowLong.
- Calculate the value using following formula: Value = (TargetWnd1 kernel address) + (offset of field cbwndExtra) + (0x3) - (0x28) The resulting value will be used in the next step.
- Attacker calls the win32k service system call to trigger NtUserSetWindowLongPtr as follows: syscall(0x133a, MyMainWnd window handle, GWL_ID, value from previous step) 0x133a is corresponding syscall ID on windows 7 64 bit version.
- Attack calls keybd_event to simulate a VK_MENU key event. This event, combined with the events mentioned in step 2, will trigger the corruption point in xxxNextWindow.
At this stage, the memory looks like following figure:
Figure 3. One bit setThis figure shows us why we used the formula in step e. TargetWnd1's tagWnd object field cbwndExtra is changed from 0 to 0x40000000. By changing just one bit, the attacker can now read and write any address in a large range in the kernel space by just setting one bit.
- The attacker wants to get the ability to read content from a specific kernel address from user mode by exploiting the corrupted TargetWnd1. Let’s call this ability a function, called ReadFromKernel(address). .
- Get the value of TargetWnd2's tagWnd object field spwndParent; this has the value: *(HMValidateHandle(TargetWnd2 handle) + offset of spwndParent field in tagWnd data structure)
- Calculate the distance from TargetWnd1’s extra memory kernel address to TargetWnd2 ‘s spwndParent kernel address: The distance = (TargetWnd2’s tagWnd kernel address) + (offset of spwndParent field in tagWnd data structure) – ((TargetWnd1 tagWnd kernel address) + (offset of extra memory in tagWnd)) The offset of the extra memory was obtained earlier, in step 4c.
- Call the win32k service system call to trigger NtUserSetWindowLongPtr as follows: syscall(0x133a, TargetWnd1 window handle , distance from step b, kernel address want to read)
- Call the NtUserGetAncestor API on TargetWnd2 window handle, like so:
NtUserGetAncestor(TargetWnd2 window handle, GA_PARENT )
This returns a 32-bit integer, which is the content read from the kernel address in step c. This happens because the NtUserGetAncestor API calls the win32k service function NtUserGetAncestor in the kernel. If the argument is GA_PARENT, the function can be simplify as follows:
Value = *(tagWnd. spwndParent)
The window object status would be as described in the figure below:
Figure 4. TargetWnd2 object corruptedTherefore, if one calls NtUserGetAncestor(TargetWnd2 window handle, GA_PARENT ), the returned value is *(Targetwnd2 tagwnd.spwndParent) => *(kernel address to be read)
- Call the win32k service system call to trigger NtUserSetWindowLongPtr as follows: syscall(0x133a, TargetWnd1 window handle, distance from step b, original TargetWnd2 tagWnd.spwndParent) This restores the original value, to keep the system stable.
- The attacker wants to get the ability to write content to a specific kernel address from user mode by exploiting the corrupted TargetWnd1. Let's call this capability a function, called WriteToKernel(address, content).
- Get TargetWnd2's tagWnd.strName.Buffer value using the HMValidateHandle method.
- Calculate the distance from TargetWnd1’s extra memory kernel address to TargetWnd2's tagWnd.strName.Buffer kernel address, calculated in the step above. The distance = (TargetWnd2’s tagWnd kernel address) + ((offset of tagWnd.strName.Buffer field in tagWnd data structure) – (TargetWnd1 tagWnd kernel address) + (offset of extra memory in tagWnd))
- Make a win32k service system call to trigger NtUserSetWindowLongPtr as follows: syscall(0x133a, TargetWnd1 window handle , distance from step b, kernel address)
- Call the SetWindowText API to write the content:
SetWindowText(TargetWnd2 window handle, content buffer)
content buffer contains the location of the content that is to be written.
At this time, the status of the window objects are as follows:
Figure 5. TargetWnd2 object corruptionThis SetWindowText API call can be simplified as strcpy(TargetWnd2 tagWnd.strName.Buffer, content buffer) => strcpy(kernel address, content buffer)
- Call the win32k service system call to trigger NtUserSetWindowLongPtr as follows: syscall(0x133a, TargetWnd1 window handle, distance from step b, original TargetWnd2 tagWnd.strName.Buffer) This restores the original value, to keep the system stable.
- Attacker replaces the current process’s token with the system process’s token to escalate the available privileges.
- Traverse all EProcess objects by using ReadFromKernel(tagWnd.head.pti -> ppi -> Process-> ActiveProcessLinks of kernel address).
- To find the current process’s EProcess object kernel address and system process’s EProcess object kernel address, compare their process IDs. The system process has an ID of 4.
- Get the system process's token value by using ReadFromKernel(EProcess.token kernel address).
- Use WriteToKernel(current EProcess.token kernel address , system process token) to replace the current process's token with the one from the system process.
- Destroy both TargetWnd1 and TargetWnd2 tagWnd objects.
- Resume all threads in the current process.
- 25718: HTTP: Microsoft Windows Privilege Escalation Vulnerability
- 1008033-Microsoft Windows Elevation Of Privilege Vulnerability
- 1008034-Microsoft Windows Multiple Security Vulnerabilities (MS16-135)