An introduction to exploiting userspace race conditions on iOS

Let’s walk through the discovery and exploitation of CVE-2018-4331, a race condition in the com.apple.GSSCred XPC service that could be used to execute arbitrary code inside the GSSCred process, which runs as root on macOS and iOS. The exploit, gsscred-race, targets iOS 11.2, although versions up through iOS 11.4.1 are vulnerable. This post will show how I discovered the bug, how I analyzed its exploitability, and how I developed a JOP program that allowed me to take control of the process.

Bypassing platform binary restrictions with task_threads()

Because task ports have been abused in so many exploits over the years, Apple decided to add a mitigation that protects platform binaries (i.e., binaries with an Apple code signature) from being modified by non-platform binaries via task ports. However, there was a significant limitation to this design: an API called task_threads() that would return the thread ports for all the threads in the task. In this post, we’ll look at the mitigation, the workaround, and implications for exploitation. My threadexec library uses this technique to achieve code execution in platform binaries via a task or thread port on macOS and iOS.

iOS privilege escalation via crashing

Among the vulnerabilities fixed in iOS 11.4.1 and macOS 10.13.6 is CVE-2018-4280, a Mach port replacement issue in launchd that was very similar to CVE-2018-4206. This vulnerability could be exploited to impersonate system services, at which point it is possible to escape the sandbox and elevate privileges.

I developed an exploit called blanket for this vulnerability earlier this year. The exploit achieves code execution inside of ReportCrash, which is a highly privileged process, and then uses these new privileges to disable codesigning and spawn a bind shell. All of this is achieved without compromising the kernel in any way. (Sometimes the easiest way to win is not to play.) Even though the vulnerability was only fixed in iOS 11.4.1, the exploit is specific to iOS 11.2.6 and will need adjustment to work on later versions.

Reading process memory using XPC strings

This is a short post about another bug I discovered mostly by accident. While reversing libxpc, I noticed that XPC string deserialization does not check whether the deserialized string is actually as long as the serialized length claims: it could be shorter. That is, the serialized XPC message might claim that the string is 1000 bytes long even though the string contains a null byte at index 100. The resulting OS_xpc_string object will then think its C string on the heap is longer than it actually is.

While directly exploitating this vulnerability to execute arbitrary code is difficult, there’s another path we can take. The length field of an OS_xpc_string object is trusted when serializing the string into a message, so if we can get an XPC service to send us back the string it just deserialized, it will over-read from the heap C-string buffer and send us all of that extra data in the message, giving us a snapshot of that process’s heap memory. The resulting exploit primitive is similar to how the Heartbleed vulnerability could be used to over-read heap data from an OpenSSL-powered server’s memory.

Analyzing the iOS 12 kernelcache's tagged pointers

Not long after the iOS 12 developer beta was released, I started analyzing the new kernelcaches in IDA to look for interesting changes. I immediately noticed that ida_kernelcache, my kernelcache analysis toolkit, was failing on the iPhone 6 Plus kernelcache: it appeared that certain segments, notably the prelink segments like __PRELINK_TEXT, were empty. Even stranger, when I started digging around the kernelcache, I noticed that the pointers looked bizarre, starting with 0x0017 instead of the traditional 0xffff.

It appears that Apple may be making significant changes to the iOS kernelcache on some devices, including abandoning the familiar split-kext design in favor of a monolithic Mach-O and introducing some form of static pointer tagging, most likely as a space-saving optimization. In this post I’ll describe some of the changes I’ve found and share my analysis of the tagged pointers.

How to build an iOS command line tool with Xcode 9.3

While working on an upcoming project, I found that I needed to create a standalone Mach-O executable that would share code (possibly a lot of code) with an iOS application managed through Xcode. The iOS app would exploit a system vulnerability, elevate privileges, and then spawn the standalone binary as a payload. However, I didn’t want to have to build the payload binary on the command line: not only does that introduce friction to the build flow, but also it means managing the same shared code files in two separate build systems. Thus, I decided to figure out how to add a standalone iOS executable target in Xcode.

I couldn’t find an up-to-date reference online for how to make Xcode build a standalone Mach-O executable for iOS, so after figuring out way to do it, I decided to write this guide. In this post we will create an Xcode project that mimics the macOS command line tool project type. You can also use my ios-command-line-tool project as an example.

Designing an advanced kernel function call primitive on iOS

In this post I’m going to describe a technique for calling arbitrary kernel functions on iOS that extends a 2-argument function call primitive to support up to 14 64-bit arguments. This is a refinement of memctl’s first call strategy, which extends a 6-argument function call primitive into an 8-argument call. My goal is to shed light on how I design jump-oriented programs, and hopefully provide a reference for others looking to design similar programs.

I first developed the 14-argument kernel function call strategy in November of last year, and then introduced an updated variant at the end of January. You can see the implementations in the files call_strategy_3.c and call_strategy_5.c. This post will examine call strategy 5 since this strategy works on the iPhone 8 running iOS 11.1.2, which at the time of writing is the most recent platform with a public kernel exploit.

