CVE-2020-27930: Safari RCE in Type 1 fonts handled by libType1Scaler.dylib

Posted by Mateusz Jurczyk and Sergei Glazunov, Project Zero (2021-02-04)

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

Exploit Sample:

Access to the exploit sample? Yes

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

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.

Is the exploit method novel?
Exploit method:  Still under analysis.

How do you think you would have found this bug? 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:

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 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.

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

Found variants: N/A

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.

Potential detection methods for similar 0-days: 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:

No comments:

Post a Comment