Mateusz Jurczyk and Sergei Glazunov, Project Zero (Originally posted on Project Zero blog 2021-02-04)

The Basics

Disclosure or Patch Date: 5 November 2020

Product: Apple Safari


Affected Versions: iOS 14.1 and previous, macOS 10.15.6 and previous

First Patched Version: iOS 14.2 and macOS 10.15.7

Issue/Bug Report:

Patch CL: N/A

Bug-Introducing CL: N/A

Reporter(s): Mateusz Jurczyk & Sergei Glazunov of Google Project Zero

The Code


Exploit sample: N/A

Did you have access to the exploit sample when doing the analysis? Yes

The Vulnerability

Bug class: Stack-Based Out-of-Bounds Read/Write Access

Vulnerability details: There is a stack-based out-of-bounds read/write condition due to incomplete input validation to ensure a user-supplied integer is positive, not negative.

Apple macOS and iOS use libType1Scaler.dylib as their interpreter for the legacy Type 1 PostScript font format. This library can be reached with user-controlled data via the Apple Safari web browser. The libTyp1Scaler.dylib'DoType1InterpretCharString function is the main interpreter of the PostScript glyph outline programs. One of the supported instructions is callothersubr, as defined on page 55 of the Type 1 spec. It expects the following syntax:

arg_1 . . . arg_n n othersubr# callothersubr

According to the specification, the instruction is supposed to call into a custom font-defined PostScript procedure identified by the othersubr# index and pass n arguments (arg_1, ..., arg_n) to it. In practice, there is a set of othersubrs that have predefined meaning and are implemented by the interpreter natively (e.g. 0-4, 6, 12, 13, ..., 30) and for all others, the library silently ignores them by removing n values from the operand stack and continuing execution. However, there is a serious security vulnerability caused by the incorrect handling of negative values of n.

For non-standard othersubrs, the pseudo-code below will be executed

int n = POP(); // user-controlled

op_sp -= n;
if (op_sp < &op_stk[0]) {
 // bail out;

The new stack pointer is properly sanitized against the bottom of the stack (which is important for positive n), but the interpreter fails to verify that op_sp doesn't go beyond &op_stk[63] for a negative n. Once the op_sp pointer goes out of bounds, the Type 1 program gets access to the native stack frame of the current function and its callers, including return addresses, various pointers to data, code, etc. Coupled with the ability to execute conditional logic and arithmetic operations through the CharString operators, it is straightforward from this point to construct an arbitrary read/write primitive, build a ROP chain directly on the stack, or execute arbitrary code by other means.

Patch analysis: N/A

Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.): I find it most likely that the bug was found via reverse engineering and manual code audit, as Type 1 CharString interpreters are mostly self-contained and easy to reason about. Pointer arithmetic issues in such code are relatively easy to spot and trigger manually, but harder to identify through fuzzing. It's also possible that it was recognized as a variant of similar bugs found in equivalent Type 1 font engines in the past: e.g. the BLEND vulnerability in Microsoft and Adobe products.

(Historical/present/future) context of bug:

The Apple libType1Scaler.dylib code dates back to the early 1990's and shares a common ancestor with the PostScript font engines used in Microsoft Windows (ATMFD, fontdrvhost) and Adobe (CoolType) software. It may therefore share some of the same bugs that have been fixed in other forks of the code throughout the years. Many of the font exploitation techniques discussed historically also apply to Apple's library.

The Exploit

(The terms exploit primitive, exploit strategy, exploit technique, and exploit flow are defined here.)

Exploit strategy (or strategies): Still under analysis

Exploit flow: Still under analysis

Known cases of the same exploit flow: Still under analysis

Part of an exploit chain? This vulnerability was used as a part of an iOS exploit chain. It was used as the Safari RCE and chained with a kernel info leak (CVE-2020-27950) and a kernel privilege escalation (CVE-2020-27932).

The Next Steps

Variant analysis

Areas/approach for variant analysis (and why): Perform a complete audit of the CharString interpreter implemented in libType1Scaler.dylib.

Found variants:

  • CVE-2020-27943: Apple CoreText libType1Scaler.dylib heap buffer overflow in Counter Control hints
  • CVE-2020-27946: Apple CoreText libType1Scaler.dylib memory disclosure via uninitialized array
  • CVE-2020-29624: Apple CoreText libFontParser.dylib stack corruption in the handling of /BlendDesignPositions Type 1 objects
  • CVE-2020-27944: Apple CoreText libType1Scaler.dylib heap out-of-bounds write due to integer overflow in STOREWV othersubr

Structural improvements

  • Insert extra "safety nets" in the main CharString interpreter function to guarantee that even if the stack pointer is shifted out of bounds, subsequent PostScript operators cannot operate on it.
  • Remove support for unused, deprecated Type 1 font features in libType1Scaler.dylib to reduce the effective attack surface.
  • Deprecate the libType1Scaler.dylib library entirely or make it non-reachable from the context of typical attack vectors (e.g. Safari).
  • Move the font parsing code into a separate, sandboxed worker process in macOS and/or iOS.

0-day detection methods

Type 1 fonts are many decades old and have been long superseded by more efficient and advanced formats. They are exceedingly rare nowadays, and even more so in the context of web browsing where only formats such as TTF, OTF, WOFF and WOFF2 are officially supported. As a result, any Type 1 fonts embedded in websites or dynamically loaded in JavaScript are highly suspicious, especially if they include non-standard features such as Multiple Masters or unusual instructions like callothersubr.

Other References

  • List of bugs reported by Project Zero in Microsoft and Adobe Type 1 Interpreters in 2014/2015
  • July 2015: A series of blog posts about the exploitation of the BLEND Type 1 operator vulnerabilities (CVE-2015-0093, CVE-2015-3052), similar in nature to the callothersubr issue discussed here