I hope you haven’t got bored with bypassing the Driver Signature Enforcement mechanism (present on all 64-bit Microsoft Windows operating systems since Vista) just yet – in either case, stay calm… this is going to be the last post of the series. After using multiple drivers shipped with the OS in the default configuration to trigger Blue Screens of Death from user-mode, and implementing a complete bypass of the functionality by using a design flaw in how the CSRSS subsystem interacts with the win32k.sys kernel module, I am going to present one final argument that the idea of preventing ring-3 applications with administrative privileges from causing bad things happen in kernel-mode is fundamentally flawed and would be never fully effective, unless a complete rewrite of the Windows kernel (including revisiting many low-level system assumptions) would be in order. Don’t keep it too seriously though – I am not claiming that the described Windows behavior has security implications of any kind – rather, it’s just a kernel curiosity illustrating how reliant the kernel is on certain assumptions that haven’t had a chance to be validated for many years.

To provide some context, several years ago Alex Ionescu and omega_red from the woodmann.com reverse-engineering forums discovered that invoking certain graphical system calls (i.e. the ones handled by win32k.sys) from within one of a few system processes would result in an unhandled kernel exception, a system bugcheck and potentially an exploitable condition (the original crash was caused by a NULL Pointer Dereference). None of the system processes would ever call the affected system services in a way that would bring the system down, so the only way for an attacker to achieve the same effect would be to inject a malicious payload into one of the highly-privileged processes and execute it – an activity which clearly takes an Administrator account to succeed. Alex got so intrigued by the post that he performed an in-depth investigation of this weird behavior and later presented the results of his work along with some other win32k.sys security-related findings on the BlackHat US 2008 conference in a presentation called “Pointers and Handles: A Story of Unchecked Assumptions in the Windows Kernel“. In short, the graphical driver blindly dereferenced an internal pointer storing a reference to the caller’s desktop, but since some processes (e.g. CSRSS.EXE) were not assigned to any specific desktop, the kernel module would end up using a NULL pointer as a valid one: as straight-forward as it sounds.

What comes to mind first after reading through the slides is “if there is one win32k.sys syscall with an unchecked assumption like this, there are probably a bunch of others, too?”. There was no other way to find out but check myself, which I happened to do 2.5y ago, but the results have remained unpublished until today :) Let’s start with a very trivial idea: a simple system call fuzzer which when injected into the CSRSS process would indefinitely call completely random graphical services with random parameters. The source code of the one that I was using (Windows 7 64-bit only) can be found here (csrss_win32k_fuzzer.zip, 5,63kB). As it turned out after just a few minutes of running the fuzzer, the outcome greatly exceeded my expectations – I was able to easily reproduce almost twenty different system crashes using just this very simple approach.

Here’s a list of the system calls that I found vulnerable back in 2010; keep in mind that I was performing the tests on Windows 7 SP0, so some of them might have already been fixed (rather unlikely, though):

win32k!NtUserActivateKeyboardLayout

win32k!NtUserChangeDisplaySettings

win32k!NtUserCreateWindowEx

win32k!NtUserDdeInitialize

win32k!NtUserFindWindowEx

win32k!NtUserGetDCEx

win32k!NtUserGetWindowDC

win32k!NtUserInvalidateRect

win32k!NtUserLoadKeyboardLayoutEx

win32k!NtUserMagControl

win32k!NtUserRedrawWindow

win32k!NtUserRemoveProp

win32k!NtUserSetCapture

win32k!NtUserSetProp

win32k!NtUserTrackMouseEvent

win32k!NtUserUnloadKeyboardLayout

win32k!NtUserValidateRect

win32k!NtUserWindowFromPoint

These were all routines that were dereferencing a pointer to an internal structure which hadn’t been initialized before due to the nature of the CSRSS process – all of them crashed on a NULL pointer dereference condition. Given the fact that the fuzzer chooses completely senseless parameters for the system calls, I suspect there there must be at least twice as many of them residing somewhere in the graphical driver. As I analyzed a few of them, I realized that most would be quite difficult to exploit in a reliable manner, and even when successfully exploited… well, it only converts administrative privileges in your system into ring-0 access. Still, I contacted MSRC about the cases, but they apparently haven’t received any attention yet as the crashes still reproduce. If you are curious, feel free to investigate any of them on your own.

Below follow excerpts of a bunch of bugchecks triggered a few years ago that I found particularly interesting. Have fun!

win32k!NtUserValidateRect

