Windows Kernel Local Denial-of-Service #2: win32k!NtDCompositionBeginFrame (Windows 8-10)

Another week, another way to locally crash the Windows kernel with an unhandled exception in ring-0 code (if you haven’t yet, see last week’s DoS in win32k!NtUserThunkedMenuItemInfo). Today, the bug is in the win32k!NtDCompositionBeginFrame system call handler, whose beginning can be translated into the following C-like pseudo-code:

NTSTATUS STDCALL NtDCompositionBeginFrame(HANDLE hDirComp, PINPUT_STRUCTURE lpInput, POUTPUT_STRUCTURE lpOutput) {
  NTSTATUS st;
  INPUT_STRUCTURE Input;
  DirectComposition::CConnection *Connection;

  if (lpInput != NULL) {
    try {
      ProbeForRead(lpInput, sizeof(INPUT_STRUCTURE), 1);
      RtlCopyMemory(&Input, lpInput, sizeof(INPUT_STRUCTURE));
      st = STATUS_SUCCCESS;
    } __except(EXCEPTION_EXECUTE_HANDLER) {
      st = GetExceptionCode();
    }
  } else {
    st = STATUS_INVALID_PARAMETER;
  }

  KeEnterCriticalRegion();
  if (NT_SUCCESS(st)) {
    st = DirectComposition::CConnection::ReferenceHandle(hDirComp, &Connection);
    if (NT_SUCCESS(st)) {
      if (Microsoft_Windows_Win32kEnableBits & 1) {
        Template_xq(&DCompBeginFrameEvent, hDirComp, lpInput->SomeField);
      }
      [...]
    }
  }

  [...]
}

Since the i/o structure names and definitions are not known to me, I just generically called them INPUT_STRUCTURE and OUTPUT_STRUCTURE; their details are non-essential to understand the bug. Here, we can see that the 2nd argument (lpInput) is accessed twice: once in line 9, with a proper sanitization with an inlined ProbeForRead call and a try/except block, but then also in line 23, where a field at offset 0x10 (SomeField in the above listing) is read from the user pointer while exception handling is disabled. The Template_xq function is just a thin wrapper around EtwWrite, which is used for logging kernel-mode events. This is the bug we want to exploit.

Read more

Windows Kernel Local Denial-of-Service #1: win32k!NtUserThunkedMenuItemInfo (Windows 7-10)

Back in 2013, Gynvael and I published the results of our research into discovering so-called double fetch vulnerabilities in operating system kernels, by running them in full software emulation mode inside of an IA-32 emulator called Bochs. The purpose of the emulation (and our custom embedded instrumentation) was to capture detailed information about accesses to user-mode memory originating from the kernel, so that we could later run analysis tools to discover multiple references to single memory addresses within the scope of one system call, and produce meaningful reports. The project was called Bochspwn [1][2][3] (or kfetch-toolkit on Github) and was largely successful, leading to the discovery of several dozen serious vulnerabilities in the Windows kernel. We believe it also played a significant role in popularizing the double-fetch vulnerability class and the concept of using system-wide instrumentation for security, as several other fruitful projects ensued as a result, probably most notable of which is Xenpwn.

After all this time, I decided to get back on the subject of full system instrumentation and analyzing various execution traces in search of indicators of potential vulnerabilities. Specifically, one of my goals was to develop more patterns (based on memory accesses or other events) which could signal problems in kernel-mode code other than just double fetches. One intuitive example of such pattern is the lack of exception handling being set up at the time of accessing ring-3 memory area. As the documentation of the Windows ProbeForRead function states:

Drivers must call ProbeForRead inside a try/except block. If the routine raises an exception, the driver should complete the IRP with the appropriate error. Note that subsequent accesses by the driver to the user-mode buffer must also be encapsulated within a try/except block: a malicious application could have another thread deleting, substituting, or changing the protection of user address ranges at any time (even after or during a call to ProbeForRead or ProbeForWrite).

Read more