NT Internals

Pointers and Handles - A Story of Unchecked Assumptions in the Windows Kernel

Last month on Black Hat conference Alex Ionescu presented several vulnerabilities discovered in Win32k.sys. In first part of the presentation Alex discussed a couple of unchecked pointer dereferences. In the second part (most focused) he discussed about user-mode accessible handles, and incorrect usage of Nt/Zw APIs when dealing with user-mode data. Alex wrote that calling NtUserGetThreadState or NtUserGetDCEx from Client Server Runtime Process (csrss.exe) can cause immediately BSoD. But there is another one function that dereferences Win32ThreadInfo->Desktop - NtUserGetWindowDC.

I wrote three demonstration DoS exploits. One for NtUserGetDCEx, NtUserGetWindowDC and NtUserCloseWindowStation. In the last one I used different scenario then Alex Ionescu showed on his blog. Firstly we look for handle of WindowStation then set ProtectFromClose attribute and finally call NtUserCloseWindowStation.

Black Hat 2008 Wrap-up

NtUserGetDCEx_DoS.zip - Local Denial of Service Exploit
NtUserGetWindowDC_DoS.zip - Local Denial of Service Exploit
NtUserCloseWindowStation_DoS.zip - Local Denial of Service Exploit

Looking for vulnerabilities in different drivers I found a driver which allows any user to call ZwClose directly by supplying a specially crafted Irp to the IOCTL handler function.

	.text:00012572 @@ioctl_close_handle:
	.text:00012572                 cmp     dword ptr [eax+4], 210h        ; OutputBufferLength
	.text:00012579                 jb      @@invalid_buffer_size
	.text:0001257F                 mov     eax, [ebx+0Ch]                 ; SystemBuffer
	.text:00012582                 push    dword ptr [eax+208h]
	.text:00012588                 call    ds:ZwClose

DESlock+ - 3.2.7 (vdlptokn.sys) - Local Denial of Service Exploit

At the beginning of the year mu-b of digit-labs published information about few serious vulnerabilities he found in drivers distributed with DESlock+ - DESLock+ IOCTL Request Local Code Execution and Denial of Service Vulnerabilities. Yesterday he placed on digit-labs another exploits to prove that vulnerabilities in latest version of DESlock+ still exists. I decided to publish sample DoS exploit related with vdlptokn.sys device driver. I hope that this time Data Encryption Systems will correct all vulnerabilities.

Vdlptoken_DoS.zip - Local Denial of Service Exploit

ESET SysInspector - (esiadrv.sys) Proof of Concept Exploit

ESET SysInspector is prone to a local privilege escalation vulnerability, which could be exploited by local users in order o execute arbitrary code with kernel privileges. The problem specifically exists within the IOCTL handling code in the esiadrv.sys (3.0.65535.0) device driver. The device driver fails to validate user supplied addresses passed to IOCTLs. All IOCTLs are generated as METHOD_NEITHER. Although this is not serious vulnerability because the device driver is loaded and unloaded dynamically with the GUI application.

	.text:00012C3A @@ioctl_0x223C1F:
	.text:00012C3A                 mov     eax, [ebp+10h]                 ; InputBuffer
	.text:00012C3D                 cmp     eax, ebx
	.text:00012C3F                 jz      short @@jump
	.text:00012C41                 cmp     [ebp+14h], edi                 ; InputBufferLength
	.text:00012C44                 jnz     short @@jump
	.text:00012C46                 cmp     dword ptr [eax], 12345678h     ; InputBuffer[0] == 0x12345678 ?
	.text:00012C4C                 jnz     short @@jump
	.text:00012C4E                 push    offset aBsodCalled             ; "BSOD called\n"
	.text:00012C53                 call    DbgPrint
	.text:00012C58                 pop     ecx
	.text:00012C59                 push    0DEADDEADh
	.text:00012C5E                 call    ds:KeBugCheck
	.text:00012C64 @@jump:
	.text:00012C64                 mov     esi, [ebp+18h]                 ; OutputBuffer
	.text:00012C67                 cmp     esi, ebx
	.text:00012C69                 jz      short @@invalid_parameter
	.text:00012C6B                 cmp     [ebp+1Ch], edi                 ; OutputBufferLength
	.text:00012C6E                 jb      short @@invalid_parameter
	.text:00012C70                 mov     [ebp+10h], ebx
	.text:00012C73                 lea     eax, [ebp+10h]
	.text:00012C76                 push    eax
	.text:00012C77                 push    dword ptr [ebp+28h]            ; DeviceObject
	.text:00012C7A                 call    sub_148EC
	.text:00012C7F                 mov     [ebp-1Ch], eax
	.text:00012C82                 mov     eax, [ebp+10h]
	.text:00012C85                 mov     [esi], eax                     ; OutputBuffer[0] - ?x????????
	.text:00012C87                 mov     [ebp-20h], edi
	.text:00012C8A                 jmp     short @@return

