Ian Beer, Project Zero (Originally posted on Project Zero blog 2020-07-27)

The Basics

Disclosure or Patch Date: 7 February 2019

Product: Apple iOS

Advisory: https://support.apple.com/en-us/HT209520

Affected Versions: Exploit targeted iOS 12-12.1

First Patched Version: iOS 12.1.4

Issue/Bug Report: N/A

Patch CL: N/A

Bug-Introducing CL: N/A

Reporter(s): Clement Lecigne of Google's Threat Analysis Group (TAG), Ian Beer & Samuel Groß of Google Project Zero, & an anonymous researcher (according to Apple's release notes)

The Code

Proof-of-concept: N/A

Exploit sample: N/A

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

The Vulnerability

Bug class: use-after-free (UaF)

Vulnerability details:

cfprefds hosts the com.apple.cfprefsd.daemon XPC service. This service offers a number of operations, including a "multi-message" operation which allows bundling multiple individual operations into one larger message. The vulnerability is in the implementation of this multi-message handling.

The code allocates an array on the stack and fills it with raw pointers to each of the sub-messages inside the multi-message. It then invokes each message handler, passing a custom reply_handler block which rather than sending the reply, instead overwrites the raw sub-message pointer in the stack buffer with a retained reply message pointer.

The code fails to take into account the fact that not all message handlers invoke the reply_handler block, as not all messages send replies. This can leave some slots in the stack buffer containing raw pointers, and some containing retained pointers. xpc_release will be called on each of the entries, dropping an extra reference on the raw pointers which was never obtained leading to a UaF.

Patch analysis: N/A

Thoughts on how this vuln might have been found (fuzzing, code auditing, variant analysis, etc.):

A manual audit of the service should have found this bug; the dual use of the stack buffer is a sufficiently weird primitive that I think it would have stood out. A generic XPC fuzzer might be able to find it, but I don't think it's necessarily trivially fuzzable because you'd need well-formed XPC objects with the right dictionaries.

(Historical/present/future) context of bug:

This vulnerability was discovered and reported at the same time as CVE-2019-7287. Google's TAG discovered a cache of iOS exploit chains being used in the wild. CVE-2019-7286 and CVE-2019-7287 were the only two vulnerabilities that were still 0-days at the time of discovery. This vulnerability, CVE-2019-7286, is the sandbox escape that is paired with CVE-2019-7287, the kernel vulnerability.

The Exploit

Is the exploit method known? Yes

Exploit method:

This vulnerability is a sandbox escape. It runs as shellcode loaded in a sandboxed WebContent renderer process and gets ROP execution in cfprefsd, a more privileged process. They exploit the UaF using an XPC heap spray to fill the target heap with a fake objective C object with a fake selector cache. When a method is invoked on this object, it will trigger a ROP payload. The ROP stack opens an IOKit userclient mach port and sends it back to the renderer, allowing the shellcode to interact with the kernel driver and exploit a kernel bug (CVE-2019-7287).

The Next Steps

Variant analysis

Areas/approach for variant analysis (and why):

Manual XPC object lifetime management is tricky and there are a few APIs which can be unsafe. Looking for places where xpc_retain/xpc_release are called could be a good start, but they're probably quite common.

Found variants:

Structural improvements

0-day detection methods

A memory sanitizer tool would detect 0-days like this.

Other References