Who put that kernel pointer in my crash log?

On February 26th, while looking at an iOS application crash log, I noticed a distinctive value in register x18:

Register x18 in this NYTimes app crash log contains a kernel pointer.

This crash log from the New York Times app contains a kernel pointer in register x18.

Register x18 contained a value prefixed by 0xfffffff0 (7 fs and a 0), which is the telltale signature of a kernel pointer. I almost dismissed this as a coincidence, but decided to check some other crash logs as well. To my surprise, all of them contained the same value in register x18, even across different apps. This suggested a true kernel information leak, and a serious one. What was going on? The answer would lead back, of all places, to the Meltdown vulnerability.

Reconstructing C++ classes in the iOS kernelcache using IDA Pro

Last October I released ida_kernelcache, an IDA Pro toolkit for analyzing iOS kernelcache files. My goal was to make working with kernelcaches in IDA a bit easier by improving segment names, automatically converting some pointers into offsets, symbolicating virtual methods and virtual method tables, and automatically renaming stub functions in kexts. Today, I’m releasing what I’ve found to be the most useful part of the toolkit thus far: automatically reconstructing class layouts and C structs via data flow analysis.

CVE-2017-13868: A fun XNU infoleak

Way back in October of 2017, I discovered CVE-2017-13868, a kernel information leak in XNU that was quite fun to analyze and exploit. While browsing the XNU source code, I noticed that the function ctl_ctloutput didn’t check the return value of a call to sooptcopyin. This immediately caught my attention because error checking in the kernel is very important: poor error checking is a frequent source of security bugs. In this case, failing to check the return value opened a race window that could allow a privileged process to read an arbitrary amount of uninitialized kernel heap data.

Live kernel introspection on iOS

Part of effective security research is having the right tools to analyze vulnerabilities. Apple allows users to develop kernel extensions and debug the kernel on macOS, but neither is supported on iOS. This post explains how I developed memctl, a kernel introspection tool for macOS and iOS that I’ve been using for the past year to analyze the kernel.

Memctl uses the kernel task port to reliably read and write kernel memory and to reliably call arbitrary kernel functions with arbitrary arguments on both macOS and iOS. Other useful features are implemented on top of this basic functionality, mostly convenience routines to call kernel functions that would otherwise be difficult to find or call. Memctl’s functionality is provided both as a library (called libmemctl) and as a command-line tool.

Coincidentally, Ian Beer described how he developed his own kernel memory debugger in Exception-oriented exploitation on iOS, which was published late into my work on memctl. To me this shows how useful such a tool could be. While I developed memctl primarily for my own use, I am open-sourcing it in case someone else finds my work useful.

physmem: Accessing Physical Memory from User Space on OS X

Late in 2015 I was looking for a way to create an instance of an IOKit user client with a visible NULL pointer dereference when I discovered something intriguing: the default implementation of IOService::newUserClient checks the IOUserClientClass property on the service when determining what user client class to allocate. This caught my attention because IOKit provides an API to set arbitrary properties on an IOService from user space. If any IOService allowed setting the IOUserClientClass property, that would create an opportunity for kernel code execution.

I immediately started looking for setProperty calls with attacker-controlled keys and values. Amazingly, I found that IOHIDevice would iterate the attacker-supplied properties dictionary and indiscriminately add each key-value pair to its own set of properties. This post is about how I leveraged this vulnerability to gain read/write access to physical memory from user space, and how this awesome primitive can be used to get fully reliable kernel code execution.

I reported this issue to Apple in January of 2016, and it was assigned CVE-2016-1825. It was fixed in OS X El Capitan 10.11.5. A proof-of-concept exploit for this vulnerability (and the variant CVE-2016-7617) is available in my physmem repository on GitHub. This vulnerability is not present on iOS.

Mac OS X Privilege Escalation via Use-After-Free: CVE-2016-1828

Among the bugs that Apple patched in OS X 10.11.5 is CVE-2016-1828, a use-after-free I discovered late last year while looking through the kernel source. Combined with CVE-2016-1758, an information leak patched in 10.11.4, this vulnerability can be used to execute arbitrary code in the kernel. In this post I’ll document how I created rootsh, a local privilege escalation for OS X 10.10.5 (14F27).

CVE-2016-1828 is a use-after-free in the function OSUnserializeBinary. By passing a crafted binary blob to this function, it is possible to invoke a virtual method on an object with a controlled vtable pointer. I leveraged the use-after-free to create a NULL pointer dereference, allowing the vtable and the ROP stack to live in user space.

CVE-2016-1758 is a kernel stack disclosure in the function if_clone_list. 8 bytes of uninitialized kernel stack are copied to user space. Those bytes can be initialized to a known location within the kernel text segment by invoking a system call prior to triggering the disclosure. After leaking the text segment pointer, the kernel slide can be computed by subtracting the base address of that particular text segment location from the leaked address.