Skip to content

Subtle information disclosure in WIN32K.SYS syscall return values

While performing some random research related to the WIN32K.SYS driver syscalls a few months ago, I stumbled on an interesting finding – when examining the full 32-bit (or in the case of the original research – 64) return values, some of the services seemed to return unusual numbers, for example 0xfffffa8000ea0000. After investigating the subject for a while, it turned out that a part of the WIN32K syscall interface indeed does not always return a value of the native CPU size (as opposed to the core NT kernel). Instead, some of the graphical services seem to be explicitly declared as:

VOID NtUserRandomService( ... )


USHORT NtUserRandomService( ... )

Funny enough to find such types of quirks in the Windows kernel, eh? A list of the flawed syscall names is as follows:

  • ETHREAD (full disclosure)
    • NtUserAlterWindowStyle
    • NtUserSetThreadState
    • NtUserNotifyWinEvent
    • NtUserModifyUserStartupInfoFlags
    • NtUserSetThreadLayoutHandles
    • NtUserNotifyIMEStatus
    • NtUserSetThreadLayoutHandles
    • NtUserSetRipFlags (Windows XP only)
    • NtUserSetDbgTag (Windows XP only)
  • ETHREAD (partial disclosure)
    • NtUserRegisterClassExWOW
    • NtUserGetKeyState
    • NtUserGetAsyncKeyState
    • NtUserVkKeyScanEx
    • NtUserSetWindowWord
    • NtUserSetClassWord
  • W32THREAD (full disclosure)
    • NtGdiEngUnlockSurface
    • NtGdiPATHOBJ_vEnumStartClipLines
    • NtGdiEngDeletePath
    • NtGdiEngDeleteClip
    • NtGdiFONTOBJ_vGetInfo
  • Other (unknown?)
    • NtGdiFlush
    • NtGdiEngUnlockSurface

The above list was created based on the Windows 7 64-bit graphical system call table, although I do not guarantee it is by any means complete (system calls might have been added, removed, or altered since then). As can be seen, a user-mode application is primarily able to read the addresses of two, internal kernel-mode structures (assigned to the currently executed thread, thus no serious information disclosure is going on, here). These are:

  • ETHREAD – the very standard NT kernel structure, assigned to every single thread running in the system,
  • W32THREAD – an internal, and pretty much unexplored structure, allocated on demand (i.e. when the thread calls a win32k service for the first time). For more details, you’re advised to take a look at the Mysteries of win32k & GDI post on the Woodmann RCE Forums.

In order to confirm the story a little, let’s take a look at the exemplary NtUserGetKeyState system call epilogue:

mov     [ecx+48h], edx
mov     eax, [eax+70h]
mov     [ecx+4Ch], eax
call    _LeaveCrit@0    ; LeaveCrit()
mov     ax, di
pop     edi
pop     esi
pop     ebp
retn    4

Clearly, the code doesn’t care about the upper 16 bits of the return value (leading to a partial disclosure) – that’s also the case for the rest of the aforementioned routines. A complete log from a Windows 7 64-bit instance of a Proof of Concept code follows:

--- 64bit Kernel-address disclosure:
[+] [ETHREAD] NtUserAlterWindowStyle          : 0xfffffa8000ea80b0
[+] [ETHREAD] NtUserSetThreadState            : 0xfffffa8000ea80b0
[+] [ETHREAD] NtUserNotifyWinEvent            : 0xfffffa8000ea80b0
[+] [ETHREAD] NtUserModifyUserStartupInfoFlags: 0xfffffa8000ea80b0
[+] [ETHREAD] NtUserSetThreadLayoutHandles    : 0xfffffa8000ea80b0
[+] [ETHREAD] NtUserNotifyIMEStatus           : 0xfffffa8000ea80b0
[+] [ETHREAD] NtUserSetThreadLayoutHandles    : 0xfffffa8000ea80b0
[+] [W32THREAD] NtGdiEngUnlockSurface         : 0xfffff900c216f010
[+] [W32THREAD] NtGdiPATHOBJ_vEnumStartClipLines: 0xfffff900c216f010
[+] [W32THREAD] NtGdiEngDeletePath            : 0xfffff900c216f010
[+] [W32THREAD] NtGdiEngDeleteClip            : 0xfffff900c216f010
[+] [W32THREAD] NtGdiFONTOBJ_vGetInfo         : 0xfffff900c216f010

--- 48bit Kernel-address disclosure (the AX value is uncontrolled):
[+] [ETHREAD] NtUserRegisterClassExWOW        : 0xfffffa8000ea0000
[+] [ETHREAD] NtUserGetKeyState               : 0xfffffa8000ea0000
[+] [ETHREAD] NtUserGetAsyncKeyState          : 0xfffffa8000ea0000
[+] [ETHREAD] NtUserVkKeyScanEx               : 0xfffffa8000ea0332
[+] [ETHREAD] NtUserSetWindowWord             : 0xfffffa8000ea0000
[+] [ETHREAD] NtUserSetClassWord              : 0xfffffa8000ea0000

