Defeating Windows Driver Signature Enforcement #1: default drivers

One of the obvious things about the Windows operating system for anyone actively working on its kernel security is that the Driver Signature Enforcement (DSE in short) is not effective and can be bypassed with relative ease by any determined individual. From a historical perspective, the “feature” was introduced in the 64-bit build of Windows Vista in 2007 and has been shipped with all 64-bit Windows editions since then. In essence, it was designed to prevent unsigned device drivers (or kernel modules in general) from being loaded and executed with ring-0 privileges. Consequently, it broke one of the fundamental Windows assumptions followed since the early years of the NT family – administrative privileges in the system would no longer be equivalent to the ability to run arbitrary ring-0 code, effectively introducing a completely new layer of privilege separation in the system.

Soon after the change was presented to wide audience, the enforcement was spectacularly defeated by Joanna Rutkowska, who took advantage of the fact that users in the Administrators group had been granted access to raw disk data and thus were able to modify parts of the pagefile.sys swap file potentially containing paged-out ring-0 code, to later execute the injected payload by crafting a special kernel-mode request (IRP to the NULL.sys driver in that particular case). Joanna’s and Alexander’s presentation was titled IsGameOver() Anyone? and received quite a lot of media attention at the time (mid 2007), starting a global discussion regarding the sense and security implications of introducing the mechanism. As a direct outcome, Microsoft decided to address this particular attack by disabling user-mode access to raw disk contents, and later on follow up with additional page hash/signing implementation for the hibernation and swap files (thanks Alex!). Since five years ago, the mechanism hasn’t been publicly criticized or otherwise discussed anymore – perhaps everyone just got used to its existence and (in)effectiveness.

Although you can think of it in terms of an additional barrier that ring-0 badware developers have to face, it certainly isn’t considered a security measure. Despite the extra separation level, admin → kernel escalations are not of much interest to the overall community, given that administrative rights are by far enough to compromise a system and guarantee infction persistence (with the small exception of the ability to communicate with machine peripherals, e.g. in order to set up a MBR/VBR rootkit). Additionally, a number of kernel-mode Windows components and features created in the pre-Vista era relies on ultimate trust to the Administrators group on so many levels that I really doubt it is realistically possible to reliably separate the two privilege levels at this point without investing an incredible amount of resources to introduce significant changes into the many areas of current system design.

I don’t usually pay too much attention to admin → kernel escalations when I sometimes stumble upon them during my daily work. However, today I would like to showcase an interesting set of bugs that I accidentally noticed a few weeks ago while doing some unrelated research. The nature of these bugs visibly exposes how flawed the signature enforcement idea has been from the very beginning. So, to the point: all device drivers present in the default Windows installation directory (Windows\system32\drivers) are obviously digitally signed and thus can be freely loaded or unloaded, assuming administrative privileges. Performing any of the two operations upon most .sys files doesn’t lead to any interesting behavior; however, if you attempt to unload some of them – or even better, load them in ring-0 more than once – you might end up triggering various types of Windows bugchecks, some of them in the most interesting locations or contexts I have seen in a long time. It turns out that these otherwise high-quality kernel modules fail to properly recover from encountering an error during DriverEntry initialization, or have serious bugs in the DriverUnload routines, most likely assuming that they would never be used anyway. Depending on the exact edition of Microsoft Windows, you can find up to 10 device drivers that are affected by the described flaws (out of ~300, if you’re interested in the metrics) in a default system installation.

Nobody expects a driver loaded twice!

Keep in mind that a system crash in one of those drivers doesn’t necessarily imply that it can be successfully exploited and used to run arbitrary ring-0 code. For instance, both DriverEntry and DriverUnload routines are typically called from within the System process, one that is inaccessible from user-mode; therefore, any NULL Pointer Dereferences occuring there aren’t of much use as they will always result in a Blue Screen of Death. As you will see, a few of the reproduced crashes are more complicated and affect the system state as a whole (e.g. by corrupting the kernel pools), therefore, they are likely enough to subvert the Driver Signature Enforcement mechanism. Ironically, a feature that was bypassed using a fairly complex attack and argued over for a long time could have been defeated with just the default drivers shipped with every Windows out there… that’s fascinating :-)

Below are listed eight crashes that I have reproduced on a Windows 7 SP1 64-bit machine; additionally, there is one more case of a driver that only fails under the new Windows 8 operating system. For each discussed driver, you can also find a short explanation of the programming bug that led to the bugcheck. I’m intentionally not including reliable exploits for any of the issues. Some of them do seem like good exploitation candidates, though :) Have fun!

acpi.sys