Esiasdrv_DoS.zip - Local Denial of Service Exploit

Windows XP SP2/SP3 (NtUserConsoleControl) - Local Privilege Escalation

Several days ago Jonathan Ness added informational entries on Security Research & Defense blog concerning Windows XP kernel 0day claim in win32k!NtUserConsoleControl [1]. Jonathan explains that unchecked pointers in privileged services like NtUserConsoleControl are rather reliability issues, not security vulnerabilities.

Some of the services provided by Multi-User Win32 Driver (win32k.sys) are intended to be called only by Client Server Runtime Process (csrss.exe). One of them is NtUserInitialize which is used for Win32 subsystem initialization. NtUserInitialize checks if global variable gpepCSRSS was initialized already, if so STATUS_UNSUCCESSFUL status is returned, otherwise the address of the caller's process object is stored in gpepCSRSS. Privileged services can be recognized by constant prologue of their body. Before the service will do its job, it checks that the caller's process object is equal to gpepCSRSS, if not service returns STATUS_ACCESS_DENIED. This is a list of privileged services:

- NtUserRemoteConnect(x, x, x)
- NtUserSetInformationThread(x, x, x, x)
- NtUserConsoleControl(x, x, x)
- NtUserSetInformationProcess(x, x, x, x)
- NtUserNotifyProcessCreate(x, x, x, x)
- NtUserRemoteRedrawScreen()
- NtUserRemoteStopScreenUpdates()
- NtUserCtxDisplayIOCtl(x, x, x)
- NtUserHardErrorControl(x, x, x)
- NtUserQueryInformationThread(x, x, x, x, x)
- NtGdiFullscreenControl(x ,x ,x ,x ,x)

Few internal functions are invoked by:

- NtUserCallNoParam(x)
- NtUserCallOneParam(x, x)
- NtUserCallTwoParam(x, x, x)

Only one service - NtGdiFullscreenControl - uses parameters validation, other services and internal functions trust to all passed parameters.

It is true that only an administrator can play with this issue, but like in the case of access to \Device\PhysicalMemory [4][5][7] and some functionality provided by NtSystemDebugControl service [6][7], this functionality allows malicious software to get into the kernel of forbidden way (the only correct way of calling into the kernel is to invoke NtLoadDriver service), without paying attention to security software. Like in other cases this functionality was removed in Windows 2003 SP1.

	.text:BF8E5400 ; __stdcall NtUserConsoleControl(x, x, x)
	.text:BF8E5400 NtUserConsoleControl proc near
	.text:BF8E5400 ConsoleCommand = dword ptr 8
	.text:BF8E5400 ConsoleInformation = dword ptr 0Ch
	.text:BF8E5400 ConsoleInformationLength = dword ptr 10h
	.text:BF8E5400                 mov     edi, edi
	.text:BF8E5402                 push    ebp
	.text:BF8E5403                 mov     ebp, esp
	.text:BF8E5405                 push    esi
	.text:BF8E5406                 call    EnterCrit
	.text:BF8E540B                 call    ds:PsGetCurrentProcess
	.text:BF8E5411                 cmp     eax, gpepCSRSS
	.text:BF8E5417                 jnz     short @@status_access_denied
	.text:BF8E5419                 push    [ebp+ConsoleInformationLength]
	.text:BF8E541C                 push    [ebp+ConsoleInformation]
	.text:BF8E541F                 push    [ebp+ConsoleCommand]
	.text:BF8E5422                 call    xxxConsoleControl
	.text:BF8E5427                 mov     esi, eax
	.text:BF8E5429 @@return:
	.text:BF8E5429                 call    LeaveCrit
	.text:BF8E542E                 mov     eax, esi
	.text:BF8E5430                 pop     esi
	.text:BF8E5431                 pop     ebp
	.text:BF8E5432                 retn    0Ch
	.text:BF8E5435 @@status_access_denied:
	.text:BF8E5435                 mov     esi, 0C0000022h
	.text:BF8E543A                 jmp     short @@return
	.text:BF8E543A NtUserConsoleControl endp