Since the disclosed addresses do not pose a direct threat to the system security, the MSRC team did not consider the issue worth fixing. Well, although I do agree that it’s a minor issue, it is still amusing for me to find such peculiarities in the main Windows graphical kernel module ;) No source code this time, although I do encourage you to fire your IDA and verify the findings by yourself (Windows WIN32K.SYS System Call Table might come in handy).

Take care!

PS. It seems that I wasn’t the only one coming across this matter – Tavis Ormandy has apparently tweeted about it a year ago, as well ;) Nice one.

PS2. I received the first, valid submission for Pimp CrackMe – a reversing challenge by Gynvael and me (see: link). Well done!

{ 8 } Comments

  1. Cr4sh | 22-May-11 at 07:57:49 | Permalink

    Interesting stuff.
    It would be nice if some win32k syscall handlers allowed to store in rax bytes from a caller-controlled address.

  2. j00ru | 22-May-11 at 08:12:54 | Permalink

    @Crash: Yeah, it would certainly be nice ;)
    In such a case, I guess MSRC would take the time and issue a patch, as it’d be quite a robust Information Disclosure vuln (which I think win32k.sys has plenty of, by the way).

  3. jf | 22-May-11 at 08:18:52 | Permalink

    useful in a remote ring3->ring0 transition via system call bug; <3 win32k.

  4. NCR | 22-May-11 at 17:13:51 | Permalink

    Nice post!!!, as jf said, it could be very useful in some scenarios where you need to do a transition ring3->ring0.

  5. j00ru | 22-May-11 at 22:43:11 | Permalink

    @jf @NCR: Yeah, this kind of information might definitely come in handy in ring-0 exploitation ;) There are quite a few curiosities in the kernel user/gdi implementation, more coming soon.

    It is worth to note that the ETHREAD address can be currently obtained using a NtQuerySystemInformation(SystemHandleInformation) undocumented call. AFAIK, there’s no such “official” method for the second internal structure, W32THREAD.

  6. Indy | 26-Jun-11 at 01:34:12 | Permalink

    This useless information, have a legal way to get a reference to the object:

    QueryObject proc uses ebx ObjectHandle:HANDLE, Object:PVOID
    Local SystemInformation:PVOID, SystemInformationLength:ULONG
    Local ProcessInformation:PROCESS_BASIC_INFORMATION
    Local HandleInformation[sizeof(SYSTEM_HANDLE_INFORMATION) + 4]:BYTE
    invoke ZwQueryInformationProcess, NtCurrentProcess, ProcessBasicInformation, addr ProcessInformation, sizeof(PROCESS_BASIC_INFORMATION), NULL
    test eax,eax
    jnz Exit
    mov SystemInformation,eax
    invoke ZwQuerySystemInformation, SystemHandleInformation, addr HandleInformation, sizeof(SYSTEM_HANDLE_INFORMATION) + 4, addr SystemInformationLength
    jne Exit
    cmp SystemInformationLength,NULL
    je Exit
    add SystemInformationLength,50*sizeof(SYSTEM_HANDLE_INFORMATION)
    invoke ZwAllocateVirtualMemory, NtCurrentProcess, addr SystemInformation, 0, addr SystemInformationLength, MEM_COMMIT, PAGE_READWRITE
    test eax,eax
    jnz Exit
    invoke ZwQuerySystemInformation, SystemHandleInformation, SystemInformation, SystemInformationLength, Eax
    test eax,eax
    mov edx,SystemInformation
    jnz Error
    mov ebx,ProcessInformation.UniqueProcessId
    mov ecx,dword ptr [edx]
    mov eax,ObjectHandle
    add edx,4
    cmp [edx].ProcessId,ebx
    jne @f
    cmp [edx].Handle,ax
    je Objext
    loop Next
    mov eax,STATUS_NOT_FOUND
    jmp Error
    mov edx,[edx].Object
    mov ebx,Object
    xor eax,eax
    mov dword ptr [ebx],edx
    push eax
    invoke ZwFreeVirtualMemory, NtCurrentProcess, addr SystemInformation, addr SystemInformationLength, MEM_RELEASE
    pop eax
    QueryObject endp

  7. j00ru | 01-Jul-11 at 01:55:11 | Permalink

    @Indy: Yeah, I do realize that it is possible to retrieve the ETHREAD address by other means (namely, the NtQuerySystemInformation service). I even mentioned it in one of my comment replies:

    It is worth to note that the ETHREAD address can be currently obtained using a NtQuerySystemInformation(SystemHandleInformation) undocumented call.

  8. Kiona | 08-Jul-11 at 00:49:55 | Permalink

    There’s nothing like the relief of findnig what you’re looking for.

{ 2 } Trackbacks

  1. […] j00ru opisuje ciekawe wartości, które zwracane przez niektóre usługi. […]

  2. […] vendor’s attitude towards the impact of kernel-related leaks has been presented in the recent “Subtle information disclosure in WIN32K.SYS syscall return values” blog […]

Post a Comment

Your email is never published nor shared. Required fields are marked *