TRAP_FRAME:  fffff88003174430 -- (.trap 0xfffff88003174430)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=0000000000000001 rbx=0000000000000000 rcx=0000000000008086
rdx=0000000000007190 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80002e38740 rsp=fffff880031745c8 rbp=0000000000000000
r8=0000000000000001  r9=fffff88003174620 r10=0000000000000003
r11=fffff880031745c0 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
hal!HalpGetChipHacks:
fffff800`02e38740 ??              ???
Resetting default scope

LAST_CONTROL_TRANSFER:  from fffff80002974d92 to fffff80002885490

STACK_TEXT:  
fffff880`03173b58 fffff800`02974d92 : fffff800`02e38740 fffffa80`00cd7040 00000000`00000065 fffff800`028c9178 : nt!RtlpBreakWithStatusInstruction
fffff880`03173b60 fffff800`02975b7e : fffff880`00000003 fffff880`03174430 fffff800`028c99d0 00000000`00000050 : nt!KiBugCheckDebugBreak+0x12
fffff880`03173bc0 fffff800`0288d744 : fffff880`031744b0 fffff800`02e04245 00000000`00000001 00000000`00000000 : nt!KeBugCheck2+0x71e
fffff880`03174290 fffff800`028392ac : 00000000`00000050 fffff800`02e38740 00000000`00000008 fffff880`03174430 : nt!KeBugCheckEx+0x104
fffff880`031742d0 fffff800`0288b76e : 00000000`00000008 fffff800`02e38740 ffff00a0`71908000 00000000`00000000 : nt! ?? ::FNODOBFM::`string'+0x4621f
fffff880`03174430 fffff800`02e38740 : fffff800`02e2a665 00000000`00000000 00000000`00000000 00000000`00000000 : nt!KiPageFault+0x16e
fffff880`031745c8 fffff800`02e2a665 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff800`02c5c0ed : hal!HalpGetChipHacks
fffff880`031745d0 fffff800`02e30ebf : fffff880`034a3f70 00000000`00000000 fffff880`034a5360 00000000`00000001 : hal!HalpPiix4Detect+0x309
fffff880`03174740 fffff880`034c06f8 : ffffffff`800006b8 fffffa80`02759060 fffffa80`02759060 00000000`000007ff : hal!HaliInitPowerManagement+0x37
fffff880`031747b0 fffff800`02c72467 : ffffffff`800006b8 fffffa80`015ac040 fffffa80`02759060 00000000`000007ff : acpi_fffff88003471000!DriverEntry+0x6f0
fffff880`03174860 fffff800`02c72865 : fffffa80`01508220 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0xa07
fffff880`03174b30 fffff800`02897a21 : fffff800`00000000 ffffffff`800006cc fffff800`02c72810 fffff800`02a2a658 : nt!IopLoadUnloadDriver+0x55
fffff880`03174b70 fffff800`02b2acce : 00000000`00000000 fffffa80`00cd7040 00000000`00000080 fffffa80`00cbc040 : nt!ExpWorkerThread+0x111
fffff880`03174c00 fffff800`0287efe6 : fffff800`029ffe80 fffffa80`00cd7040 fffffa80`00cd6680 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
fffff880`03174c40 00000000`00000000 : fffff880`03175000 fffff880`0316f000 fffff880`03173bf0 00000000`00000000 : nt!KeStartSystemThread+0x16

Explanation

The crash occurs during the execution of acpi!DriverEntry – the driver’s entry point calls the hal!HaliInitPowerManagement import routine. From there, the call chain goes all the way to nt!HalpGetChipHacks residing in a special “INIT” section. Since the section is marked as Discardable through a IMAGE_SCN_MEM_DISCARDABLE flag in its Characteristics descriptor field, the corresponding physical memory is unmapped from virtual space as soon as it is no longer needed (that would be early stages of system boot). Consequently, loading acpi.sys driver at any time after the system is already up and running would most likely result in an attempt to execute an unmapped memory area, as shown in the above listing.

bowser.sys

TRAP_FRAME:  fffff880031893c0 -- (.trap 0xfffff880031893c0)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=fffff880031895d8 rbx=0000000000000000 rcx=0000000000000000
rdx=0000000000000100 rsi=0000000000000000 rdi=0000000000000000
rip=fffff80002b9bc54 rsp=fffff88003189550 rbp=fffffa8001dfe000
r8=fffff88003189610  r9=0000000000000000 r10=fffff800029f93c0
r11=fffff88003189668 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz na po nc
nt!ObpGetObjectSecurity+0x2c:
fffff800`02b9bc54 0fb64718        movzx   eax,byte ptr [rdi+18h] ds:5e00:0018=??
Resetting default scope

LAST_CONTROL_TRANSFER:  from fffff8000296cd92 to fffff8000287d490

