CVE-2021-1732: Windows win32k flag setting out of sync in xxCreateWindowEx
Quan Jin, DBappSecurity
The Basics
Disclosure or Patch Date: 10 February 2021
Product: Microsoft Windows
Advisory: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2021-1732
Affected Versions: For Windows 10 20H2 x64, KB4598242 and previous
First Patched Version: For Windows 10 20H2 x64, KB4601319
Issue/Bug Report: N/A
Patch CL: N/A
Bug-Introducing CL: N/A
Reporter(s): JinQuan, MaDongZe, TuXiaoYi, and LiHao of DBAPPSecurity Co., Ltd
The Code
Proof-of-concept: https://github.com/KaLendsi/CVE-2021-1732-Exploit
Exploit sample: N/A
Did you have access to the exploit sample when doing the analysis? Yes
The Vulnerability
Bug class: Flag setting out of sync
Vulnerability details:
The bug is caused by xxxClientAllocWindowClassExtraBytes callback in win32kfull!xxxCreateWindowEx. The callback causes the setting of tagWND.WndExtra and its corresponding flag to be out of sync.
When xxxCreateWindowEx creating a window that has WndExtra area, it will call xxxClientAllocWindowClassExtraBytes to trigger a callback, the callback will return to user mode to allocate WndExtra area. In the custom callback function, the attacker could call NtUserConsoleControl and pass in the handle of current window, this will change tagWND.WndExtra to an offset, and setting a corresponding flag to indicate that tagWND.WndExtra now is an offset. After that, the attacker could call NtCallbackReturn in the callback and return an arbitrary value. When the callback ends and return to kernel mode, the return value will overwrite the tagWND.WndExtra, but the corresponding flag is not cleared. After that, the unchecked offset value is directly used by kernel code for heap memory addressing, causing out-of-bounds access.
Patch analysis: N/A
Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):
This type of vulnerability can be discovered by carefully auditing the possible changes in the flag before and after the callback function, especially carefully audit those API functions that can be called to change the kernel structure flag during the callback process.
(Historical/present/future) context of bug:
DBappSecurity found this bug exploited as an independent component.
The exploit was discovered by DBappSecurity in December 2020. The initial exploit was designed to attack Windows 1909 x64. It’s possible that the attackers decided to use this exploit to achieve privilege escalation on a windows 10 1909 x64 machine.
The Exploit
The details about the exploitation method were posted by DBappSecurity in their post "WINDOWS KERNEL ZERO-DAY EXPLOIT (CVE-2021-1732) IS USED BY BITTER APT IN TARGETED ATTACK".
Exploit strategy (or strategies):
- The origin exploit mainly creates three windows: window 0, window 1 and window 2.
- The exploit calls NtUserConsoleControl to change the flag of window 0 to offset, and leaks an offset of window 0 via HMValidateHandle.
- In the callback, the exploit calls NtUserConsoleControl again to change the flag of window 2 to offset, and triggers the bug to overwrite the tagWND.WndExtra of window 2 to the leak offset of window 0.
- When the callback ends, window 2 obtains the primitive to operate the kernel tagWND of window 0.
Exploit flow:
- The HMValidateHandle function is used to leak the offset of tagWND.
- The exploit calls SetWindowLongW to set cbWndExtra of of window 0 to 0xfffffff, this gives window 0 the OOB read/write primitives.
- The exploit then uses the OOB write primitive of window 0 to modify the style of window 1(dwStyle|=WS_CHILD), after that, the exploit replaces the origin spmenu of window 2 with a fake spmenu.
- The arbitrary read primitive is achieved by fake spmenu works with GetMenuBarInfo.
- The arbitrary write primitive is achieved via window 0 and window 1, works with SetWindowLongPtrA.
- After achieving the arbitrary read/write primitives, the exploit leaks a kernel address from the origin spmemu, then searches through it to find the EPROCESS of current process.
- Finally, the exploit traversals ActiveProcessLinks to get the token of SYSTEM EPROCESS and the token area address of current EPROCESS, and swaps the current process Token value with SYSTEM Token. This is a known technique that has been documented in many places such as here.
Known cases of the same exploit flow:
-
Many windows kernel exploits will use HMValidateHandle to leak kernel address.
-
Most win32k exploits will use SetWindowLong* functions to achieve arbitrary read/write primitives.
-
Most windows kernel exploits will swap the current process token with SYSTEM token.
Part of an exploit chain? No
The Next Steps
Variant analysis
Areas/approach for variant analysis (and why): The variant of this bug can be discovered by carefully auditing the possible changes in the flag before and after the callback function. Especially carefully audit those API functions that can be called to change the kernel structure flag during the callback process.
Found variants: N/A
Structural improvements
Ideas to kill the bug class: Carefully auditing the possible changes in the flag before and after the win32k callback function
Ideas to mitigate the exploit flow: N/A
Other potential improvements: N/A
0-day detection methods
- Check whether the token of current process is replaced with SYSTEM token at the right time.
- Check whether a call to HmValidateHandle is from an address out of user32.dll, there is a demo of this method.
Other References
- February 2021: "WINDOWS KERNEL ZERO-DAY EXPLOIT (CVE-2021-1732) IS USED BY BITTER APT IN TARGETED ATTACK" by DBappSecurity