The remaining boot drivers are loaded and initialized
SYMANTEC ADVANCED THREAT RESEARCH 1
Assessment of Windows Vista Kernel-Mode Security
• Driver signing [1]
• PatchGuard [2]
A. What’s Covered
This paper examines the new security features that have been incorporated to keep malicious code from compromising the kernel. Since most of these features are only available in the 64-bit edition of Windows Vista, this paper will focus on the 64-bit edition.
A. | SYMANTEC ADVANCED THREAT RESEARCH | 2 | |
---|---|---|---|
II. |
|
||
|
A typical BCD entry for the Boot Manager looks like this:
Windows Boot Manager
Locale: en-US
Inherit options: {globalsettings}
Display order: {current}
Timeout: 30
The bootmgr calls the Windows Vista OS Loader, which is located under %SystemRoot%\System32\WINLOAD.EXE. WINLOAD.EXE replaces NTLDR (the legacy Windows NT OS loader). For the remainder of this section, “it” refers to the instructions in WINLOAD.EXE beginning at the entry point (OslMain).
A typical BCD entry for the Windows Vista OS Loader looks like this:
SYMANTEC ADVANCED THREAT RESEARCH 3
Path: \Windows\system32\WINLOAD.EXE
Description: Microsoft Windows
Locale: en-US
Inherit options: {bootloadersettings}
Boot debugger: No
Pre-boot EMS Enabled: No
Advanced options: No
Options editor: No
Windows device: partition=C:
Windows root: \Windows
Resume application: {3ced334e-a0a5-11da-8c2b-cbb6baaeea6d} No Execute policy: OptIn
Detect HAL: No
No integrity checks: No
Disable boot display: No
Boot processor only: No
Firmware PCI settings: No
Log initialization: No
OS boot information: No
Kernel debugger: No
HAL breakpoint: No
EMS enabled in OS: No
For Full Volume Encryption (_FvebDeviceFunctionTable) these
are:
dd 0 ; FVE has no EnumerateDeviceClass callback
dd offset _FvebOpen@8 ; FvebOpen(x,x)
dd offset _FvebClose@4 ; FvebClose(x)
dd offset _FvebRead@16 ; FvebRead(x,x,x,x)
dd offset _FvebWrite@16 ; FvebWrite(x,x,x,x)
dd offset _FvebGetInformation@8 ; FvebGetInformation(x,x) dd offset
_FvebSetInformation@8 ; FvebSetInformation(x,x) dd offset _FvebReset@4 ;
FvebReset(x)
For block I/O (_BlockIoDeviceFunctionTable) these are:
dd offset _BlockIoEnumerateDeviceClass@12 ;
BlockIoEnumerateDeviceClass(x,x,x) dd offset _BlockIoOpen@8 ;
BlockIoOpen(x, x)
dd offset _BlockIoClose@4 ; BlockIoClose(x)
dd offset _BlockIoReadUsingCache@16 ;
BlockIoReadUsingCache(x,x,x,x)
dd offset _BlockIoWrite@16 ; BlockIoWrite(x,x,x,x)
dd offset _BlockIoGetInformation@8 ; BlockIoGetInformation(x,x)
dd offset _BlockIoSetInformation@8 ; BlockIoSetInformation(x,x)
dd offset ?handleInputChar@OsxmlMeter@@UAEHG@Z ;
OsxmlMeter::handleInputChar(ushort) dd offset _BlockIoCreate@12 ;
BlockIoCreate(x,x,x)
For PXE (_UdpFunctionTable):
dd offset _UdpEnumerateDeviceClass@12 ; UdpEnumerateDeviceClass(x,x,x)
dd offset _UdpOpen@8 ; UdpOpen(x,x)
dd offset _SpClose@4 ; SpClose(x)
dd offset _UdpRead@16 ; UdpRead(x,x,x,x)
dd offset _UdpWrite@16 ; UdpWrite(x,x,x,x)
dd offset _UdpGetInformation@8 ; UdpGetInformation(x,x)
dd offset _UdpSetInformation@8 ; UdpSetInformation(x,x)
dd offset _UdpReset@4 ; UdpReset(x)
You’ll notice that some of the function callbacks are shared between different classes (e.g., serial port and PXE).
Next it discovers the system disks (OslEnumerateDisks) and loads the system hive HKEY_LOCAL_MACHINE (OslpLoadSystemHive). After the system hive is loaded, we encounter the first code integrity check point in the Windows Vista boot process (OslInitializeCodeIntegrity). First it calls MincrypL_SelfTest, which validates the SHA1 hashing and PKCS1 signature verification algorithms are working (using a pre-defined test case). If the pre-defined test case fails, it returns error code 0xC0000428. Next, it checks if a debugger is enabled (BlBdDebuggerEnabled). If there is a debugger enabled, it calls KnownAnswerTest; otherwise, it skips the test.
Next it loads the OS signed catalog from %SystemRoot%\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\nt5.cat in BlImgRegisterCodeIntegrityCatalogs (internally this calls the MinCrypL_AddCatalog API function).
If the signature doesn’t match but a debugger is enabled (BlBdDebuggerEnabled returns TRUE) then it will print the message:
*** Windows is unable to verify the signature of the file %s.
OslLoadImage calls GetImageValidationFlags to check the filename against a pre-defined list of boot drivers in LoadBootImagesTable. If integrity checks are enabled, then boot drivers must be signed by a trusted root authority and all the image hashes must match the signed catalog file unless a debugger is enabled. If a debugger is enabled, WINLOAD.EXE does not enforce this requirement. Instead it will print an error message to the debugger, but will otherwise ignore the code integrity check failure. However, the following boot drivers (also listed in Appendix A) must pass the code integrity checks even if a debugger is enabled (otherwise WINLOAD.EXE will refuse to boot Windows Vista):
\Windows\system32\bootvid.dll
\Windows\system32\pshed.dll
\Windows\system32\WINLOAD.EXE
\Windows\system32\NTOSKRNL.exe
\Windows\system32\HAL.dll
1.Displays the progress bar seen during bootup
2.Loads %SystemRoot%\AppPatch\drvmain.sdb (the application compatability database)
3.Loads %SystemRoot%\System32\acpitabl.dat
4.Loads an INF file pointed to by HKEY_LOCAL_MACHINE\CurrentControlSet\Control\Errata\InfName if present in the registry.
After OslpLoadAllModules has finished, OslMain saves the boot log (OslpLogSaveInformation), finishes the Full Volume Encryption loading if enabled (BlFveSecureBootRestrictToOne and BlTpmShutdown), and finally calls OslArchTransferToKernel to transfer control to NTOSKRNL.EXE.
WINLOAD.EXE was responsible for checking the integrity of the signatures of boot drivers. NTOSKRNL.EXE, in contrast, is responsible for the verification of system drivers (loaded after boot drivers) and drivers loaded at runtime (i.e., by the user or a device being inserted into the system). When integrity checks are enabled, the code integrity of the loaded image is checked SeValidateImageHeader (a wrapper to CiValidateImageHeader in CI.DLL) and SeValidateImageData (a wrapper to CiValidateImageData in CI.DLL). SeValidateImageHeader is called whenever an executable is mapped into kernel memory (via MmCreateSection). The code sections of kernel drivers are verified in SeValidateImageData which is called when a kernel module is being loaded. Runtime checks (e.g., continuously polling for modifications to the code sections of kernel drivers) are handled by PatchGuard and CI.DLL—discussed later in this paper.
A. |
|
III. |
---|
B. Implementation
Windows Vista not only requires the driver be signed, but it also requires it be signed by one of eight (as of Build 5365) trusted root authority certificates. The driver signing checks for loading boot drivers is handled by WINLOAD.EXE, whereas signing checks for all other drivers is handled by NTOSKRNL.EXE (which incidentally uses CI.DLL to do the actual checks).
SYMANTEC ADVANCED THREAT RESEARCH 8
Windows Vista build 5365 has eight trusted root authority certificates embedded in its code:
|
|
---|---|
0 | |
1 | |
2 | |
3 | |
4 | |
5 |
|
6 |
|
7 | |
HKEY_LOCAL_MACHINE\Software\Microsoft\EnterpriseCertificates has no effect on driver signing. |
If it is signed by a trusted root certificate authority, then the final step is to verify the actual signature—this is done by MinCryptVerifySignedHash. MinCryptVerifySignedHash performs the following sequence:
3) MinCrypK_FindPageHashesInCatalog
SYMANTEC ADVANCED THREAT RESEARCH 9
CRITICAL_STRUCTURE_CORRUPTION (109)
This bugcheck is generated when the kernel detects that critical kernel code or data have been corrupted. There are generally three causes for a corruption:
1) A driver has inadvertently or deliberately modified critical kernel code or data. See http://www.microsoft.com/whdc/driver/kernel/64bitPatching.mspx
2) A developer attempted to set a normal kernel breakpoint using a kernel debugger that was not attached when the system was booted. Normal breakpoints, "bp", can only be set if the debugger is attached at boot time. Hardware breakpoints, "ba", can be set at any time. 3) A hardware corruption occurred, e.g. failing RAM holding kernel code or data.Type of corrupted region, can be
0 : A generic data region
1 : Modification of a function or .pdata
2 : A processor IDT
3 : A processor GDT
4 : Type 1 process list corruption
5 : Type 2 process list corruption
6 : Debug routine modification
7 : Critical MSR modification
This is an enhanced version of an idea proposed in [6]. The background is that PatchGuard must register a timer event that will trigger the next scan (PatchGuard scans for changes in random intervals). These entries are represented using the KTIMER structure. In [6], the authors proposed walking the list of registered timer events to find an entry with an invalid DeferredContext. The PatchGuard entry is easy to detect because all other entries will have a valid DeferredContext pointer in the KTIMER structure.
The problem is that this list, pointed to by the KiTimerListHeadvariable is exported. The authors of [6] did not provide any good mechanism to discover this variable in memory and instead proposed some rudimentary heuristics. We propose a more reliable method to find the KiTimerListHead variable. After locating the KiTimerListHeadvariable, we traverse the linked list and locate all time entries that do not have a valid DeferredContext pointer. We cannot find the location of PatchGuard code in memory from the DeferredContext pointer because it is encoded (using unknown random numbers). Instead, we can remove the entries to disable PatchGuard. This is a partial implementation in C utilizing this technique:
// Likewise, we will be at the head of the list because there can't be anything smaller.
// If KeSetTimer returns FALSE, then the timer already expired
// So just use the smallest unit possible and we be at KiTimerListHead[0].Flink DueTime.QuadTime = -1;
First, add a memory read breakpoint (using the Intel debug registers) on IDT entry 1. Second, add an interrupt 3 (breakpoint exception) handler. PatchGuard will scan the IDT sequentially from the first entry to the last, so PatchGuard thread will trigger the memory read breakpoint. A custom interrupt handler will be installed to handle breakpoint exceptions.
Third, wait for the memory read breakpoint exception to call the special interrupt handler we’ve installed. If the interrupt is not a memory read breakpoint on the first IDT entry, then this exception will be passed to the original interrupt 3 handler. Otherwise, the faulting instruction pointer is the PatchGuard thread and steps can be taken to disable PatchGuard (such as overwriting the PatchGuard code page with NOPs). This approach is likely to be effective at detecting PatchGuard since it detects a basic behavior of any integrity-checking memory scanning algorithm.
The author previously created proof-of-concept tool that utilized \Device\PhysicalMemory to detect BIOS rootkits.
VI. CODE INTEGRITY
boot loader checks the integrity of the kernel, the Hardware Abstraction Layer (HAL), and the boot-start drivers. After these
binaries have been verified, the system starts and the memory manager will call CI to verify any binaries that are loaded into
1. CI.DLL is disabled when integrity checks are disabled, PatchGuard is always enabled.
2.They serve different purposes. PatchGuard is made by the Windows Core team and is meant to prevent OS hooking.
\nt\multimedia\mf\core\grlreader\kmode\daytona\objchk\amd64\grlreaderk.obj
\nt\multimedia\lib\amd64\GrlReaderK.lib
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\peauthkrnl.obj
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\peauthkrnlcomm.obj
CI.DLL and PEAUTH.SYS are tightly coupled and are both core components of the “Protected Media Path” (functionality provided by the Microsoft DRM) described on Slides 6-14 in [13]. In effect, the Microsoft DRM is hard-coded into Windows Vista. Making CI.DLL an import DLL of NTOSKRNL.EXE must have been a strategic decision by Microsoft to prevent tampering with the kernel load sequence, as it has the additional implication of tightly integrating the DRM capabilities into Windows Vista:
• In Windows, imported DLLs are initialized before the main executable. If any of the DLLs fail to load (that is, the DLL’s DllMain returns FALSE), then the main executable will not load. So in effect, this means that if the Code Integrity DLL fails to load for whatever reason, NTOSKRNL will not load.
B. Implementation
CI.DLL exports CiInitialize which is called from NTOSKRNL.EXE (in SepInitializeCodeIntegrity). CiInitialize calls PESetInitialState to take a snapshot of the system state (that will serve as the baseline) and also creates a cache of catalog cache in %SystemRoot%\SystemRoot\System32\CatRoot\{F750E6C3-38EE-11D1-85E5-00C04FC295EE}\bootcat.cache.
A. | VII. |
|
---|
Windows Vista handles several network protocols in the kernel through the use of kernel mode drivers. If a vulnerability is discovered in one of these signed network drivers, the vulnerability could allow full machine compromise from a remote attacker. Such an attack is not precluded by Vista’s driver signing. Some examples of signed drivers handling network protocols in Windows Vista include:
The most straight forward way to evade driver signing restrictions is to simply patch the on-disk executable files and disable the checks entirely. To load unsigned drivers at runtime, NTOSKRNL.EXE needs to be patched. However, patching NTOSKRNL.EXE will invalidate its digital signature, so that WINLOAD.EXE will refuse to load it. Therefore, WINLOAD.EXE will also need to be patched.
One barrier that immediately becomes apparent when an attempt is made to patch these images is Windows Resource
We’ve discussed in this paper the numerous security enhancements Windows Vista has added to kernel-mode security. The Windows Vista kernel enhancements are aimed at preventing unsigned code from being injected into the kernel and to establish a chain-of-trust from the time that Vista boots until applications are run. In this paper, we have identified some limits to the effectiveness of Windows Vista’s new security capabilities:
1.It is possible to disable the driver signing and code integrity capabilities by using binary patches on WINLOAD.EXE and CI.DLL. Patching these files at runtime is quite straightforward; each file requires patching at just a single location. Though these files are protected by Windows Resource Protection (WRP), this can easily be evaded as we have demonstrated in [7].
SYMANTEC ADVANCED THREAT RESEARCH 14
REFERENCES
A. Boot Drivers That Must Pass Integrity Checks in winload.exe (even if a debugger is enabled)
If integrity checks are enabled, then boot drivers must be signed by a trusted root authority and all the image hashes must match the signed catalog file. However, if a debugger is enabled winload.exe does not enforce this requirement. Instead it will print an error message to the debugger, but will otherwise ignore the code integrity check failure. However, the following boot drivers must pass the code integrity checks even if a debugger is enabled (otherwise the boot process will fail):
\Windows\system32\bootvid.dll
\Windows\system32\ci.dll
\Windows\system32\clfs.sys
\Windows\system32\hal.dll
\Windows\system32\kdcom.dll
\Windows\system32\ntoskrnl.exe
\Windows\system32\pshed.dll
\Windows\system32\winload.exe
\Windows\system32\drivers\ksecdd.sys
\Windows\system32\drivers\spldr.sys
\Windows\system32\drivers\tpm.sys\SystemRoot\system32\DRIVERS\tdx.sys
\SystemRoot\system32\DRIVERS\smb.sys
\SystemRoot\system32\drivers\afd.sys
\SystemRoot\System32\DRIVERS\netbt.sys \SystemRoot\system32\DRIVERS\pacer.sys \SystemRoot\system32\DRIVERS\netbios.sys \SystemRoot\system32\drivers\smbali.sys \SystemRoot\system32\drivers\smbhc.sys \SystemRoot\system32\DRIVERS\wanarp.sys \SystemRoot\system32\DRIVERS\rdbss.sys \SystemRoot\system32\DRIVERS\nsiproxy.sys \SystemRoot\system32\drivers\csc.sys
\SystemRoot\System32\Drivers\dfsc.sys \SystemRoot\system32\DRIVERS\cdfs.sys \SystemRoot\system32\drivers\luafv.sys \SystemRoot\system32\DRIVERS\irda.sys \SystemRoot\system32\DRIVERS\nwifi.sys \SystemRoot\system32\DRIVERS\ndisuio.sys \SystemRoot\system32\DRIVERS\rspndr.sys \SystemRoot\System32\DRIVERS\srvnet.sys \SystemRoot\system32\DRIVERS\bowser.sys \SystemRoot\System32\drivers\mpsdrv.sys \SystemRoot\system32\drivers\mrxdav.sys \SystemRoot\system32\DRIVERS\mrxsmb.sys \SystemRoot\system32\DRIVERS\mrxsmb10.sys \SystemRoot\system32\DRIVERS\mrxsmb20.sys \SystemRoot\System32\DRIVERS\srv2.sys \SystemRoot\System32\DRIVERS\srv.sys
\SystemRoot\System32\DRIVERS\srv.sys
\SystemRoot\system32\DRIVERS\parvdm.sys \SystemRoot\system32\drivers\peauth.sys \SystemRoot\System32\Drivers\secdrv.SYS \SystemRoot\system32\DRIVERS\tcpipreg.sys \SystemRoot\system32\drivers\HTTP.sys \SystemRoot\system32\DRIVERS\usbccgp.sys \SystemRoot\system32\DRIVERS\USBSTOR.SYS \SystemRoot\system32\DRIVERS\hidusb.sys \SystemRoot\System32\Drivers\fastfat.SYS
|
---|
\nt\base\ci\dll\objchk\amd64\ci.exp
\nt\base\ci\dll\objchk\amd64\ci.obj
\nt\base\ci\dll\objchk\amd64\ci.res
\nt\base\ci\dll\objchk\amd64\cicert.obj
\nt\base\ci\dll\objchk\amd64\cifips.obj
\nt\base\ci\dll\objchk\amd64\ciimage.obj
\nt\base\ci\dll\objchk\amd64\cilog.obj
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\a ntidebugging.obj
\nt\base\ci\PEAuthKrnl\PEAuthKrnlLib\objchk\amd64\P EAuthKrnl.lib
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\p eauthkrnl.obj
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\p eauthkrnlcomm.obj
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\p eauthkrnlhashbuckets.obj
\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\p eauthkrnlhelper.obj\nt\base\ci\peauthkrnl\peauthkrnllib\objchk\amd64\p eauthkrnlloads.obj
\nt\base\ci\PEAuthKrnl\PEAuthStore\amd64fre\PEAuthS tore.lib
\nt\base\crts\crtw32\convert\nt\objchk\amd64\_strto l.obj
\nt\base\crts\crtw32\convert\nt\objchk\amd64\_strto q.obj
\nt\base\crts\crtw32\convert\nt\objchk\amd64\atox.o bj
\nt\base\crts\crtw32\hack\nt\objchk\amd64\stubs.obj \nt\base\crts\crtw32\misc\nt\objchk\amd64\bsearch.o bj
\nt\base\crts\crtw32\misc\nt\objchk\amd64\ctype.obj \nt\base\crts\crtw32\misc\nt\objchk\amd64\gshandler .obj
\nt\base\crts\crtw32\misc\nt\objchk\amd64\gshandler seh.obj
\nt\base\crts\crtw32\misc\nt\objchk\amd64\invargk.o bj
\nt\base\crts\crtw32\misc\nt\objchk\amd64\lconv.obj \nt\base\crts\crtw32\misc\nt\objchk\amd64\nlsdata2.