STACK_TEXT:  
fffff880`03188ae8 fffff800`0296cd92 : ffffffff`ffffffe8 fffffa80`00cdf040 00000000`00000065 fffff800`028c1178 : nt!RtlpBreakWithStatusInstruction
fffff880`03188af0 fffff800`0296db7e : fffff880`00000003 fffff880`031893c0 fffff800`028c19d0 00000000`00000050 : nt!KiBugCheckDebugBreak+0x12
fffff880`03188b50 fffff800`02885744 : fffff8a0`02537f30 00000000`00000000 00000000`000001f1 fffff880`037eb008 : nt!KeBugCheck2+0x71e
fffff880`03189220 fffff800`028312ac : 00000000`00000050 ffffffff`ffffffe8 00000000`00000000 fffff880`031893c0 : nt!KeBugCheckEx+0x104
fffff880`03189260 fffff800`0288376e : 00000000`00000000 ffffffff`ffffffe8 00000000`00000000 fffff880`03189620 : nt! ?? ::FNODOBFM::`string'+0x4621f
fffff880`031893c0 fffff800`02b9bc54 : 00000000`003e002c fffff8a0`0241a350 00e20104`100000e2 40000000`00020020 : nt!KiPageFault+0x16e
fffff880`03189550 fffff880`037d6252 : fffffa80`00000100 00000000`00000000 fffffa80`01dfe000 fffffa80`02075e70 : nt!ObpGetObjectSecurity+0x2c
fffff880`031895e0 fffff880`037ec122 : fffffa80`01dfe000 00000000`00000000 fffffa80`01dfe000 fffffa80`0102b000 : bowser_fffff880037d2000!BowserCreateAdminSecurityDescriptor+0x42
fffff880`03189670 fffff800`02c6a467 : fffffa80`02075e70 fffffa80`02075e70 fffffa80`02075e70 00000000`00000000 : bowser_fffff880037d2000!DriverEntry+0x11a
fffff880`03189860 fffff800`02c6a865 : fffffa80`011cbfc0 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0xa07
fffff880`03189b30 fffff800`0288fa21 : fffff800`00000000 ffffffff`80000830 fffff800`02c6a810 fffffa80`00cdf040 : nt!IopLoadUnloadDriver+0x55
fffff880`03189b70 fffff800`02b22cce : 0b040f0c`05100d05 fffffa80`00cdf040 00000000`00000080 fffffa80`00cc3040 : nt!ExpWorkerThread+0x111
fffff880`03189c00 fffff800`02876fe6 : fffff800`029f7e80 fffffa80`00cdf040 fffffa80`00cde680 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
fffff880`03189c40 00000000`00000000 : fffff880`0318a000 fffff880`03184000 fffff880`031898a0 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

Long story short, the relevant, buggy piece of code within bowser!DriverEntry is represented by the following pseudo-code:

  RtlInitUnicodeString(&BowserNameString, L"\\Device\\LanmanDatagramReceiver");
  IoCreateDevice(DriverObject, 0, &BowserNameString, 0x13u, 0, 0, &DeviceObject);

[...]

  ObGetObjectSecurity(DeviceObject, &SecurityDescriptor, MemoryAllocated);

The driver tries to create a device with a fixed name and after returning from the IoCreateDevice call, it blindly assumes that the operation has succeeded; if it is the second instance of the module in the operating system, the assumption is broken, resulting in numerous dereferences of the NULL address treated as a device object pointer.

Diskdump.sys

