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:

  • For Windows 10 1607 x64, KB4525236 and previous
  • For Windows 7 x64, KB4525233 and previous

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:

  1. For older builds, the Bitmap kernel addresses are leaked by the GdiSharedHandlerTable
  2. Creating an AcceleratorTable object and using gSharedInfoHandleTable to leak its kernel address, then freeing the AcceleratorTable 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:

  1. Any other system window types still have the uninitialized variable vulnerability, or
  2. 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