[CVE-2016-1865] Some unexploitable but unsandboxable OS X/iOS Kernel NULL pointers
After examining qwertyoruiop’s tpwn poc and presentation I was wondering if Apple totally understood the root cause of that bug and if there were other similar issues.
It turned out that yes, there were other bugs.
One of them I found, which had maybe some chances of being exploitable on OS X without SMAP
, was patched in 10.11.4
.
In this post I will discuss one of the other unexploitable null pointers I disclosed to Apple after the 10.11.4
release, because surprisingly even after 2 very similar bugs fixed, Apple failed to eliminate all of them.
Those bugs can be pretty annoying, since they can at least panic the kernel from any context and sandbox.
Unfortunately on OS X, unlike other Operating Systems, kernel NULL pointers are still a problem if your machine doesn’t support SMAP
, since the NULL page under certain circumtances can be mapped, and if the bug allows it (like Luca’s tpwn kernel NULL pointer), it can be exploited.
Checkout Ian Beer’s issues in the Project Zero tracker for additional details.
MIG and IKOT_TASK:
Without going too much in details, since there are very good articles (from J Levin) onlines and books (the ones from Levin again and the one from Amit Singh), and as I mentioned, checkout Luca’s presentation. I will also take some shortcuts and use a more “free” language and try to explain the concepts instead of all the details, which you can eventually check in the code or in the mentioned references:
- MIG is a OS X / iOS IPC “higher level facility” of interfaces built upon mach messages and mach ports used to communicate between tasks, including with the kernel.
- MIG systems exports a well defined interface of methods you can remotely invoke.
- Those methods have a signature and parameters types. Since only some “primitive” parameters are understood by the IPC system, some of them must be validated and converted. The root cause of the bug resides in this last statement.
- What’s IKOT_TASK then? To understand this bug you just have to know that it’s a “type” of mach port. They are all mach ports but there are different flavors depending on what they represent. For example if you call
mach_task_self()
you get back a port of kind “task” if you callmach_thread_self()
you get back a port of kind “thread”.
One of the bugs
Take a look at this simple kernel method, which you can invoke from userspace with MIG like we said:
Notice that the first parameter it’s of type task_t
.
This type is not a “primitive” type, in a MIG sense, so it has to be converted. You can see this in the MIG interface definition file task.defs
with this procedure:
task_t
is defined in another mach_types.defs
file with the conversion function to convert it from a mach_port_t
:
So we have to check the “intran” (translator function from the mach_port_t
received to a task_t
) function convert_port_to_task(mach_port_t)
:
As you can see this function can potentially return a NULL pointer (TASK_NULL
) if the mach_port we pass to it it’s not of IKOT_TASK
. Even the comment says it can return a NULL.
But read then again the task_get_assignment
function.
As you can see there is no check if the parameter is null or not, so if it’s null we will crash in a read access from NULL page at: if (!task->active)
.
The bug it’s totally useless, but anyway I wanted to share this writeup to show to you that sometimes when a bug is fixed, other very similar bugs are still present in the code and unfixed. And also offers a quick tour of the MIG interface to the kernel.
POC
As you can see we pass to task_get_assignment
a mach_thread_self()
which is not IKOT_TASK
, triggering the null pointer.
The correct use (without the bug) of this API is to pass a mach_task_self()
or another task port.
Timeline:
- 2016/3/23 The issues are reported to Apple via email at
product-security@apple.com
with 90 days responsible disclosure policy. - 2016/5/27 Apple told me even if it’s not fixed in 10.11.5, the fix it’s scheduled for the next release, so holding public disclosure.
- 2016/7/17 CVE-2016-1865 is assigned and the fix is pushed in 10.11.6
Take aways:
- Sometimes vendors just fix the immediate problem and bug, and don’t investigate carefully about the root cause and search for additional bugs that share the same pattern.