STACK_TEXT:  
fffff880`04b98e28 fffff800`029ced92 : 00000000`000001f4 fffffa80`013c7b60 00000000`00000065 fffff800`02923178 : nt!RtlpBreakWithStatusInstruction
fffff880`04b98e30 fffff800`029cfb7e : 00000000`00000003 00000000`00000000 fffff800`029239d0 00000000`00000034 : nt!KiBugCheckDebugBreak+0x12
fffff880`04b98e90 fffff800`028e7744 : fffff880`04b995c8 00000000`00000000 fffffa80`0232bd60 00000000`00001000 : nt!KeBugCheck2+0x71e
fffff880`04b99560 fffff800`02c3e2af : 00000000`00000034 00000000`000001f4 ffffffff`c0000420 00000000`00000000 : nt!KeBugCheckEx+0x104
fffff880`04b995a0 fffff880`012c0f08 : fffff880`00000000 00000000`00001000 fffffa80`0232bd60 fffffa80`00001001 : nt! ?? ::NNGAKEGL::`string'+0x4fb24
fffff880`04b99660 fffff880`01165098 : fffffa80`00d7a320 fffffa80`0232bcf8 fffffa80`00c00640 fffffa80`00d7a301 : Ntfs!NtfsCopyReadA+0x1a8
fffff880`04b99840 fffff880`011688ba : fffff880`04b99910 00000000`0399c103 00000000`0399c100 fffffa80`00d7a300 : fltmgr!FltpPerformFastIoCall+0x88
fffff880`04b998a0 fffff880`01186630 : fffffa80`00d7a320 00000000`00000000 fffff880`04b99a00 00000000`00001000 : fltmgr!FltpPassThroughFastIo+0xda
fffff880`04b998e0 fffff800`02bd0e99 : fffffa80`00d7a320 fffff880`00000001 fffffa80`00cfd080 fffffa80`00d7a320 : fltmgr!FltpFastIoRead+0x1d0
fffff880`04b99980 fffff800`028e68d3 : 00000000`0000053c 00000000`00000000 00000000`00000000 00000000`00000000 : nt!NtReadFile+0x417
fffff880`04b99a70 00000000`774c137a : 000007fe`fd471aca 00000000`00000000 00000002`00000003 00000001`01d88f00 : nt!KiSystemServiceCopyEnd+0x13
00000000`04fcd458 000007fe`fd471aca : 00000000`00000000 00000002`00000003 00000001`01d88f00 00000000`00000005 : ntdll!NtReadFile+0xa
00000000`04fcd460 00000000`77261559 : 00000000`00000000 00000000`0000053c 00000000`00000000 00000000`04fcd620 : KERNELBASE!ReadFile+0x76
00000000`04fcd4e0 00000000`74a005d9 : 00000000`00023000 00000000`04fcd620 00000000`0399c130 00000000`00001000 : kernel32!ReadFileImplementation+0x55
00000000`04fcd520 00000000`00023000 : 00000000`04fcd620 00000000`0399c130 00000000`00001000 00000000`00000000 : mpengine+0x605d9
00000000`04fcd528 00000000`04fcd620 : 00000000`0399c130 00000000`00001000 00000000`00000000 00000000`74a00628 : 0x23000
00000000`04fcd530 00000000`0399c130 : 00000000`00001000 00000000`00000000 00000000`74a00628 00000000`01d7c340 : 0x4fcd620
00000000`04fcd538 00000000`00001000 : 00000000`00000000 00000000`74a00628 00000000`01d7c340 00000000`749c9be1 : 0x399c130
00000000`04fcd540 00000000`00000000 : 00000000`74a00628 00000000`01d7c340 00000000`749c9be1 00000000`00000000 : 0x1000

Explanation

The traditional definition of a DriverEntry function is as follows:

NTSTATUS DriverEntry(
  _In_  struct _DRIVER_OBJECT *DriverObject,
  _In_  PUNICODE_STRING RegistryPath
)

Under normal circumstances, the second parameter contains a valid pointer into a string describing the driver’s registry path. For diskdump.sys, however, the semantics of the parameter are different; the kernel seems to use it as some kind of a shared structure, both read from and written to by diskdump.sys. In fact, one of the first operations executed by the routine is the following call:

memset(*(void **)(RegistryPath + 8), 0, 0x10000u);

That’s right – the driver ends up zeroing-out 65,536 bytes of the kernel pool area. After that happens, it doesn’t take more than a few seconds (usually far less) to see the screen being filled with blue pixels. Very difficult in reliable exploitation, but perhaps possible with the right degree of kernel pool massaging.

Dumpata.sys

CONTEXT:  fffff88003181e50 -- (.cxr 0xfffff88003181e50)
rax=fffff880035a93f8 rbx=fffffa8000d4e000 rcx=fffffa8000d4e010
rdx=fffffa8000d4e000 rsi=0069007600720065 rdi=fffffa8000d4e010
rip=fffff880035a905a rsp=fffff88003182830 rbp=0000000000000000
r8=0000000000008000  r9=fffffa80022e6be0 r10=0000000000000000
r11=fffff80002809000 r12=0000000000000001 r13=ffffffff80000828
r14=fffffa800100a700 r15=000000000000001c
iopl=0         nv up ei ng nz na po nc
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010286
Dumpata!IdeDumpPortInitialize+0x52:
fffff880`035a905a 8b4608          mov     eax,dword ptr [rsi+8] ds:002b:00690076`0072006d=????????

[...]

STACK_TEXT:  
fffff880`03182830 fffff800`02c6e467 : fffffa80`022e6be0 fffffa80`00d4e000 00000000`00000000 00000000`000007ff : Dumpata!IdeDumpPortInitialize+0x52
fffff880`03182860 fffff800`02c6e865 : fffffa80`011fb900 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0xa07
fffff880`03182b30 fffff800`02893a21 : fffff800`00000000 ffffffff`80000828 fffff800`02c6e810 fffffa80`00cd5680 : nt!IopLoadUnloadDriver+0x55
fffff880`03182b70 fffff800`02b26cce : 01000501`00050100 fffffa80`00cd5680 00000000`00000080 fffffa80`00cba040 : nt!ExpWorkerThread+0x111
fffff880`03182c00 fffff800`0287afe6 : fffff800`029fbe80 fffffa80`00cd5680 fffffa80`00cd5b60 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
fffff880`03182c40 00000000`00000000 : fffff880`03183000 fffff880`0317d000 fffff880`03181280 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

Similarly to Diskdump.sys, the Dumpata.sys driver appears to interpret the second parameter of DriverEntry differently from its typical meaning. This time, an internal IdeDumpPortInitialize function treats some of unicode string’s bytes as a virtual memory address and tries to access it… with no chance to succeed.

dxg.sys

TRAP_FRAME:  fffff8800316d3e0 -- (.trap 0xfffff8800316d3e0)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=fffff8800316d500 rbx=0000000000000000 rcx=0000000000005a4d
rdx=fffff96000050000 rsi=0000000000000000 rdi=0000000000000000
rip=fffff800028aa8ef rsp=fffff8800316d578 rbp=fffff8800316d670
r8=0000000000000000  r9=fffff8800316d5d0 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz na pe nc
nt!RtlImageNtHeaderEx+0x3f:
fffff800`028aa8ef 66390a          cmp     word ptr [rdx],cx ds:fffff960`00050000=????
Resetting default scope