FAULTING_IP: win32k!xxxRedrawWindow+2e fffff960`000b62ae 488b4108 mov rax,qword ptr [rcx+8] CONTEXT: fffff880065c8170 -- (.cxr 0xfffff880065c8170) rax=fffff900c01edc30 rbx=0000000000000000 rcx=0000000000000000 rdx=0000000000000000 rsi=0000000000000285 rdi=0000000000000000 rip=fffff960000b62ae rsp=fffff880065c8b50 rbp=fffff880065c8ca0 r8=0000000000000000 r9=0000000000000285 r10=fffff9600013c9dc r11=0000000000000000 r12=0000000000000000 r13=0000000000000000 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl zr na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010246 win32k!xxxRedrawWindow+0x2e: fffff960`000b62ae 488b4108 mov rax,qword ptr [rcx+8] ds:002b:00000000`00000008=???????????????? Resetting default scope DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT BUGCHECK_STR: 0x3B PROCESS_NAME: csrss.exe CURRENT_IRQL: 2 LAST_CONTROL_TRANSFER: from fffff9600013ca9e to fffff960000b62ae STACK_TEXT: fffff880`065c8b50 fffff960`0013ca9e : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : win32k!xxxRedrawWindow+0x2e fffff880`065c8bb0 fffff800`026991d3 : fffffa80`00af2660 00000000`00000000 00000000`00000000 fffffa80`00af2660 : win32k!NtUserValidateRect+0xc2 fffff880`065c8c20 000007fe`f8002122 : 000007fe`f800121f 00000000`000b000a 000007fe`f80010d8 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 00000000`0225fa88 000007fe`f800121f : 00000000`000b000a 000007fe`f80010d8 00000000`00000000 00000000`77388fb4 : fuzz+0x2122 00000000`0225fa90 00000000`000b000a : 000007fe`f80010d8 00000000`00000000 00000000`77388fb4 00000000`00000000 : fuzz+0x121f 00000000`0225fa98 000007fe`f80010d8 : 00000000`00000000 00000000`77388fb4 00000000`00000000 00000000`17a52b11 : 0xb000a 00000000`0225faa0 00000000`00000000 : 00000000`77388fb4 00000000`00000000 00000000`17a52b11 00000000`17a52b73 : fuzz+0x10d8

win32k!NtUserSetCapture

FAULTING_IP: win32k!xxxCapture+e7 fffff960`0013a14b 8b4828 mov ecx,dword ptr [rax+28h] CONTEXT: fffff88006d7b100 -- (.cxr 0xfffff88006d7b100) rax=0000000000000000 rbx=0000000000000000 rcx=fffff900c1a0a010 rdx=0000000000000000 rsi=0000000000000002 rdi=0000000000000000 rip=fffff9600013a14b rsp=fffff88006d7bae0 rbp=fffff900c01a93f0 r8=0000000000000002 r9=0000000000000c00 r10=fffff960000eb818 r11=fffff900c1a0a010 r12=fffff900c1a0a010 r13=0000000000000000 r14=0000000000000001 r15=0000000000000000 iopl=0 nv up ei ng nz na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010286 win32k!xxxCapture+0xe7: fffff960`0013a14b 8b4828 mov ecx,dword ptr [rax+28h] ds:002b:00000000`00000028=???????? Resetting default scope DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT BUGCHECK_STR: 0x3B PROCESS_NAME: csrss.exe CURRENT_IRQL: 2 LAST_CONTROL_TRANSFER: from fffff96000139fa3 to fffff9600013a14b STACK_TEXT: fffff880`06d7bae0 fffff960`00139fa3 : 00000000`00000000 fffff880`06d7bca0 fffff880`06d7bca0 00000000`00000000 : win32k!xxxCapture+0xe7 fffff880`06d7bba0 fffff960`000eb890 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : win32k!xxxSetCapture+0x5f fffff880`06d7bbd0 fffff800`026c81d3 : fffffa80`0218a2b0 00000000`7b78c0cb 00000000`00000000 00000000`00000000 : win32k!NtUserSetCapture+0x78 fffff880`06d7bc20 000007fe`f88c2122 : 000007fe`f88c121f 00000000`000b000a 000007fe`f88c10d8 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 00000000`0216fb98 000007fe`f88c121f : 00000000`000b000a 000007fe`f88c10d8 00000000`00000000 00000000`777a8fb4 : fuzz+0x2122 00000000`0216fba0 00000000`000b000a : 000007fe`f88c10d8 00000000`00000000 00000000`777a8fb4 00000000`7c0e37e1 : fuzz+0x121f 00000000`0216fba8 000007fe`f88c10d8 : 00000000`00000000 00000000`777a8fb4 00000000`7c0e37e1 00000000`00000000 : 0xb000a 00000000`0216fbb0 00000000`00000000 : 00000000`777a8fb4 00000000`7c0e37e1 00000000`00000000 00000000`7c0e3884 : fuzz+0x10d8

win32k!NtUserFindWindowEx

