CVE-2019-7286: iOS use-after-free in cfprefsd
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
- March 2019: "Analysis and Reproduction of iOS/OSX Vunerability: CVE-2019-7286" and "CVE-2019-7286 Part II: Gaining PC Control" by Zecops Research Team
- August 2019: Detailed, technical blog post series about these iOS exploit chains "A very deep dive into iOS exploit chains found in the wild"