MacOS, iOS task threading was open to hijack
When Apple shipped its security bug-fixes earlier this week, one patch mostly passed under the radar.
Ian Beer of Google Project Zero, who found a deep-down vulnerability in the XNU kernel, first reported it to Apple in February this year, and it took until now to clean it up properly.
It took eight months, apparently, because of a basic architectural feature of the kernel: calling target functions directly instead of via the MIG IPC (Mach interface generator inter process communication) layer is fast, but “there’s no central point where access to a resource can be cut off”.
In this post, task_t considered harmful, Beer describes (in gloriously geek-out detail) a discovery that needed “a large refactor in MacOS 10.12.1 / iOS 10.1” to fix.
The TL;dr version is that the bugs offered at least two exploit types: privilege escalation, and sandbox escape.
How come? Because when a new program – of the type SUID binary – launches, old tasks and thread ports are invalidated, but the essence of the bug is in this Chromium Project Zero bug report:
“When a suid binary is executed it’s true that the task’s old task and thread ports get invalidated, however, the task struct itself stays the same.
There’s no fork and no creation of a new task.
This means that any pointers to that task struct now point to the task struct of an euid 0 process.”
Back to Beer’s more readable post: by creating a dangling task_t, an exploit can do things like writing to one IOKit process’s buffer from another process – and, as he demonstrates, that means one process can write to a more privileged process.
“Since this bug also allows us to gain any entitlements we want as well as root it’s easy to use it to defeat kernel code signing on OS X and load an unsigned kernel extension.
See the exploit for CVE-2016-1757 for one way to do this.”
“Every task_t pointer is a potential security bug”, Beer writes, because “there’s no locking mechanism to let you assert that the privileges of a task struct haven’t changed since you got access to it and just because kernel code got access to a task struct at one time doesn’t mean it should have access later.”
Apple went through two rounds of mitigations before this week’s fix, because, Beer notes, “This isn’t an easy bug class to fix … “There are task_t pointers everywhere”. ®