FAULTING_IP: win32k!FindWindowEx+b7 fffff960`001f78d7 488b4f60 mov rcx,qword ptr [rdi+60h] CONTEXT: fffff880068e00e0 -- (.cxr 0xfffff880068e00e0) rax=fffff960003ce600 rbx=0000000000000000 rcx=0000000000000000 rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000 rip=fffff960001f78d7 rsp=fffff880068e0ac0 rbp=fffff880068e0ca0 r8=0000000000000000 r9=0000000000000000 r10=0000000000000002 r11=0000000000000000 r12=0000000000000000 r13=00000000021df880 r14=0000000000000001 r15=0000000000000001 iopl=0 nv up ei pl zr na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010246 win32k!FindWindowEx+0xb7: fffff960`001f78d7 488b4f60 mov rcx,qword ptr [rdi+60h] ds:002b:00000000`00000060=???????????????? Resetting default scope DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT BUGCHECK_STR: 0x3B PROCESS_NAME: csrss.exe CURRENT_IRQL: 2 LAST_CONTROL_TRANSFER: from fffff960001cfbbf to fffff960001f78d7 STACK_TEXT: fffff880`068e0ac0 fffff960`001cfbbf : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : win32k!FindWindowEx+0xb7 fffff880`068e0b30 fffff800`026861d3 : fffffa80`00ae0b60 00000000`021df848 fffff880`068e0bc8 000007fe`fb5812a0 : win32k!NtUserFindWindowEx+0x167 fffff880`068e0bb0 00000000`7763020a : 00000000`77648a89 00000000`021df890 00000000`00000000 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 00000000`021df828 00000000`77648a89 : 00000000`021df890 00000000`00000000 00000000`00000000 00000000`00000000 : USER32!NtUserFindWindowEx+0xa 00000000`021df830 00000000`776489f8 : 00000000`001ae320 00000000`021dfe28 00000000`00000000 000007fe`fb5817c2 : USER32!InternalFindWindowExA+0x96 00000000`021df8b0 000007fe`fb58132f : 000007fe`fb5810c8 00000000`00000000 00000000`021df858 00000000`021df978 : USER32!FindWindowA+0x18 00000000`021df8f0 000007fe`fb5810c8 : 00000000`00000000 00000000`021df858 00000000`021df978 000007fe`fb580001 : fuzz+0x132f 00000000`021df8f8 00000000`00000000 : 00000000`021df858 00000000`021df978 000007fe`fb580001 000007fe`fb5817c2 : fuzz+0x10c8

win32k!NtUserDdeInitialize

