One Bit To Rule A System: Analyzing CVE-2016-7255 Exploit In The Wild

Recently, Google researchers discovered a local privilege escalation vulnerability in Windows which was being used in zero-day attacks, including those carried out by the Pawn Storm espionage group. Microsoft was able to release a patch by the next Patch Tuesday, November 8. This entry provides a complete analysis of the vulnerability based on samples acquired in the wild.

This is an easily exploitable vulnerability which can be found in all supported versions of Windows, from Windows 7 to Windows 10. The exploit code we’ve seen in the wild only affects 64-bit versions of Windows, although both 32- and 64-bit versions have the underlying flaw. Let us examine this vulnerability in some detail 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.

Description of the vulnerability

The vulnerability is in the win32k.sys kernel module, which is a well-known attack surface. The function NtUserSetWindowLongPtr replaces the target window’s spmenu field with the function’s argument without any checks when using GWLP_ID and the target window’s style is WS_CHILD. NtUserSetWindowLongPtr is a win32k service function which can be called from user mode by using the corresponding system call ID. In other words, this gives user mode code a way to replace the target window’s spmenu value with anything.

The win32k function xxxNextWindow gets the target window’s spmenu value and takes it as pointer to tagMenu object. It sets one bit in the tagMenu object’s fFlags field (tagMenu.fFlags = tagMenu.fFlags|0x4) when the input argument is VK_MENU. The fFlags offset in the tagMenu data structure is 0x28. User mode code can use the keybd_event API call to trigger xxxNextWindow. The following figure summarizes these relationships:

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:

  1. Attacker suspends all threads in the current process (the browser, iexplore.exe), except the thread which is running the exploit shellcode.
  2. Attacker creates a new thread (let’s call this thread A). Thread A does the following:
    1. Sleep 400 milliseconds
    2. Make multiple keybd_event calls to simulate the user pressing Ctrl+Tab
  3. 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:

    1. syscall id for the function NtUserSetWindowLongPtr. On 64-bit versions of Windows 7, this is 0x133a.
    2. offset of data structure tagTHREADINFO’s field ppi. On 64-bit versions of Windows 7, this is 0x158.
    3. offset of data structure EProcess’s field ActiveProcessLinks. On 64-bit versions of Windows 7, this is 0x188.
    4. offset of data structure EProcess‘s field Token. This is 0x208.
  4. 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 cbwndExtra

    The 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:

    1. 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.
    2. 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,
    3. 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.
    4. Destroy Window A.
    5. 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.
    6. Create two windows, one for each class in the previous step. Let us call these ExtraWnd1 and ExtraWnd2.
    7. Attacker calls HMValidateHandle for the two windows.
    8. 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.
    9. Destroy the two windows.
  5. Create a thread. This thread will contain the main exploit steps, and let’s call it the exploit thread.
  6. 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:
    1. Create 0x100 windows using the CreateWindowEx API call. Let’s call these windows MyExtraWnd, whose tagWnd object field cbWndExtra is 0.
    2. 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.
    3. Destroy the other windows.
    4. The attacker creates a window named MyMainWnd and sets the window style WS_CHILD by using the API call SetWindowLong.
    5. 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.
    6. 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.
    7. 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 set

      This 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.

  7. 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). .
    1. 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)
    2. 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.
    3. 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)
    4. 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 corrupted

      Therefore, if one calls NtUserGetAncestor(TargetWnd2 window handle, GA_PARENT ), the returned value is *(Targetwnd2 tagwnd.spwndParent) => *(kernel address to be read)

    5. 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.
  8. 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).
    1. Get TargetWnd2′s tagWnd.strName.Buffer value using the HMValidateHandle method.
    2. 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))
    3. Make a win32k service system call to trigger NtUserSetWindowLongPtr as follows:
      syscall(0x133a, TargetWnd1 window handle , distance from step b, kernel address)
    4. 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 corruption

      This SetWindowText API call can be simplified as strcpy(TargetWnd2 tagWnd.strName.Buffer, content buffer) => strcpy(kernel address, content buffer)

    5. 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.
  9. Attacker replaces the current process’s token with the system process’s token to escalate the available privileges.
    1. Traverse all EProcess objects by using ReadFromKernel(tagWnd.head.pti -> ppi -> Process-> ActiveProcessLinks of kernel address).
    2. 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.
    3. Get the system process’s token value by using ReadFromKernel(EProcess.token kernel address).
    4. Use WriteToKernel(current EProcess.token kernel address , system process token) to replace the current process’s token with the one from the system process.
  10. Destroy both TargetWnd1 and TargetWnd2 tagWnd objects.
  11. Resume all threads in the current process.

Conclusion

The win32k.sys kernel module is a well-known target for exploit authors. It’s worth noting that by design, the Chrome sandbox does not allow any win32k.sys system calls at all. This helps reduce the threat from exploits that rely on running code via the browser process.

Microsoft is continuously working to improve the security of win32k.sys, as this is a frequent target of attackers looking for vulnerabilities in Windows. New mitigation mechanisms have been added into Windows, and in fact this attack would not work on a system running the latest version of Windows 10 (the Anniversary Update), which was released to the public on August 2, 2016,

Trend Micro Solutions

Trend Micro™ Deep Discovery™ provides detection, in-depth analysis, and proactive response to today’s stealthy malware, and targeted attacks in real-time. It provides a comprehensive defense tailored to protect organizations against targeted attacks and advanced threats through specialized engines, custom sandboxing, and seamless correlation across the entire attack lifecycle, allowing it to detect threats like the above mentioned zero-day attacks even without any engine or pattern update.

Trend Micro™ Deep Security™ and Vulnerability Protection provide virtual patching that protects endpoints from threats that abuses unpatched vulnerabilities. OfficeScan’s Vulnerability Protection shield endpoints from identified and unknown vulnerability exploits even before patches are deployed.

TippingPoint customers are protected from attacks exploiting these vulnerabilities with the following MainlineDV filters:

  • 25718: HTTP: Microsoft Windows Privilege Escalation Vulnerability

Trend Micro™ Deep Security™ and Vulnerability Protection shield endpoints and networks through Rule update DSRU16-034, which includes these Deep Packet Inspection (DPI) rules:

  • 1008033-Microsoft Windows Elevation Of Privilege Vulnerability
  • 1008034-Microsoft Windows Multiple Security Vulnerabilities (MS16-135)

 

Post from: Trendlabs Security Intelligence Blog – by Trend Micro

One Bit To Rule A System: Analyzing CVE-2016-7255 Exploit In The Wild

Read more: One Bit To Rule A System: Analyzing CVE-2016-7255 Exploit In The Wild

Incoming search terms

Story added 2. December 2016, content source with full text you can find at link above.