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( ... )
or
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 example 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!
Interesting stuff.
It would be nice if some win32k syscall handlers allowed to store in rax bytes from a caller-controlled address.
@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).
useful in a remote ring3->ring0 transition via system call bug; <3 win32k.
Nice post!!!, as jf said, it could be very useful in some scenarios where you need to do a transition ring3->ring0.
@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.
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
cmp eax,STATUS_INFO_LENGTH_MISMATCH
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
assume edx:PSYSTEM_HANDLE_INFORMATION
Next:
cmp [edx].ProcessId,ebx
jne @f
cmp [edx].Handle,ax
je Objext
@@:
add edx,sizeof(SYSTEM_HANDLE_INFORMATION)
loop Next
mov eax,STATUS_NOT_FOUND
jmp Error
Objext:
mov edx,[edx].Object
mov ebx,Object
xor eax,eax
mov dword ptr [ebx],edx
Error:
push eax
invoke ZwFreeVirtualMemory, NtCurrentProcess, addr SystemInformation, addr SystemInformationLength, MEM_RELEASE
pop eax
Exit:
ret
QueryObject endp
@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: