CVE-2019-1458: Windows win32k uninitialized variable in task switching
Maddie Stone, Project Zero (Originally posted on Project Zero blog 2020-07-27)
The Basics
Disclosure or Patch Date: 10 December 2019
Product: Microsoft Windows
Advisory: https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2019-1458
Affected Versions:
First Patched Version:
Issue/Bug Report: N/A
Patch CL: N/A
Bug-Introducing CL: N/A
Reporter(s): Anton Ivanov and Alexey Kulaev of Kaspersky Lab (Thanks to Kaspersky Lab and @florek_pl for sharing detailed analyses!)
The Code
Proof-of-concept: https://github.com/piotrflorczyk/cve-2019-1458_POC
Exploit sample: N/A
Did you have access to the exploit sample when doing the analysis? No
The Vulnerability
Bug class: Uninitialized variable
Vulnerability details:
Piotr Florczyk (@florek_pl) did a full root cause analysis and published it with a proof-of-concept (POC).
The bug is that a field, \*(gpsi + 0x154)
, in a global structure, tagSERVERINFO
, which defines information about system windows for the operating system, is uninitialized. This then permits a user-space application to set extra window data in the task-switch window (FNID_SWITCH
, 0x280), which is usually only able to be set by the kernel. This extra window data is a pointer which is then dereferenced and written to. This gives a limited write primitive to an arbitrary memory address.
Patch analysis: N/A
Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):
SetWindowLongPtr
bugs have been very prevalent over the years. Therefore, I think it would be quite easy for a skilled researcher to identify through manual reverse engineering/auditing that the FNID_SWITCH field in gpsi was initially uninitialized and then follow the code path through to see that that would permit the attacker-controlled pointer dereference. Due to the different conditions and the order of system calls, I think it would be more difficult to fuzz this bug rather than manually reverse it.
(Historical/present/future) context of bug:
Kaspersky Lab found this bug exploited in conjunction with the Chrome 0-day CVE-2019-13720. The Chrome exploit embedded the exploit for this vulnerability, CVE-2019-1458, as the elevation of privilege.
This exploit was discovered by Kaspersky in November 2019. Windows 7, which this exploit targeted, was hitting end-of-life in January 2020. It’s possible that the attackers decided to just go ahead and use this exploit even if it was “noisy” or potentially easier to detect, knowing that the EOL was coming.
The Exploit
Is the exploit method known? Yes
Exploit method:
The details about the exploitation method were posted by Kaspersky Lab in their post “The Zero Day Exploits of Wizard Opium”.
To exploit this bug, a Windows kernel memory corruption, Bitmaps were used to both bypass ASLR and get arbitrary kernel read and write. This post describes the technique in detail. In their blog post, Kaspersky explains that the exploit supports two different methods for ASLR bypass:
- For older builds, the Bitmap kernel addresses are leaked by the
GdiSharedHandlerTable
- Creating an
AcceleratorTable
object and usinggSharedInfoHandleTable
to leak its kernel address, then freeing theAcceleratorTable
and allocating a Bitmap in its place.
To elevate privileges, the exploit steals the system token from the EPROCESS
object in a project running as SYSTEM
. This is a known technique that has been documented in many places such as here.
The Next Steps
Variant analysis
Areas/approach for variant analysis (and why):
When patching this bug, Microsoft added initialization for 2 other system window types in addition to FNID_SWITCH
. There are more than just these system window types so it’d be worth exploring why only these ones were selected for initialization and if:
- Any other system window types still have the uninitialized variable vulnerability, or
- The other window types who now have initialization have any SetWindowLongPtr vulnerabilities.
Another interesting area for variant analysis would be in the task switching code. I originally attempted to patch-diff this vulnerability when the patch was first released, but ended up reverse engineering another bug in the task-switching code [blog post. There appears to be quite a bit of legacy code still in the module. With at least 2 bugs in it in 3 months, and one exploited in the wild, it seems like a ripe area for analysis.
Note: Project Zero did not perform either of these analyses.
Found variants: N/A
Structural improvements
This vulnerability was not exploited against the latest Windows release due to mitigations that have been added, such as Win32k lockdown for sandboxed code and they’ve mitigated the two ASLR bypass methods this exploit uses. This shows that Microsoft’s mitigations and structural improvements are working.
0-day detection methods
User applications that are creating system windows such as FNID_SWITCH
.
Other References
- December 2019: “Windows 0-day exploit CVE-2019-1458 used in Operation WizardOpium” by Kaspersky Lab
- March 2020: Root Cause Analysis and POC of CVE-2019-1458 by florek_pl
- March 2020: “TFW you-get-really-excited-you-patch-diffed-a-0day-used-in-the-wild-but-then-find-out-it-is-the-wrong-vuln” by Maddie Stone, process of attempting to patch-diff CVE-2019-1458, but instead analyzing another bug in win32k task switching*
- May 2020: “The zero-day exploits of Operation WizardOpium” by Kaspersky Lab