... .text:BF8E55A0 @@case_07: .text:BF8E55A0 mov esi, [ebp+ConsoleInformation] .text:BF8E55A3 mov ecx, [esi] ; ConsoleInformation[0] - DesktopHandle .text:BF8E55A5 xor ebx, ebx .text:BF8E55A7 push ebx ; HandleInformation .text:BF8E55A8 mov [esi+0Ch], ebx ; ConsoleInformation[3] <-; 0x00000000 .text:BF8E55AB mov eax, ds:ExDesktopObjectType .text:BF8E55B0 mov eax, [eax] .text:BF8E55B2 lea edx, [ebp+ConsoleCommand] .text:BF8E55B5 push edx ; Object .text:BF8E55B6 push 1 ; AccessMode .text:BF8E55B8 push eax ; ObjectType .text:BF8E55B9 push ebx ; DesiredAccess .text:BF8E55BA push ecx ; Handle .text:BF8E55BB call ds:ObReferenceObjectByHandle .text:BF8E55C1 cmp eax, ebx .text:BF8E55C3 mov edi, [ebp+ConsoleCommand] .text:BF8E55C6 mov [ebp+ObjectPointer], edi .text:BF8E55C9 jl @@exit ...

The easiest way to exploit this issue is to call NtUserConsoleControl with control code equal seven. The malicious user doesn't even need to prepare and pass a valid buffer. If the buffer at index zero points to invalid desktop's object handle, invoked function ObReferenceObjectByHandle will return applied status which will cause immediate exit from the main function. But before calling ObReferenceObjectByHandle the main function overwrites the buffer at index three with zero. This allows to overwrite any address with zero value.

The issue of privileged services was touch a couple of months ago [3], but ostentatious exploit was provided only on July 11 by Azy [2]. I prepared another ostentatious piece of code which exploiting this issue.

NtUserConsoleControl_Exp.zip - Local Privilege Escalation Exploit

[1] - Latest Baidu public posting requires Adminisrator to elevate
[2] - [0day] Windows kernel vulnerability
[3] - Accessing memory in the context of csrss
[4] - NT's "\dev\kmem"
[5] - Playing with Windows /dev/(k)mem
[6] - Multiple Windows XP Kernel Vulnerability Allow User Mode Programs To Gain Kernel Privileges
[7] - Subverting Windows 2003 SP1 Kernel Integrity Protection

Hidden Data Detection Softwares

I've created the list of anti rootkit (and related to anti rootkit) software. I suppose, that this list is incomplete and contains out-of-date software. I'll be grateful for any information about latest versions of listed software and propositions of software not presented on this list. I admit that in the near future I'm going to use this software to comparative tests.

Hidden Dynamic-Link Library Detection Test

According to mentioned comparative tests I've published results of Hidden Dynamic-Link Library Detection Test. This article is a small introduction to test series I've begun. In the next part I will share results of hidden process/thread detection.

Hidden Process Detection Test

Today I've published results of Hidden Process Detection Test. This test includes short demonstrational movie of process hiding using the testing tool - Invisible Process 1.0. In this movie I show final results of process detection by the most advanced software. Next time I'll share results of Hidden Kernel Module Detection Test.

Microsoft Windows NT #GP Trap Handler Allows Users to Switch Kernel Stack

