ZDI-CAN-12671: Windows Kernel DoS/Privilege Escalation via a NULL Pointer Deref
January 28, 2021 | Simon ZuckerbraunThis blog details a NULL pointer dereference in the Windows win32kfull.sys
kernel-mode graphics module discovered by ZDI contributor Marcin Wiązowski. It can be used to create a denial-of-service condition. In limited circumstances, it can be used for privilege escalation, though if modern mitigations are present privilege escalation will not be possible. Due to the limited impact, Microsoft has made the decision not to service this bug report. As such, we disclosed the vulnerability as a 0-day according to our policy. This article details the vulnerability, ZDI-CAN-12671, and explains its impact.
The Vulnerability
In win32k, any drawing operation is performed upon an abstracted drawing surface (“surface”) represented in the kernel by a SURFOBJ
structure:
Two fields are highlighted above. The field hdev
is a handle to a particular device driver. The field flags
is partially undocumented, but some of the flags that it can contain are the HOOK_*
flags documented here. Each HOOK_*
flag indicates that a particular graphics primitive should be delegated to the device driver specified by hdev
. For example, HOOK_BITBLT
indicates that every BitBlt drawing operation performed on the surface should be delegated to the appropriate DrvBitBlt
primitive offered by the device driver.
The bug is found in the function win32kfull.sys!BLTRECORD::bRotate
, specifically in the one that takes four parameters. Within this function, it performs a PlgBlt
drawing operation on a surface. If HOOK_PLGBLT
is set in the flags
field of the SURFOBJ
, it delegates to the underlying device driver’s DrvPlgBlt
, as explained above. The problem, though, is that it fails to check whether the driver specified by hdev
actually offers a native DrvPlgBlt
. If no such function is offered by the driver, the corresponding entry in the driver’s function table will be NULL, and win32kfull.sys!BLTRECORD::bRotate
will attempt to perform a call to the NULL address.
The various HOOK_*
flags can be set from user mode by calling gdi32!EngAssociateSurface
. There are some additional details involved in preparing a surface for exploiting this bug, but those are secondary to the vulnerability and are beyond the scope of this article.
Exploitation Potential
To exploit this, the first thing needed is a graphics output device driver that does not export a DrvPlgBlt
function. One such driver is the multi-monitor driver implemented in win32kfull
itself. The exported functions of this driver are recognizable by the Mul
prefix in their names, for example, win32kfull!MulBitBlt
. Notably for our purposes, there is no win32kfull!MulPlgBlt
. This device driver is available on any system with multiple active monitors.
Without further preparation, triggering the vulnerability produces a branch to address 0 in kernel mode, crashing the system.
Is it possible to exploit this bug for greater impact, such as a kernel escalation of privilege? Yes, but there are significant preconditions that drastically restrict when it is possible:
- It must be possible to map the NULL page and place executable code there. On currently-supported Windows systems, mapping the NULL page is not possible from an unprivileged user-mode process. There is one known exception, though: The NULL page can still be mapped in a 16-bit process. 16-bit processes can be created only if the NTVDM subsystem is installed. Note that a non-administrator cannot install the NTVDM subsystem, but if this subsystem has already been installed by an administrator, it can be utilized afterward by a non-privileged user. NTVDM is available only on 32-bit installations of Windows.
- Even if a user-mode process maps a page of executable memory at address 0, this page will be executable in user mode only but will not be executable in kernel mode. This is due to SMEP [PDF]. Kernel execution at access 0 can be achieved only on processors that do not offer the SMEP mitigation, or by disabling SMEP via processor control register CR4.
In summary, privilege escalation is possible only on a 32-bit installation of Windows, with NTVDM installed, and where the processor does not offer the SMEP mitigation. However, it should be noted that these conditions may be relaxed if the attacker has knowledge of additional vulnerabilities that can be exploited for SMEP bypass or NULL page mapping. In his submission, Marcin did include working proof-of-concept code that demonstrates privilege escalation under a specific set of circumstances. While the risk to users is small, it is not zero. It is our hope Microsoft reconsiders and produces a patch to address this bug in the future.
You can find me on Twitter at @HexKitchen, and follow the team for the latest in exploit techniques and security patches.