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.