Few days ago, Tavis Ormandy has published (Microsoft Windows NT #GP Trap Handler Allows Users to Switch Kernel Stack) interesting vulnerability he found in Windows' support for Intel's hardware 8086 emulation support. This really old vulnerability is related to other vulnerability discovered by Derek Soeder - EEYE: Windows VDM #UD Local Privilege Escalation. Tavis Ormandy has published also a PoC exploit which can be downloaded here - KiTrap0D.zip.

Few days earlier Matthew "j00ru" Jurczyk and Gynvael Coldwind have published a paper (GDT and LDT in Windows kernel vulnerability exploitation) which "describes some possible ways of exploiting kernel-mode write-what-where vulnerabilities in a stable manner, on Microsoft Windows NT-family systems". Technique described in this paper is suitable for situations where the attacker is able to overwrite only 1 byte of the protected memory.

I decided to accelerate disclosure of a few advisories (NTIADV0805, NTIADV0808, NTIADV0902). I'm also going to introduce some changes in Vulnerability Disclosure Process.

Is RISING Antivirus 2008/2009/2010 still vulnerable?

Last Saturday I've published two advisories related to RISING Antivirus software. While checking latest version of this software I only checked software provided on http://www.rising-global.com/ where can be downloaded RISING Antivirus 2009 in version Today I was noticed that there is available RISING Antivirus 2010 which certainly should contain fixed device drivers. I've checked this version ( - it can be downloaded here - http://rsdownload.rising.com.cn/for_down/ravdver/risol.exe) and I confirm that this version is also vulnerable. As proof I'm providing sample exploit - RsNTGdi_Exp.zip. I'm also composing a new advisory since I found another vulnerability in RISING Antivirus 2010.

Hidden Driver Detection Test

It is high time to publish another part of Hidden Data Detection Software test series - Hidden Driver Detection Test.

Dangerous Hot Patching feature of KingSoft software.

Three days ago (Xuanyuan Smart) published a Proof of Concept which exploits Hot Patching feature of KAVSafe.sys device driver which is shipped with KingSoft WebShield. About two months earlier I've analyzed KingSoft software and found this vulnerability as well. I've observed that not only KAVSafe.sys shipped with KingSoft WebShield is vulnerable, there are other drivers (KAVBase.sys, KAVBootC.sys, BC.sys) shipped with almost all KingSoft products which have the same feature as KAVSafe.sys. All devices created by vulnerable drivers have default security descriptors, so they are accessible by all users including unprivileged/non-administrative users. Some of drivers have implementad a piece of code which is called during device handle oppening event (IRP_MJ_CREATE). This code checks if the image path of the process which trying to open handle of the device ("\Device\KAVSafe" in case of KAVSafe.sys driver) is located in a default installation folder of WebShield software. And because KAVSafe.sys compares data obtained from registry with data stored in PEB of the caller process, the caller process can manipulate PEB's data before trying to open device handle to fool this mechanism - exactly as in the PoC exploit.

	.text:00012120 ; int __stdcall Ioctl_HotPatch_0x830020D4(int, int, int, int, int)
	.text:00012120 Ioctl_HotPatch_0x830020D4 proc near
	.text:00012120 LocalInputBuffer = dword ptr -8
	.text:00012120 ByteCounter = dword ptr -4
	.text:00012120 InputBuffer = dword ptr  8
	.text:00012120 InputBufferLength = dword ptr  0Ch
	.text:00012120 OutputBuffer = dword ptr  10h
	.text:00012120 OutputBufferLength = dword ptr  14h
	.text:00012120 IoStatus = dword ptr  18h
	.text:00012120                 push    ebp
	.text:00012121                 mov     ebp, esp
	.text:00012123                 sub     esp, 8
	.text:00012126                 mov     [ebp+ByteCounter], 0
	.text:0001212D                 mov     eax, [ebp+InputBuffer]
	.text:00012130                 mov     [ebp+LocalInputBuffer], eax
	.text:00012133                 mov     ecx, [ebp+IoStatus]
	.text:00012136                 mov     dword ptr [ecx], 0C0000001h
	.text:0001213C                 mov     edx, [ebp+IoStatus]
	.text:0001213F                 mov     dword ptr [edx+4], 0
	.text:00012146                 cmp     [ebp+InputBuffer], 0
	.text:0001214A                 jz      short __exit__enabple_wp_exit
	.text:0001214C                 cmp     [ebp+InputBufferLength], 4Ch
	.text:00012150                 jb      short __exit__enabple_wp_exit
	.text:00012152                 mov     eax, [ebp+LocalInputBuffer]
	.text:00012155                 mov     ecx, [eax+44h]                 ; PatchSize
	.text:00012158                 push    ecx                            ; HotPatchCodeSize
	.text:00012159                 mov     edx, [ebp+LocalInputBuffer]
	.text:0001215C                 add     edx, 48h
	.text:0001215F                 push    edx                            ; HotPatchCode
	.text:00012160                 mov     eax, [ebp+LocalInputBuffer]
	.text:00012163                 mov     ecx, [eax+40h]                 ; FunctionAddress
	.text:00012166                 push    ecx                            ; CodeAddressOffset
	.text:00012167                 mov     edx, [ebp+LocalInputBuffer]
	.text:0001216A                 push    edx                            ; ModuleName
	.text:0001216B                 mov     ecx, offset unk_1C258
	.text:00012170                 call    HotPatchKernelModule
	.text:00012175                 mov     [ebp+ByteCounter], eax
	.text:00012178                 cmp     [ebp+ByteCounter], 0
	.text:0001217C                 jz      short __exit__enabple_wp_exit
	.text:0001217E                 mov     eax, [ebp+IoStatus]
	.text:00012181                 mov     ecx, [ebp+ByteCounter]
	.text:00012184                 mov     [eax+4], ecx
	.text:00012187                 mov     edx, [ebp+IoStatus]
	.text:0001218A                 mov     dword ptr [edx], 0
	.text:00012190 __exit__enabple_wp_exit:
	.text:00012190                 mov     eax, [ebp+IoStatus]
	.text:00012193                 mov     eax, [eax]
	.text:00012195                 mov     esp, ebp
	.text:00012197                 pop     ebp
	.text:00012198                 retn    14h
	.text:00012198 Ioctl_HotPatch_0x830020D4 endp

Input buffer passed to the device dispatch routine contains four items: first item - contains name of the kernel module which will be patched, second - an offset of the function (code), third - size of the new code, and finally fourth item contains the new code. In a first phase of hot patching, the "GetModuleBaseByName" function walks through linked list of loaded kernel modules (DriverObject->DriverSection), looking for module which name is equal to name sent in a IOCTL request. If appropriate module will be found, function returns a module base which will be added to the offset (second item of the IOCTL buffer).

	.text:0001B280 ; int __stdcall HotPatchKernelModule(int, int, int, int)
	.text:0001B280 HotPatchKernelModule proc near
	.text:0001B280 var_14 = dword ptr -14h
	.text:0001B280 CodeSize = dword ptr -10h
	.text:0001B280 var_C = dword ptr -0Ch
	.text:0001B280 ModuleBase = dword ptr -8
	.text:0001B280 LocalOffset = dword ptr -4
	.text:0001B280 ModuleName = dword ptr  8
	.text:0001B280 CodeAddressOffset = dword ptr  0Ch
	.text:0001B280 HotPatchCode = dword ptr  10h
	.text:0001B280 HotPatchCodeSize = dword ptr  14h
	.text:0001B280                 push    ebp
	.text:0001B281                 mov     ebp, esp
	.text:0001B283                 sub     esp, 14h
	.text:0001B286                 mov     [ebp+var_14], ecx
	.text:0001B289                 mov     [ebp+CodeSize], 0
	.text:0001B290                 mov     [ebp+var_C], 0
	.text:0001B297                 mov     [ebp+ModuleBase], 0
	.text:0001B29E                 mov     [ebp+LocalOffset], 0
	.text:0001B2A5                 call    DisableWriteProtect
	.text:0001B2AA                 lea     eax, [ebp+var_C]
	.text:0001B2AD                 push    eax
	.text:0001B2AE                 mov     ecx, [ebp+ModuleName]
	.text:0001B2B1                 push    ecx
	.text:0001B2B2                 mov     ecx, [ebp+var_14]
	.text:0001B2B5                 call    GetModuleBaseByName
	.text:0001B2BA                 mov     [ebp+ModuleBase], eax
	.text:0001B2BD                 cmp     [ebp+ModuleBase], 0
	.text:0001B2C1                 jnz     short __module_loaded
	.text:0001B2C3                 jmp     short __enabple_wp_exit
	.text:0001B2C5                 jmp     short __enabple_wp_exit
	.text:0001B2C7 __module_loaded:
	.text:0001B2C7                 mov     edx, [ebp+HotPatchCodeSize]
	.text:0001B2CA                 cmp     edx, [ebp+var_C]
	.text:0001B2CD                 jbe     short __save_code_size
	.text:0001B2CF                 jmp     short __enabple_wp_exit
	.text:0001B2D1                 jmp     short __enabple_wp_exit
	.text:0001B2D3 __save_code_size:
	.text:0001B2D3                 mov     eax, [ebp+HotPatchCodeSize]
	.text:0001B2D6                 mov     [ebp+CodeSize], eax
	.text:0001B2D9                 mov     [ebp+LocalOffset], 0
	.text:0001B2E0                 jmp     short __is_address_valid
	.text:0001B2E2 __loop:
	.text:0001B2E2                 mov     ecx, [ebp+LocalOffset]
	.text:0001B2E5                 add     ecx, 1
	.text:0001B2E8                 mov     [ebp+LocalOffset], ecx
	.text:0001B2EB __is_address_valid:
	.text:0001B2EB                 mov     edx, [ebp+LocalOffset]
	.text:0001B2EE                 cmp     edx, [ebp+CodeSize]
	.text:0001B2F1                 jge     short __enabple_wp_exit
	.text:0001B2F3                 mov     eax, [ebp+ModuleBase]
	.text:0001B2F6                 add     eax, [ebp+CodeAddressOffset]
	.text:0001B2F9                 add     eax, [ebp+LocalOffset]
	.text:0001B2FC                 push    eax
	.text:0001B2FD                 call    ds:MmIsAddressValid
	.text:0001B303                 movzx   ecx, al
	.text:0001B306                 test    ecx, ecx
	.text:0001B308                 jnz     short __patch_byte
	.text:0001B30A                 jmp     short __enabple_wp_exit
	.text:0001B30C __patch_byte:
	.text:0001B30C                 mov     edx, [ebp+ModuleBase]
	.text:0001B30F                 add     edx, [ebp+CodeAddressOffset]
	.text:0001B312                 mov     eax, [ebp+HotPatchCode]
	.text:0001B315                 add     eax, [ebp+LocalOffset]
	.text:0001B318                 mov     ecx, [ebp+LocalOffset]
	.text:0001B31B                 mov     al, [eax]
	.text:0001B31D                 mov     [edx+ecx], al
	.text:0001B320                 jmp     short __loop
	.text:0001B322 __enabple_wp_exit:
	.text:0001B322                 call    EnablaWriteProtect
	.text:0001B327                 mov     eax, [ebp+LocalOffset]
	.text:0001B32A                 mov     esp, ebp
	.text:0001B32C                 pop     ebp
	.text:0001B32D                 retn    10h
	.text:0001B32D HotPatchKernelModule endp

Finally "HotPatchKernelModule" function checks if the address of code to patch "is valid" - wasn't paged out, and if so, it copies a new code (byte by byte) at specified address.

Affected Software:

Kingsoft WebShield
    KAVSafe.sys <= 2010.4.14.609

Kingsoft Internet Security 9 Plus
    KAVSafe.sys - Kingsoft Antivirus Defend Engine Safe Module <= 2009.8.10.138
    KAVBase.sys - KAVBase Application <= 2009.6.17.160
    KAVBootC.sys - Kingsoft Antivirus Defend Engine Bootclean Module <= 2009.8.10.138

Kingsoft Antivirus Security System
    BC.sys - Kingsoft Boot safe <= 2010.4.7.88

Kingsoft Antivirus Online
    KAVBootC.sys - Kingsoft Boot Clean <= 2008.4.28.85
  Kingsoft Anti-Spyware
    KAVBootC.sys - Kingsoft Boot Clean <= 2008.4.28.85

Kingsoft Internet Security 2011
    KAVSafe.sys - Kingsoft Antivirus Defend Engine Safe Module <= 2010.4.14.609

Kingsoft System Cleaner
    BC.sys - Kingsoft Boot safe <= 2010.4.7.88
    KAVSafe.sys - Kingsoft Antivirus Defend Engine Safe Module <= 2010.4.14.609


Other products and versions may also be affected.

Non-Affected Software:

Kingsoft KSafe
Kingsoft Antivirus XEngine System

Copyright © 2oo8-2o11 NT Internals. All rights reserved.