FAULTING_IP: win32k!HMFreeObject+f5 fffff960`00159b19 488b8f80000000 mov rcx,qword ptr [rdi+80h] CONTEXT: fffff8800264cee0 -- (.cxr 0xfffff8800264cee0) rax=fffff900c0084ce0 rbx=fffff900c0403870 rcx=fffff960003381bc rdx=0000000000000000 rsi=fffff900c019e120 rdi=0000000000000000 rip=fffff96000159b19 rsp=fffff8800264d8c0 rbp=fffff900c019e139 r8=0000000000000000 r9=0000000000000000 r10=00000000c4000000 r11=fffffa80027efb60 r12=0000000000140259 r13=0000000000000001 r14=0000000000000000 r15=0000000000000000 iopl=0 nv up ei pl zr na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010246 win32k!HMFreeObject+0xf5: fffff960`00159b19 488b8f80000000 mov rcx,qword ptr [rdi+80h] ds:002b:00000000`00000080=???????????????? Resetting default scope DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT BUGCHECK_STR: 0x3B PROCESS_NAME: csrss.exe CURRENT_IRQL: 2 LAST_CONTROL_TRANSFER: from fffff9600015f252 to fffff96000159b19 STACK_TEXT: fffff880`0264d8c0 fffff960`0015f252 : 00000000`00000000 fffff900`c019e178 00000000`00000000 fffff900`c019e120 : win32k!HMFreeObject+0xf5 fffff880`0264d900 fffff960`0015fa20 : 00000000`00000000 fffff900`c019e120 fffff900`c1a95010 00000000`00000000 : win32k!xxxFreeWindow+0x1332 fffff880`0264da00 fffff960`00196276 : fffff900`00000000 fffff900`c2264350 fffff880`0264dca0 fffff900`c2264350 : win32k!xxxDestroyWindow+0x6e0 fffff880`0264dab0 fffff960`00167336 : 00000000`00000000 fffff880`0264dca0 fffff900`c2264350 00000000`00000000 : win32k!xxxDestroyThreadDDEObject+0xc2 fffff880`0264dae0 fffff800`026e41d3 : fffffa80`027efb60 00000000`022ff828 fffff880`0264dbc8 00000000`00000000 : win32k!NtUserDdeInitialize+0x1d6 fffff880`0264dbb0 000007fe`f2e42122 : 000007fe`f2e4121f 00000000`000b000a 000007fe`f2e410d8 00000000`00000000 : nt!KiSystemServiceCopyEnd+0x13 00000000`022ff808 000007fe`f2e4121f : 00000000`000b000a 000007fe`f2e410d8 00000000`00000000 00000000`774a8fb4 : fuzz+0x2122 00000000`022ff810 00000000`000b000a : 000007fe`f2e410d8 00000000`00000000 00000000`774a8fb4 00000000`5cc008b0 : fuzz+0x121f 00000000`022ff818 000007fe`f2e410d8 : 00000000`00000000 00000000`774a8fb4 00000000`5cc008b0 00000000`00000000 : 0xb000a 00000000`022ff820 00000000`00000000 : 00000000`774a8fb4 00000000`5cc008b0 00000000`00000000 00000000`5cc00976 : fuzz+0x10d8

win32k!NtUserCreateWindowEx

FAULTING_IP: win32k!InternalGetRealClientRect+f fffff960`0009d0e3 0fb74142 movzx eax,word ptr [rcx+42h] CONTEXT: fffff88002a8cd70 -- (.cxr 0xfffff88002a8cd70) rax=0000000000000000 rbx=0000000080000000 rcx=0000000000000000 rdx=fffff88002a8d7b0 rsi=0000000000000000 rdi=0000000000000000 rip=fffff9600009d0e3 rsp=fffff88002a8d750 rbp=fffff88002a8d888 r8=0000000000000002 r9=fffff900c05824b0 r10=0000000000000000 r11=0000000000000100 r12=fffff900c21ed250 r13=fffff900c05824b0 r14=fffff900c05824b0 r15=0000000000000000 iopl=0 nv up ei ng nz na po nc cs=0010 ss=0018 ds=002b es=002b fs=0053 gs=002b efl=00010286 win32k!InternalGetRealClientRect+0xf: fffff960`0009d0e3 0fb74142 movzx eax,word ptr [rcx+42h] ds:002b:00000000`00000042=???? Resetting default scope DEFAULT_BUCKET_ID: VISTA_DRIVER_FAULT BUGCHECK_STR: 0x3B PROCESS_NAME: csrss.exe CURRENT_IRQL: 2 LAST_CONTROL_TRANSFER: from fffff9600009cef6 to fffff9600009d0e3 STACK_TEXT: fffff880`02a8d750 fffff960`0009cef6 : 00000000`80000000 00000000`00000000 fffff880`0000002c 00000000`00000002 : win32k!InternalGetRealClientRect+0xf fffff880`02a8d780 fffff960`000bced8 : 00000000`80000000 fffff880`02a8dca0 00000000`00000000 00000000`00000000 : win32k!SetTiledRect+0x5a fffff880`02a8d7e0 fffff960`000bff98 : 00000000`00000000 00000000`00000000 fffff880`02a8dae8 00000000`00000000 : win32k!xxxCreateWindowEx+0x18e4 fffff880`02a8da50 fffff800`026921d3 : fffffa80`02584b60 fffff880`02a8dad0 fffff880`02a8dae8 00000000`00000000 : win32k!NtUserCreateWindowEx+0x554 fffff880`02a8dbb0 00000000`76d1255a : 00000000`76d129d7 00000000`0224f308 00000000`00000000 00000000`0224f308 : nt!KiSystemServiceCopyEnd+0x13 00000000`0224eed8 00000000`76d129d7 : 00000000`0224f308 00000000`00000000 00000000`0224f308 00000000`0224f308 : USER32!NtUserCreateWindowEx+0xa 00000000`0224eee0 00000000`76d12718 : 00000000`00000018 00000000`80000000 00000000`00000000 00000000`00000000 : USER32!VerNtUserCreateWindowEx+0x27c 00000000`0224f250 00000000`76d0c9a0 : 00000000`00000000 00000000`00413f90 00000000`0224fa28 00000000`00000000 : USER32!CreateWindowEx+0x404 00000000`0224f3a0 000007fe`f44e13fa : 00000000`0224f480 00000000`76f725a8 000007fe`00000000 00000000`00001112 : USER32!CreateWindowExA+0x70 00000000`0224f420 00000000`0224f480 : 00000000`76f725a8 000007fe`00000000 00000000`00001112 000007fe`00000000 : fuzz+0x13fa 00000000`0224f428 00000000`76f725a8 : 000007fe`00000000 00000000`00001112 000007fe`00000000 000007fe`00000000 : 0x224f480 00000000`0224f430 00000000`00000000 : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlpFreeHeap+0x528