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)
- NtUserSetRipFlags (Windows XP only)
- NtUserSetDbgTag (Windows XP only)
- ETHREAD (partial disclosure)
- W32THREAD (full disclosure)
- Other (unknown?)
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).
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.