Talos Vulnerability Report

TALOS-2020-1016

Microsoft Windows 10 Kernel SetMapMode MM_HIENGLISH information disclosure vulnerability

March 10, 2020
CVE Number

CVE-2020-0791

Summary

An exploitable information disclosure vulnerability exists in the kernel of Microsoft Windows 10 Insider Preview Fast and Stable. A specially crafted executable can cause an out-of-bounds read, resulting in information disclosure. To trigger this vulnerability, the attacker needs to execute a specially crafted executable.

Tested Versions

Microsoft Corporation Windows 10 Kernel Insider Preview Fast
Microsoft Corporation Windows 10 Kernel Stable

Product URLs

https://www.microsoft.com/en-us/

CVSSv3 Score

7.3 - CVSS:3.0/AV:L/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:H

CWE

CWE-125 - Out-of-bounds Read

Details

With the lockdown of user-space application capabilities, Windows kernel's attack surface presents a popular target for local privilege escalation exploits.

A vulnerability exists in the way window sizes are calculated. Triggering this vulnerability can be observed with special pool enabled in verifier for drivers from win32k.

While executing the supplied PoC, Windows kernel crashes inside vStrWrite01 from win32kfull. Pseudocode of this function is as follows:

  if ( a2 )
{
  v4 = (struct _STRRUN *)((char *)a1 + 8);
  v52 = (struct _STRRUN *)((char *)a1 + 8);
  if ( !a4 )
  {
    v5 = a3;
    v6 = *((_DWORD *)a3 + 12) + *((_DWORD *)a3 + 13) * *(_DWORD *)a1;
    v50 = *((_DWORD *)a3 + 12) + *((_DWORD *)a3 + 13) * *(_DWORD *)a1;
    while ( 1 )
    {
      if ( v4 == a2 )
        return;
      v56 = *(_DWORD *)v4;
      v60 = *(_DWORD *)v4 + *((_DWORD *)v4 + 1);
      v7 = *(_DWORD *)v4 & 0x1F;
      v8 = a1;
      v9 = (int *)(v6 + 4 * (*(int *)v4 >> 5));
      v10 = *v9;                                                                    [1]
      v66 = v9;
      v53 = v9;
      v44 = *v9;

A crash occurs due to an out of bounds dereference at [1].

A call to vStrWrite01 is made from win32kfull!EngStretchBltNew at line [2] bellow. Based on reverse engineering, the function EngStretchBltNew is used to calculate the bounds of windows within RECT structure.

From MSDN's description:

"The StretchBlt function copies a bitmap from a source rectangle into a destination rectangle, stretching or compressing the bitmap to fit the dimensions of the destination rectangle, if necessary. The system stretches or compresses the bitmap according to the stretching mode currently set in the destination device context."

When the verifier is disabled, the code executes normally until line [3]. However, with verifier enabled, we can observe a crash:

    while ( (int)v152 < v184.bottom )
                                              {
                                                v100 = *(HSURF *)(*(_DWORD *)(v164 + 24) + 4 * (_DWORD)v159);
                                                v95->hsurf = v100;
                                                if ( v100 )
                                                {
                                                  ms_exc.registration.TryLevel = 0;
                                                  v101 = ((int (__stdcall *)(unsigned int, struct _SURFOBJ *, SURFOBJ *, int, struct _SURFOBJ *, LONG, LONG, int, int, unsigned int))sizl)(
                                                           v164,
                                                           v95,
                                                           pso,
                                                           v158,
                                                           v166,
                                                           v184.left,
                                                           v184.right,
                                                           v123,
                                                           v99,
                                                           v119 != 0 ? (unsigned int)v173 : 0);
                                                  ((void (__stdcall *)(struct _SURFOBJ *, int))v120)(v95, v101);                                                [2]
                                                  ms_exc.registration.TryLevel = -2;
                                                  if ( v135 )
                                                    vInitBuffer(Pattern, (struct _RECTL *)v105, (unsigned int)v106);
                                                }                                                                                                                                                           [3]
                                                else if ( v135 )
                                                {
                                                  ms_exc.registration.TryLevel = 1;
                                                  ((void (__stdcall *)(unsigned int, struct _SURFOBJ *, SURFOBJ *, _DWORD, struct _SURFOBJ *, LONG, LONG, _DWORD))sizl)(
                                                    v164,
                                                    v95, 
                                                    pso,
                                                    0,
                                                    v166,
                                                    v184.left,
                                                    v184.right,
                                                    0);
                                                  ms_exc.registration.TryLevel = -2;
                                                }

In order to reach a vulnerable state, a call to SetMapMode function needs to be made.

SetMapMode(var, MM_HIENGLISH);

From MSDN:

"The SetMapMode function sets the mapping mode of the specified device context. The mapping mode defines the unit of measure used to transform page-space units into device-space units, and also defines the orientation of the device's X and Y axes."

SetMapMode changes the units used for calculation of RECT structure to MM_HIENGLISH.

From documentation:

"MM_HIENGLISH - Each logical unit is mapped to 0.001 inch. Positive x is to the right; positive y is up... This is useful for applications drawing in physically meaningful units (such as inches or millimeters)."

This results in an inconsistency with calculation of window dimensions which is measured in pixels.

During while loop in vStrWrite01 next values are read, and at some point at [1], value is read from outside the range which results in memory corruption when verifier is enabled. This constitutes an out of bounds read and it is possible that the attacker could use this vulnerability to disclose contents of sensitive memory with control over the _STRRUN structure.

Timeline

2020-02-19 - Vendor Disclosure
2020-03-10 - Public Release

Credit

Discovered by Marcin Towalski of Cisco Talos.