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.