# CVE-2021-1732: win32kfull xxxCreateWindowEx callback out-of-bounds
Mar 25, 2021 • iamelli0t
CVE-2021-1732 is a 0-Day vulnerability exploited by the BITTER APT
organization in one operation which was disclosed in February this
year[1][2][3]. This vulnerability exploits a user mode callback opportunity in
win32kfull module to break the normal execution flow and set the error flag of
window object (tagWND) extra data, which results in kernel-space out-of-bounds
memory access violation.
## Root cause analysis
The root cause of CVE-2021-1732 is:
In the process of creating window (CreateWindowEx), when the window object
tagWND has extra data (tagWND.cbwndExtra != 0), the function pointer of
user32!_xxxClientAllocWindowClassExtraBytes saved in
ntdll!_PEB.kernelCallbackTable (offset+0x58) in user mode will be called via
the nt!KeUserModeCallback callback mechanism, and the system heap allocator
(ntdll!RtlAllocateHeap) is used to allocate the extra data memory in user-
space.
By hooking user32!_xxxClientAllocWindowClassExtraBytes function in user mode,
and modifying the properties of the window object extra data in the hook
function manually, the kernel mode atomic operation of allocating memory for
extra data can be broken, then the out-of-bounds read/write ability based on
the extra data memory is achieved finally.
The normal flow of the window object creation (CreateWindowEx) process is
shown as follows (partial):
![avatar](https://images.seebug.org/1616724448986-w331s)
From the above figure, we can see that: when the window extra data size
(tagWND.cbWndExtra) is not 0, win32kfull!xxxCreateWindowEx calls the user mode
function user32!_xxxClientAllocWindowClassExtraBytes via the kernel callback
mechanism, requests for the memory of the window extra data in user-space.
After allocation, the pointer of allocated memory in user-space will be
returned to the tagWND.pExtraBytes property:
![avatar](https://images.seebug.org/1616724450357-w331s)
Here are two modes of saving tagWND extra data address (tagWND.pExtraBytes):
[Mode 1] **In user-space system heap**
As the normal process shown in the figure above, the pointer of extra data
memory allocated in user-space system heap is saved in tagWND.pExtraBytes
directly.
One tagWND memory layout of Mode 1 is shown in the following figure:
![avatar](https://images.seebug.org/1616724451170-w331s)
[Mode 2] **In kernel-space desktop heap**
The function ntdll!NtUserConsoleControl allocates extra data memory in kernel-
space desktop heap by function DesktopAlloc, calculates the offset of
allocated extra data memory address to the kernel desktop heap base address,
saves the offset to tagWND.pExtraBytes, and modifies tagWND.extraFlag |=
0x800:
![avatar](https://images.seebug.org/1616724452136-w331s)
One tagWND memory layout of Mode 2 is shown in the following figure:
![avatar](https://images.seebug.org/1616724453007-w331s)
So we can hook the function user32!_xxxClientAllocWindowClassExtraBytes in
user-space, call NtUserConsoleControl manually in hook function to modify the
tagWND extra data storage mode from Mode 1 to Mode 2, call
ntdll!NtCallbackReturn before the callback returns:
![avatar](https://images.seebug.org/1616724456200-w331s)
Then return the user mode controllable offset value to tagWND.pExtraBytes
through ntdll!NtCallbackReturn, and realize the controllable offset out-of-
bounds read/write ability based on the kernel-space desktop heap base address
finally.
The modified process which can trigger the vulnerability is shown as follows:
![avatar](https://images.seebug.org/1616724456799-w331s)
According to the modified flowchart above, the key steps of triggering this
vulnerability are explained as follows:
1. Modify the user32!_xxxClientAllocWindowClassExtraBytes function pointer in PEB.kernelCallbackTable to a custom hook function.
2. Create some normal window objects, and leak the user-space memory addresses of these tagWND kernel objects through user32!HMValidateHandle.
3. Destroy part of the normal window objects created in step 2, and create one new window object named 'hwndMagic' with the specified tagWND.cbwndExtra. The hwndMagic can probably reuse the previously released window object memory. Therefore, by searching the previously leaked window object user-space memory addresses with the specified tagWND.cbwndExtra in the custom hook function, the hwndMagic can be found before CreateWindowEx returns.
4. Call NtUserConsoleControl in the custom hook function to modify the tagWNDMagic.extraFlag with flag 0x800.
5. Call NtCallbackReturn in the custom hook function to assign a fake offset to tagWNDMagic.pExtraBytes.
6. Call SetWindowLong to write data to the address of kernel-space desktop heap base address + specified offset, which can result in out-of-bounds memory access violation.
An implementation of the hook function is demonstrated as follows:
void* WINAPI MyxxxClientAllocWindowClassExtraBytes(ULONG* size) {
do {
if (MAGIC_CBWNDEXTRA == *size) {
HWND hwndMagic = NULL;
//search from freed NormalClass window mapping desktop heap
for (int i = 2; i < 50; ++i) {
ULONG_PTR cbWndExtra = *(ULONG_PTR*)(g_pWnd[i] + _WND_CBWNDEXTRA_OFFSET);
if (MAGIC_CBWNDEXTRA == cbWndExtra) {
hwndMagic = (HWND)*(ULONG_PTR*)(g_pWnd[i]);
printf("[+] bingo! find &hwndMagic = 0x%llx in callback :) \n", g_pWnd[i]);
break;
}
}
if (!hwndMagic) {
printf("[-] Not found hwndMagic, memory layout unsuccessfully :( \n");
break;
}
// 1. set hwndMagic extraFlag |= 0x800
CONSOLEWINDOWOWNER consoleOwner = { 0 };
consoleOwner.hwnd = hwndMagic;
consoleOwner.ProcessId = 1;
consoleOwner.ThreadId = 2;
NtUserConsoleControl(6, &consoleOwner, sizeof(consoleOwner));
// 2. set hwndMagic pExtraBytes fake offset
struct {
ULONG_PTR retvalue;
ULONG_PTR unused1;
ULONG_PTR unused2;
} result = { 0 };
//offset = 0xffffff00, access memory = heap base + 0xffffff00, trigger BSOD
result.retvalue = 0xffffff00;
NtCallbackReturn(&result, sizeof(result), 0);
}
} while (false);
return _xxxClientAllocWindowClassExtraBytes(size);
}
BSOD snapshot:
![avatar](https://images.seebug.org/1616724457674-w331s)
## Exploit analysis
From Root cause anaysis, we can see that:
**" An opportunity to read/write data in the address which calculated by the
kernel-space desktop heap base address + specified offset"** can be obtained
via this vulnerability.
For the kernel mode exploitation, the attack target is to obtain system token
generally. A common method is shown as follows:
1. Exploit the vulnerability to obtain a arbitrary memory read/write primitive in kernel-space.
2. Leak the address of some kernel object, find the system process through the EPROCESS chain.
3. Copy the system process token to the attack process token to complete the privilege escalation job.
The obstacle is step 1: How to exploit **" An opportunity to read/write data
in the address which calculated by the kernel-space desktop heap base address
+ specified offset"** to obtain the arbitrary memory read/write primitive in
kernel-space.
One solution is shown in the following figure:
![avatar](https://images.seebug.org/1616724459293-w331s)
1. The offset of tagWNDMagic extra data (wndMagic_extra_bytes) is controllable via the vulnerability, so we can use SetWindowLong to modify the data in specified address calculated by desktop heap base address + controllable offset.
2. Use the vulnerability ability to modify tagWNDMagic.pExtraBytes to the offset of tagWND0 (the offset of tagWND0 is obtained by tagWND0+0x8), call SetWindowLong to modify tagWND0.cbWndExtra = 0x0fffffff to obtain a tampered tagWND0.pExtraBytes which can achieve read/write out-of-bounds.
3. Calculate the offset from tagWND0.pExtraBytes to tagWND1, call SetWindowLongPtr to replace the spMenu of tagWND1 with a fake spMenu by the tampered tagWND0.pExtraBytes, realize the arbitrary memory read ability with the help of fake spMenu and function GetMenuBarInfo.
The logic of GetMenuBarInfo to read the data in specified address is shown as
follows, the 16 bytes data is stored into MENUBARINFO.rcBar structure:
![avatar](https://images.seebug.org/1616724460069-w331s)
4. Use the tampered tagWND0.pExtraBytes to modify tagWND1.pExtraBytes with specified address, and use the SetWindowLongPtr of tagWND1 to obtain the arbitrary memory write ability.
5. After obtaining the arbitrary memory read/write primitive, we need to leak a kernel object address in desktop heap to find EPROCESS. Fortunately, when setting the fake spMenu for tagWND1 in step 3, the return value of SetWindowLongPtr is the kernel address of original spMenu, which can be used directly.
6. Finally, find the system process by traversing the EPROCESS chain, and copy the system process token to the attack process to complete the privilege escalation job. This method is relatively common, so will not be described in detail.
The final privilege escalation demonstration:
![avatar](https://images.seebug.org/1616724461559-w331s)
## References
[1] https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-1732
[2] https://ti.dbappsecurity.com.cn/blog/index.php/2021/02/10/windows-kernel-
zero-day-exploit-is-used-by-bitter-apt-in-targeted-attack-cn/
[3]
https://www.virustotal.com/gui/file/914b6125f6e39168805fdf57be61cf20dd11acd708d7db7fa37ff75bf1abfc29/detection
[4] https://en.wikipedia.org/wiki/Privilege_escalation
Unavailable Comments