LAST_CONTROL_TRANSFER:  from fffff800029b0d92 to fffff800028c1490

STACK_TEXT:  
fffff880`0316cb08 fffff800`029b0d92 : fffff960`00050000 fffffa80`00cd3680 00000000`00000065 fffff800`02905178 : nt!RtlpBreakWithStatusInstruction
fffff880`0316cb10 fffff800`029b1b7e : fffff880`00000003 fffff880`0316d3e0 fffff800`029059d0 00000000`00000050 : nt!KiBugCheckDebugBreak+0x12
fffff880`0316cb70 fffff800`028c9744 : fffffa80`00cb9040 fffff800`028fb627 00000000`00000000 fffff680`00000568 : nt!KeBugCheck2+0x71e
fffff880`0316d240 fffff800`02873bf7 : 00000000`00000050 fffff960`00050000 00000000`00000000 fffff880`0316d3e0 : nt!KeBugCheckEx+0x104
fffff880`0316d280 fffff800`028c776e : 00000000`00000000 fffff960`00050000 fffffa80`02287200 fffff960`00050000 : nt! ?? ::FNODOBFM::`string'+0x44811
fffff880`0316d3e0 fffff800`028aa8ef : fffff800`02896159 00000000`00000000 00000000`000007ff 00000000`00000001 : nt!KiPageFault+0x16e
fffff880`0316d578 fffff800`02896159 : 00000000`00000000 00000000`000007ff 00000000`00000001 fffff880`0316d6d8 : nt!RtlImageNtHeaderEx+0x3f
fffff880`0316d580 fffff800`02895993 : 00000000`00000000 00000000`00000000 fffff880`035960a4 fffffa80`01c38940 : nt!RtlpImageDirectoryEntryToDataEx+0x59
fffff880`0316d5d0 fffff800`02c9ebcc : fffff880`0316d680 00000000`00000000 fffff800`02d80101 fffff880`00000014 : nt!RtlImageDirectoryEntryToData+0x13
fffff880`0316d620 fffff800`02cab7f8 : fffff880`0357b000 fffff880`0316d7f8 00000000`00000000 fffff880`0316d7d8 : nt!MiResolveImageReferences+0x4ec
fffff880`0316d740 fffff800`02cadead : fffff880`0316d8b8 00000000`00000000 00000000`00000000 00000000`00000000 : nt!MmLoadSystemImage+0x828
fffff880`0316d860 fffff800`02cae865 : fffffa80`011dca00 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0x44d
fffff880`0316db30 fffff800`028d3a21 : fffff800`00000000 ffffffff`80000830 fffff800`02cae810 fffffa80`00cd3680 : nt!IopLoadUnloadDriver+0x55
fffff880`0316db70 fffff800`02b66cce : 223b4724`3d49253e fffffa80`00cd3680 00000000`00000080 fffffa80`00cb9040 : nt!ExpWorkerThread+0x111
fffff880`0316dc00 fffff800`028bafe6 : fffff800`02a3be80 fffffa80`00cd3680 fffffa80`00cd3b60 170a0e1a`0b101c0c : nt!PspSystemThreadStartup+0x5a
fffff880`0316dc40 00000000`00000000 : fffff880`0316e000 fffff880`03168000 fffff880`0316d8a0 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

The dxg.sys executable imports several functions from the primary Windows graphical driver – win32k.sys. As the latter image resides in so called session memory which is only mapped in threads marked as GUI and the process of resolving dxg.sys dependencies takes place in a non-GUI System process, a fatal exception is raised upon trying to access the base address of the win32k.sys image.

dxgkrnl.sys

TRAP_FRAME:  fffff88002f665a0 -- (.trap 0xfffff88002f665a0)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=fffff88002f66768 rbx=0000000000000000 rcx=0000000000000008
rdx=fffffa8001263f01 rsi=0000000000000000 rdi=0000000000000000
rip=fffff800028f27ec rsp=fffff88002f66730 rbp=0000000000000001
r8=0000000000008155  r9=0000000000000003 r10=0000000000000000
r11=fffff880014d3120 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei ng nz na po nc
nt!KiCancelTimer+0x28:
fffff800`028f27ec f00fba2b07      lock bts dword ptr [rbx],7 ds:00000000`00000000=????????
Resetting default scope

LAST_CONTROL_TRANSFER:  from fffff800029cfd92 to fffff800028e0490

STACK_TEXT:  
fffff880`02f65ce8 fffff800`029cfd92 : 00000000`00000008 fffffa80`00cd4b60 00000000`00000065 fffff800`02924178 : nt!RtlpBreakWithStatusInstruction
fffff880`02f65cf0 fffff800`029d0b7e : 00000000`00000003 00000000`00000000 fffff800`029249d0 00000000`0000000a : nt!KiBugCheckDebugBreak+0x12
fffff880`02f65d50 fffff800`028e8744 : 00000000`00000000 fffff880`00961000 00000000`00000000 fffff800`02892790 : nt!KeBugCheck2+0x71e
fffff880`02f66420 fffff800`028e7be9 : 00000000`0000000a 00000000`00000008 00000000`00000002 00000000`00000001 : nt!KeBugCheckEx+0x104
fffff880`02f66460 fffff800`028e6860 : fffff6fb`7e200138 fffff880`02f66840 00000000`00000000 00000000`00000008 : nt!KiBugCheckDispatch+0x69
fffff880`02f665a0 fffff800`028f27ec : 00000000`0000001c fffffa80`0223e0d0 ffffffff`80000824 00000000`00000001 : nt!KiPageFault+0x260
fffff880`02f66730 fffff800`028c355e : 00000000`00000000 00000000`00000000 fffff880`04e7ed00 00000000`00000003 : nt!KiCancelTimer+0x28
fffff880`02f66770 fffff880`04ed8542 : 00000000`00000010 00000000`00000000 00000000`00000000 fffff880`04e7ed00 : nt!KeCancelTimer+0x1a
fffff880`02f667a0 fffff880`04f552f0 : fffffa80`fffffcf7 ffffffff`c0000035 00000000`00000000 fffff880`04e7ed00 : dxgkrnl_fffff88004e67000!DxgkSqmShutdown+0x52
fffff880`02f667e0 fffff800`02ccd467 : fffffa80`00d29e70 00000000`00000000 00000000`ffffff57 fffffa80`02414000 : dxgkrnl_fffff88004e67000!DriverEntry+0x2e8
fffff880`02f66860 fffff800`02ccd865 : fffffa80`010358a0 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0xa07
fffff880`02f66b30 fffff800`028f2a21 : fffff800`00000000 ffffffff`80000824 fffff800`02ccd810 fffffa80`00cd4b60 : nt!IopLoadUnloadDriver+0x55
fffff880`02f66b70 fffff800`02b85cce : 44224346`23464623 fffffa80`00cd4b60 00000000`00000080 fffffa80`00cba040 : nt!ExpWorkerThread+0x111
fffff880`02f66c00 fffff800`028d9fe6 : fffff800`02a5ae80 fffffa80`00cd4b60 fffffa80`00cd4040 0f140a0f`150b1117 : nt!PspSystemThreadStartup+0x5a
fffff880`02f66c40 00000000`00000000 : fffff880`02f67000 fffff880`02f61000 fffff880`02f660a0 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

Although a NULL Pointer Dereference, this case is particularly funny. The affected part of DriverEntry works in the following way:

if (failed(WdmlibIoCreateDeviceSecure(...))) {
  DxgkSqmShutdown();
}
[...]
DxgkSqmInit();

Both routines operate on a shared structure pointed to by a global dxgkrnl!pDxgkSqmControl pointer. The pointer is initialized for the first time in the DxgkSqmInit function, therefore it is still equal to NULL at the time of calling DxgkSqmShutdown. The deinitialization proc doesn’t expect pDxgkSqmControl to be zero and simply starts destroying and cleaning up particular fields within the structure, which results in an unhandled exception at the first attempt of using the pointer in a memory operation.

ndis.sys

TRAP_FRAME:  fffff88003174280 -- (.trap 0xfffff88003174280)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=0000000000000000 rbx=0000000000000000 rcx=fffff88004d27a80
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=fffff88004ccdbbd rsp=fffff88003174410 rbp=fffff88003174500
r8=0000000000000000  r9=0000000000000000 r10=fffff80002a0f820
r11=fffffa8002565af0 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl nz ac po cy
ndis_fffff88004cc1000!ndisFindFreeRefCountPool+0x1d:
fffff880`04ccdbbd 837818ff        cmp     dword ptr [rax+18h],0FFFFFFFFh ds:0080:0018=????????
Resetting default scope

LAST_CONTROL_TRANSFER:  from fffff80002983d92 to fffff80002894490

STACK_TEXT:  
fffff880`031739c8 fffff800`02983d92 : 00000000`00000018 fffffa80`00cd4040 00000000`00000065 fffff800`028d8178 : nt!RtlpBreakWithStatusInstruction
fffff880`031739d0 fffff800`02984b7e : 00000000`00000003 00000000`00000000 fffff800`028d89d0 00000000`000000d1 : nt!KiBugCheckDebugBreak+0x12
fffff880`03173a30 fffff800`0289c744 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff800`02846790 : nt!KeBugCheck2+0x71e
fffff880`03174100 fffff800`0289bbe9 : 00000000`0000000a 00000000`00000018 00000000`00000002 00000000`00000000 : nt!KeBugCheckEx+0x104
fffff880`03174140 fffff800`0289a860 : 00000000`00000000 00000000`00000000 00000000`00000000 fffff880`04d27a88 : nt!KiBugCheckDispatch+0x69
fffff880`03174280 fffff880`04ccdbbd : 00000000`00000000 2020444e`000000b0 00000000`00000000 fffffa80`01be48d0 : nt!KiPageFault+0x260
fffff880`03174410 fffff880`04ccdae3 : fffff880`04d259a0 00000000`00000000 00000000`00000028 fffffa80`01be48d0 : ndis_fffff88004cc1000!ndisFindFreeRefCountPool+0x1d
fffff880`03174440 fffff880`04ccda9b : 00000000`00000010 00000000`00000000 00000000`00000000 00000000`000007ff : ndis_fffff88004cc1000!ndisAllocateRWLRefCounts+0x23
fffff880`03174480 fffff880`04d9bc49 : 00000000`00000000 00000000`00000001 fffff880`00000000 00000000`000007ff : ndis_fffff88004cc1000!NdisAllocateRWLock+0x5b
fffff880`031744b0 fffff880`04d9cd10 : fffffa80`0118d5b0 00000000`646e444e 00000000`00000020 00000000`00000001 : ndis_fffff88004cc1000!ndisInitializePeriodicReceives+0x1c9
fffff880`03174560 fffff800`02c81467 : fffffa80`0118d5b0 fffffa80`0118d5b0 fffffa80`0118d5b0 00000000`000007ff : ndis_fffff88004cc1000!DriverEntry+0x610
fffff880`03174860 fffff800`02c81865 : fffffa80`013142a0 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0xa07
fffff880`03174b30 fffff800`028a6a21 : fffff800`00000000 ffffffff`80000804 fffff800`02c81810 fffffa80`00cd4040 : nt!IopLoadUnloadDriver+0x55
fffff880`03174b70 fffff800`02b39cce : 00000000`00000000 fffffa80`00cd4040 00000000`00000080 fffffa80`00cb9040 : nt!ExpWorkerThread+0x111
fffff880`03174c00 fffff800`0288dfe6 : fffff800`02a0ee80 fffffa80`00cd4040 fffffa80`00cd3680 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
fffff880`03174c40 00000000`00000000 : fffff880`03175000 fffff880`0316f000 fffff880`03173280 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

The root cause of this NULL pointer dereference system crash is not clear; the case requires further investigation.

wimmount.sys

CONTEXT:  fffff88003181d90 -- (.cxr 0xfffff88003181d90)
rax=0000000000000001 rbx=0000000000000000 rcx=fffff880035b9150
rdx=0000000000000000 rsi=fffff880035b9140 rdi=0000000000000000
rip=fffff880035b7623 rsp=fffff88003182770 rbp=0000000000000000
r8=0000000000005e60  r9=0000000000000030 r10=0000000000000000
r11=fffff88003182750 r12=0000000000000001 r13=ffffffff800007e0
r14=fffffa80016690e0 r15=000000000000001c
iopl=0         nv up ei pl nz na po cy
cs=0010  ss=0018  ds=002b  es=002b  fs=0053  gs=002b             efl=00010207
wimmount!WMCommListDestroy+0x1f:
fffff880`035b7623 488b0b          mov     rcx,qword ptr [rbx] ds:002b:00000000`00000000=????????????????
Resetting default scope

[...]

STACK_TEXT:  
fffff880`03182770 fffff880`035bb0cc : 00000000`c0000034 00000000`00010246 fffff880`031827c8 00000000`00000018 : wimmount!WMCommListDestroy+0x1f
fffff880`031827a0 fffff880`035bc179 : fffffa80`01217470 fffffa80`026b8000 fffff880`035b9128 fffff800`02871adf : wimmount!Unload+0x48
fffff880`031827d0 fffff800`02c72467 : fffffa80`01217470 fffffa80`01217470 00000000`00000000 00000000`000007ff : wimmount!DriverEntry+0x171
fffff880`03182860 fffff800`02c72865 : fffffa80`010ff278 00000000`00000000 00000000`00000001 00000000`00000001 : nt!IopLoadDriver+0xa07
fffff880`03182b30 fffff800`02897a21 : fffff880`00000000 ffffffff`800007e0 fffff800`02c72810 fffffa80`00cd2680 : nt!IopLoadUnloadDriver+0x55
fffff880`03182b70 fffff800`02b2acce : 01000501`00050100 fffffa80`00cd2680 00000000`00000080 fffffa80`00cb7040 : nt!ExpWorkerThread+0x111
fffff880`03182c00 fffff800`0287efe6 : fffff800`029ffe80 fffffa80`00cd2680 fffffa80`00cd2b60 00000000`00000000 : nt!PspSystemThreadStartup+0x5a
fffff880`03182c40 00000000`00000000 : fffff880`03183000 fffff880`0317d000 fffff880`03180da0 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

Yet another failed attempt to recover from an error in DriverEntry. In case one of the external functions called there (e.g. FltRegisterFilter or FltCreateCommunicationPort) fails, the Unload routine is called. Unfortunately, it doesn’t care to verify which portions of the internal driver state have been successfully initialized before, and just destroys everything it knows of, eventually leading to usage of an unitialized NULL pointer.

rdyboost.sys (Windows 8 64-bit only)

TRAP_FRAME:  fffff8800c956a70 -- (.trap 0xfffff8800c956a70)
NOTE: The trap frame does not contain all registers.
Some register values may be zeroed or incorrect.
rax=0000000000000003 rbx=0000000000000000 rcx=fffff8800d7dd8a0
rdx=fffff8800d7dd8a0 rsi=0000000000000000 rdi=0000000000000000
rip=fffff8800d7e1650 rsp=fffff8800c956c08 rbp=0000000000000080
r8=fffffa800ccb4440  r9=0000000000000000 r10=0000000000000009
r11=fffffa800dbfbd58 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
iopl=0         nv up ei pl zr na po nc
+0x2a650:
fffff880`0d7e1650 ??              ???
Resetting default scope

[...]

STACK_TEXT:  
fffff880`0c9560c8 fffff803`a05f8c3a : 00000000`00000000 00000000`00000050 fffff880`0c956230 fffff803`a0548a68 : nt!DbgBreakPointWithStatus
fffff880`0c9560d0 fffff803`a05f828e : 00000000`00000003 fffff880`0c956230 fffff803`a05462e0 fffff880`0c956780 : nt!KiBugCheckDebugBreak+0x12
fffff880`0c956130 fffff803`a04d1444 : fffffa80`0dbfbc00 fffffa80`100a0b00 ffff61f1`669054af fffff880`0c956b00 : nt!KeBugCheck2+0x79f
fffff880`0c956850 fffff803`a066edc2 : 00000000`00000050 fffff880`0d7e1650 00000000`00000008 fffff880`0c956a70 : nt!KeBugCheckEx+0x104
fffff880`0c956890 fffff803`a04fd31c : 00000000`00000008 fffff880`0d7e1650 fffffa80`10095b00 fffff880`0d7e1650 : nt! ?? ::FNODOBFM::`string'+0x32068
fffff880`0c956930 fffff803`a04cedee : 00000000`00000008 fffffa80`10095b00 fffff880`0c956b00 fffff880`0c956a70 : nt!MmAccessFault+0x59c
fffff880`0c956a70 fffff880`0d7e1650 : fffff803`a04b3ec9 fffff803`a0778180 fffff803`a04f6f15 00000000`00000000 : nt!KiPageFault+0x16e
fffff880`0c956c08 fffff803`a04b3ec9 : fffff803`a0778180 fffff803`a04f6f15 00000000`00000000 00000000`976e7c63 : +0x2a650
fffff880`0c956c10 fffff803`a04b6386 : fffff803`a0778180 fffffa80`10095b00 fffffa80`101d16c0 fffffa80`0ccb4440 : nt!PspSystemThreadStartup+0x59
fffff880`0c956c60 00000000`00000000 : fffff880`0c957000 fffff880`0c951000 00000000`00000000 00000000`00000000 : nt!KxStartSystemThread+0x16

Explanation

At startup, rdyboost.sys creates a new kernel thread in the system starting at the SmdRBMemoryWatchdogThread routine. The driver fails to properly terminate the thread during the DriverUnload clean-up procedure, consequently leaving an actively running thread within a memory area that has been unmapped by the kernel in the final stage of unloading the driver. As soon as the scheduler gets back to the dangling thread, an exception is raised due to an attempt to execute unmapped memory, as shown in the above listing.

13 thoughts on “Defeating Windows Driver Signature Enforcement #1: default drivers”

  1. IMHO:

    DSE is misunderstood as a security feature. It is not meant to introduce a security boundary. It is target at making the kernel a more stable system by mostly controlling what goes into it. It prevents the random app developer from writing a driver if there is no very good reason for doing it that way. It also creates a mostly working registry of all drivers because all drivers have to obtain a certificate. I think it also mostly forces a quality check to be taken that is conducted by Microsoft.

    I think Microsoft is kind of OK with DSE being broken if this happens rarely and if this has no consequences for mainstream software development.

    And in the 1st paragraph I say “mostly” because the occasional exploit exists but doesn’t matter in the big scheme of things.

  2. @tobi: I totally agree. The best argument for that seems to be the way Microsoft treats these occasional exploits – they’re usually just regarded as stability issues and are not assigned security bulletins or have tight fix deadlines. Plus, it’s just infeasible to ensure high quality of every driver being digitally signed – as a few people have pointed out on Twitter, the cheapest way to bypass the enforcement might be to install a known-vulnerable AV device driver and exploit it to propagate further. Therefore, the model can’t be possibly described as a “security feature”.

  3. Hello j00ru i am your great fan you are exploit king. i want to know if a noob like me want to learn exploit developement or finding vulnerblities in windows kernel,where should we start,i have a windows internals book and rootkit arsenal book beside that what other books or forums you suggest me.please help a noob and God will help you

Comments are closed.