Tuesday, October 25, 2016

task_t considered harmful

Posted by Ian Beer, Project Zero

This post discusses a design issue at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 core of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 XNU kernel which powers iOS and MacOS. Apple have shipped two iterations of mitigations followed yesterday by a large refactor in MacOS 10.12.1/iOS 10.1. We’ll look at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bugs, how cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can be exploited to escape sandboxes and escalate privileges, and how we can defeat each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mitigations. Every step is accompanied by a working exploit.

Some background on mach ports

Mach ports are multiple-sender, single-receiver message queues maintained by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. Some special mach ports provide cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same message-passing API to userspace but messages sent to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m are handled synchronously by kernel message handlers. In this sense messages sent to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se ports are quite a lot like syscalls.

Task ports are an example of this kind of port. They handle messages which allow senders to manipulate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual memory of a task and gain access to its threads. Each task (process) has its own task port. MIG is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 name of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 tool used to generate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 serialization code used by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se kernel-owned message ports.

A low-level look at IOKit

When you create a new IOKit user client in userspace you normally call this method from IOKitLib:

kern_return_t
IOServiceOpen(
 io_service_t service,
 task_port_t owningTask,
 uint32_t type,
 io_connect_t *connect );

IOServiceOpen calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MIG generated serialization code for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 io_service_open_extended IPC method and sends that serialized message to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 provided IOService port. The mach_msg mach trap notices that this port is owned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel and calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct kernel MIG handler for this message racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than queuing it onto cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port’s message queue.

The task port passed here is called owningTask; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same name is used throughout cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userspace and kernel code. This name was cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first thing which made me suspicious. OwningTask implies an ownership relationship which might lead kernel extension developers to believe that behind cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 scenes IOKit is actually maintaining an ownership relationship which will ensure that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of this userclient will always be dominated by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 lifetime of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask. This is a dangerous assumption, and this blog post is really cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fallout from questioning this assumption. Let’s keep following cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 flow of this code into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel. Here’s a snippet from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel-side MIG deserialization code for io_service_open_extended:

mig_internal novalue _Xio_service_open_extended(
 mach_msg_header_t *InHeadP,
 mach_msg_header_t *OutHeadP)
{
...
 owningTask = convert_port_to_task(In0P->owningTask.name);

 RetCode = is_io_service_open_extended(
             service,
             owningTask,
             In0P->connect_type,
             In0P->ndr,
             (io_buf_ptr_t)(In0P->properties.address),
             In0P->propertiesCnt, &OutP->result, &connection);

 task_deallocate(owningTask);
...

The kernel has already copied-in all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rights contained in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 message so In0P->owningTask.name is actually a pointer to a struct ipc_port and not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach port’s name as seen from userspace.

Here’s convert_port_to_task:

task_t
convert_port_to_task(
 ipc_port_t port)
{
 task_t task = TASK_NULL;

 if (IP_VALID(port)) {
   ip_lock(port);
   if (ip_active(port) &&
       ip_kotype(port) == IKOT_TASK)
   {
     task = (task_t)port->ip_kobject;
     assert(task != TASK_NULL);
     task_reference_internal(task);
   }
   ip_unlock(port);
 }

 return (task);
}

This checks that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port argument really is a task port object cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n takes a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task by calling task_reference and returns cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task_t pointer. task_t is a typedef for a pointer to a struct task and as you can see from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code it’s a reference-counted object.

is_is_service_open_extended doesn’t do anything with owningTask ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than passing it to ::newUserClient:

 res = service->newUserClient(
   owningTask,
   (void *) owningTask,
   connect_type,
   propertiesDict,
   &client );

newUserClient is an IOService method which can be overridden by an IOService if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y want to offer multiple userclient types. Ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rwise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 default implementation will look up cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOService’s IOUserClient subclass class name in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOKit registry, allocate it via IOKit’s reflection API and call its ::initWithTask method. The default implementation of ::initWithTask also doesn’t do anything with owningTask.

Having looked through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code this far it looks like it’s not cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 case that by default cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask is going to hold a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient (which would prevent cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient taking a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task and causing a reference cycle.) In fact it’s clearly quite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 opposite; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient must take a reference on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask if it wishes to keep a reference to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no implicit ownership relationship at all.

Checking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 docs

There aren’t a whole lot of resources for writing OS X kernel extensions. Apple does publish a sample kext on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir developer site called AppleSamplePCI which provides examples of various IOKit design patterns. Here’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AppleSamplePCI.kext implementation of initWithTask:

bool SamplePCIUserClientClassName::initWithTask(
 task_t owningTask,
 void* securityID,
 UInt32 type,
 OSDictionary* properties)
{
 bool success = super::initWithTask(owningTask,
                                    securityID,
                                    type,
                                    properties);
 fTask = owningTask;
 fDriver = NULL;

 return success;
}

The sample userclient stores cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask argument in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fTask member variable without taking a reference. Without that reference cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no guarantee that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct pointed to by fTask hasn’t been freed after this method returns. Looking through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 rest of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 sample kext we can see that some external methods use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fTask pointer to create memory descriptors - if we can get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct pointed to by fTask to be freed cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se will be using a dangling pointer.

Opening up a handful of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r OS X kexts in IDA it’s pretty clear that lots of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m follow this anti-pattern of holding a task_t pointer without taking a reference.

Creating a dangling task_t

Mach messages provide very flexible and powerful IPC building blocks. One of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 neat things you can do is send ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes send-rights to mach ports for which you hold send or receive rights.

Because task ports give you complete control over ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r tasks cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 api to request cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port for anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task (task_for_pid) is privileged but since all tasks have send rights to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own task ports if we have code execution in two tasks we can send a send-right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 second task’s task port to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first.

In this case we’ll use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 technique outlined by Robert Sesek to create a shared mach port between a parent and forked child by stashing a send-right in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bootstrap_port special port slot. After cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 fork cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child can recover this stashed port, restore cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bootstrap port and set up a bi-directional IPC channel over which it can send its task port back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent.

Triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 UaF in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 vulnerable anti-pattern looks like this:

  • parent forks off a child
  • child sends its task port back to its parent
  • child spins
  • parent receives child’s task port and creates a vulnerable IOKit userclient passing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child’s task port as owningTask
  • parent destroys its send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child’s task port
  • parent kills child, freeing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child
  • parent has a userclient with a dangling task struct pointer

A first exploit

Looking through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOKit drivers which had this bug, one jumped out as being particularly interesting - IOSurfaceRootUserClient. Here’s what cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Apple developer docs have to say about IOSurface:

The IOSurface framework provides a framebuffer object suitable for sharing across process boundaries. It is commonly used to allow applications to move complex image decompression and draw logic into a separate process to enhance security.

In reality IOSurfaces are just wrappers around shared memory buffers. On OS X we can talk to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurface kernel extension from inside cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Safari renderer sandbox and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 Chrome GPU sandbox, amongst ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365rs.

The IOSurfaceRootUserClient class has exactly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same anti-pattern as we saw in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 AppleSamplePCI client where cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient stores a copy of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask pointer as a member variable without taking a reference. Some reversing tells us that external method 0 of IOSurfaceRootUserClient is create_surface which takes a dictionary of key-value parameters used to create a shared memory object that ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes can map into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir address spaces. By passing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following keys and values we can get IOSurfaceRootUserClient to wrap existing userspace pages in an IOSurface racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than allocating a new buffer:

 IOSurfaceAddress:   base_address
 IOSurfaceAllocSize: size
 IOSurfaceIsGlobal:  true

The IOSurface object actually just wraps an IOMemoryDescriptor which is allocated in IOSurface::allocate by calling:

IOMemoryDescriptor *
IOMemoryDescriptor::withAddressRange(
 mach_vm_address_t address,
 mach_vm_size_t length,
 IOOptionBits   options,
 task_t         task);

The final task_t task argument to IOMemoryDescriptor::withAddressRange defines which task’s virtual memory cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 descriptor should be created for. IOSurface passes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 member variable storing its copy of owningTask here, on which it doesn’t hold a reference! If we can get that task struct memory to be freed (by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original task exiting), reallocated (by anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task starting) and used as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct for a more privileged task cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n this IOMemoryDescriptor will believe it’s wrapping a portion of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 current process’s address space when it’s actually wrapping a portion of that ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r more privileged task’s virtual memory in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOMemoryDescriptor which backs this IOSurface.

Setting IOSurfaceIsGlobal=true makes that surface available to ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r processes so that by calling external method 6 (lookup_surface) on anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r IOSurfaceRootUserClient created with our own legitimate task port as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 owningTask we can build a primitive which allows us to map arbitrary portions of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process’s address spaces into our own :-)

Since cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOMemoryDescriptor is actually creating shared memory mappings of those pages we can write to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m and those writes will also be reflected in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process. IOSurfaceRootUserClient doesn’t allow us to map executables pages from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 victim but we can still map for example cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __DATA segment of libraries. This is made easier by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 shared library cache being at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same virtual address in all processes.

Putting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit togecá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r

We need a way to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct reused by a more privileged process and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we need something to overwrite in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target to get code execution.

Task structs are allocated from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365ir own kernel heap zone which greatly simplifies things. We can just kill cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child and fork and exec a few suid-root binaries and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y are very likely to re-use cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same memory pointed to by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 dangling task_t.

For cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overwrite target I chose to target cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __cleanup pointer in libc. This will be called when cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process exits.  We can play a few tricks to block cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary just before it exits by setting its stderr file descriptor to a full pipe and forcing it to write an error message giving us plenty of time to exploit cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent process and overwrite cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 __cleanup pointer before emptying cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 pipe in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent. I chose to point cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 function pointer to a gadget which adds a large constant to RSP and returns. Doing this moves cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack pointer up into argv and since we exec’ed this binary I put a simple ROP stack cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re to call setuid(0) and execve /bin/bash. The ROP payload is prefixed with a large number of ret-slide gadgets so it should be stable across most versions of OS X.

You can download this exploit and check out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original bug report.

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 cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit for CVE-2016-1757 for one way to do this.

Although this exploit uses fork and execve cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y aren’t actually required - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 only prerequisite for triggering cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug is that you need code execution in two co-operating processes which can send mach messages to each ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r, and for this particular bug to be able to talk to IOSurface. Damien DeVille has a blog post discussing ways of achieving this from within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 app sandbox on iOS using application groups. It’s also not necessary to exec a suid-root binary: we could cause cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 freed task struct to be reused by anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r more privileged task by looking up a mach service via launchd or deliberately crashing and causing launchd to run cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 CrashReporter.

Many individual instances of this bug were fixed in OS X 10.11.6/iOS 9.3.3 and Apple shipped a mitigation to prevent passing ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task’s task ports to certain IOKit methods.

Stepping back

This use-after-free bug is quite fun but it obscures a far deeper and more concerning issue. If cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient now calls task_reference() on owningTask, and owningTask has to be cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original creator of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 userclient, is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re still a bug?

Earlier this year osxreverser@ and I both independently published research about a problem with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execve syscall. In that case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re was a race condition due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 order in which execve performed certain operations when loading a suid binary which left a small race window between cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new memory map being created and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port being invalidated.

There’s a far more fundamental problem: cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execve syscall doesn’t actually create a new task struct, even when it executes a more privileged suid binary. It just modifies cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 existing task struct in-place and any objects which previously had a task_t pointer now have one to a more privileged task.

This isn’t temporal memory safety - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no use-after-free involved. Lets look in detail at why that’s such a large problem for XNU.

XNU’s Neicá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r Unix Nor Mach

In a pure Mach microkernel invalidating cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port would be sufficient to prevent any ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r process from maintaining control of a task across a privilege-escalating exec, but XNU isn’t a microkernel. Earlier we looked at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel function convert_port_to_task which takes a mach task port and converts it into a task struct pointer. This pointer can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n be used and passed around within cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel without all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 overhead of sending messages. For example when IOKit wants to manipulate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 virtual memory of a process, racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than having to send a mach message to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mach_vm MIG subsystem (which it could cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365oretically do) it instead directly calls cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 responsible kernel function.

Anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r way to think of this is that all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MIG subsystems which live in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel (IOKit, mach_vm, tasks, threads, semaphores etc) are directly linked against each ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r. They can simply call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target functions racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than going via cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MIG IPC layer. This is obviously massively faster, but comes at a cost.

Every task_t pointer is a potential security bug

The tradeoff is that now cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no central point where access to a resource can be cut off. In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y can’t just invalidate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port when a privileged exec happens and expect that to work because cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel-internal MIG subsystems don’t use task ports, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y just translate between task ports and task struct pointers once at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user/kernel boundary. The kernel has no idea where all cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel pointers to a task’s task struct are; it can’t hope to invalidate cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m.

This is a much bigger problem than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original reference counting bug. When a privilege-escalating exec takes place execve doesn’t create a new process; cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct stays cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same, just cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 privileges change. This means that every single task_t pointer in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel is a potential security bug - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no locking mechanism to let you assert that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 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.

On cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap: rewriting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurface exploit

We actually only need to slightly tweak cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original IOSurface exploit to work even with cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 correct task_reference(owningTask) call. Instead of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child passing its task port back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent we’ll instead create cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child (correctly using cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child’s own task port) and pass that userclient port back to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent.

The child can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n execve a suid-root binary which will set cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 EUID of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task to 0 without freeing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task struct. The parent still has a send right to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOSurfaceRootUserClient, and that userclient’s owningTask now has EUID 0. The parent can cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n proceed as before, blocking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child, mapping cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target’s libc __DATA segment, overwriting a function pointer and unblocking cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child so that it tries to exit and executes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 ROP stack. This new exploit also defeats cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mitigation added in 10.11.6 which stops cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 creation of userclients with ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r task’s task ports.

Note that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are no failure cases for this exploit - cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s no race to win and no use-after-free which could go wrong. The exploit should work on all OS X versions <= 10.11.6.

This primitive is slightly less powerful than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 use after free, which could break you out of very restrictive sandboxes, as you do need to call execve. These IOKit objects which store task_t pointers on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap are really just cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 top of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 iceberg though.

On cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack: exploiting task_threads

Back closer to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 user/kernel boundary as soon as convert_port_to_task has converted a task port received from userspace into a task struct pointer, that task could exec a suid-root or entitled binary and increase its privileges. Even if that task struct pointer isn’t stored on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 heap cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re could still be an exploitable bug. Once case of this is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel MIG task_threads method:

kern_return_t
task_threads(
 task_t target_task,
 thread_act_array_t *act_list,
 mach_msg_type_number_t *act_listCnt );

Given a send right to a task port this method returns send rights to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread ports for each of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 threads in that task. Here’s a snippet from MIG auto-generated code in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel:

 target_task = convert_port_to_task(
   In0P->Head.msgh_request_port); // (1)
 RetCode = task_threads(
             target_task,
             (thread_act_array_t *)&(OutP->act_list.address),
             &OutP->act_listCnt);
 task_deallocate(target_task);

Here we see that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task port is converted into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying task struct pointer which is cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n stored in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 target_task local variable which lives for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 duration of this function call.

Here’s cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 relevant code from task_threads:

 task_threads(
   task_t task,
   thread_act_array_t *threads_out,
   mach_msg_type_number_t *count)
 {
   ...
   for (thread = (thread_t)queue_first(&task->threads);
        i < actual;
        ++i, thread = (thread_t)queue_next(&thread->task_threads)) {
     thread_reference_internal(thread);
     thread_list[j++] = thread;
   }

   ...

     for (i = 0; i < actual; ++i)
       ((ipc_port_t *) thread_list)[i] = convert_thread_to_port(thread_list[i]); // (2)
     }
   ...
 }


This code iterates through cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 list of threads collecting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 struct thread pointers cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n converts those struct threads to thread ports and returns. There are a handful of locks in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 code but cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y’re not relevant.

What happens if that task is exec-ing a suid root binary at cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same time?

The relevant parts of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec code are cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se two points in ipc_task_reset and ipc_thread_reset:

 void
 ipc_task_reset(
   task_t    task)
 {
   ipc_port_t old_kport, new_kport;
   ipc_port_t old_sself;
   ipc_port_t old_exc_actions[EXC_TYPES_COUNT];
   int i;

   new_kport = ipc_port_alloc_kernel();
   if (new_kport == IP_NULL)
     panic("ipc_task_reset");

   itk_lock(task);

   old_kport = task->itk_self;

   if (old_kport == IP_NULL) {
     itk_unlock(task);
     ipc_port_dealloc_kernel(new_kport);
     return;
   }

   task->itk_self = new_kport;
   old_sself = task->itk_sself;
   task->itk_sself = ipc_port_make_send(new_kport);
   ipc_kobject_set(old_kport, IKO_NULL, IKOT_NONE); // (3)

This is followed by a call to ipc_thread_reset:

 ipc_thread_reset(
   thread_t  thread)
 {
   ipc_port_t old_kport, new_kport;
   ipc_port_t old_sself;
   ipc_port_t old_exc_actions[EXC_TYPES_COUNT];
   boolean_t  has_old_exc_actions = FALSE;
   int      i;

   new_kport = ipc_port_alloc_kernel();
   if (new_kport == IP_NULL)
     panic("ipc_task_reset");

   thread_mtx_lock(thread);

   old_kport = thread->ith_self;

   if (old_kport == IP_NULL) {
     thread_mtx_unlock(thread);
     ipc_port_dealloc_kernel(new_kport);
     return;
   }

   thread->ith_self = new_kport; // (4)

Let's call cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process which is doing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec process B and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process calling task_threads() process A and imagine cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following interleaving of execution:

A:
 target_task = convert_port_to_task(
   In0P->Head.msgh_request_port); // (1)
 A gets pointer to process B's task struct on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack

B:
 ipc_kobject_set(old_kport, IKO_NULL, IKOT_NONE); // (3)
 B is execing a suid binary and invalidates cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 old task port so that it no longer has a task struct pointer

B:
 thread->ith_self = new_kport; // (4)
 B allocates new thread ports and sets cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365m up

A:
 ((ipc_port_t *) thread_list)[i] = convert_thread_to_port(thread_list[i]); // (2)
 A reads and converts cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new thread port objects for B’s privileged threads giving A a privileged thread port

Send rights to a thread port give you complete register control. The exploit proceeds in a similar fashion to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 previous two except that once it’s got cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread port it can directly point RIP to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 gadget address racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than overwriting a function pointer. This race window is quite tight as is requires a very particular interleaving of execution but it does work. Check out cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 original bug report.

Mitigations round 2

The release of iOS 10/MacOS 10.12 brought anocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r round of mitigations to defeat.
Firstly on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 IOKit side userclient lifetime is now directly tied to that of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 creating task. Secondly cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re’s a mitigation in ipc_kobject server to detect when a MIG kernel method has raced an execve syscall and force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 method to fail if a race was detected:

/*
* Check if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 port is a task port, if its a task port cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n
* snapshot cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task exec token before cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mig routine call.
*/
ipc_port_t port = request->ikm_header->msgh_remote_port;
if (IP_VALID(port) && ip_kotype(port) == IKOT_TASK) {
 task = convert_port_to_task_with_exec_token(port, &exec_token);
}

(*ptr->routine)(request->ikm_header, reply->ikm_header);

/* Check if cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exec token changed during cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 mig routine */
if (task != TASK_NULL) {
 if (exec_token != task->exec_token) {
   exec_token_changed = TRUE;
 }
 task_deallocate(task);
}

There are three flaws with this mitigation:
  1. It only inspects cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 first argument, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are kernel MIG methods which take a task port in a different position.
  2. It only checks for task ports, cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se issues also affect thread_ports in a similar way
  3. It only mitigates cases of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 bug where we need to get cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 resources which are returned by cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MIG call (eg ports.) There are plenty of ocá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r methods which actually directly modify cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 process state racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than returning new ports.

Exploiting cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 2nd round mitigations

Although we can no longer directly get a new thread port via task_threads cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are still some more roundabout ways to get it. We just need an API which modifies state racá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365r than directly returning something useful (like a task port) to us.

task_set_exception_port allows us to set a cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exception port for a task. When an exception is raised (for example by accessing invalid memory) cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel will send an exception message to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 registered exception handler. Importantly for us that exception message contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task and thread ports for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 thread which caused cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exception.

Like almost all places in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 kernel with a task_t on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack this api has a vulnerable race condition. In process A we’ll keep calling task_set_exception_ports() passing B’s task port while B execve’s a suid binary:

mig_internal novalue _Xtask_set_exception_ports(
 mach_msg_header_t *InHeadP,
 mach_msg_header_t *OutHeadP) {
...
 task = convert_port_to_task(In0P->Head.msgh_request_port); // (1)

 OutP->RetCode =
   task_set_exception_ports(task,
                            In0P->exception_mask,
                            In0P->new_port.name,
                            In0P->behavior,
                            In0P->new_flavor);
 task_deallocate(task);
...



kern_return_t
task_set_exception_ports(
 task_t                task,
 exception_mask_t      exception_mask,
 ipc_port_t            new_port,
 exception_behavior_t  new_behavior,
 thread_state_flavor_t new_flavor)
{
...
 itk_lock(task); // (2)

 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; ++i) {
   if ((exception_mask & (1 << i)) ) {
     old_port[i] = task->exc_actions[i].port;
     task->exc_actions[i].port = ipc_port_copy_send(new_port); // (3)
     task->exc_actions[i].behavior = new_behavior;
     task->exc_actions[i].flavor = new_flavor;
     task->exc_actions[i].privileged = privileged;
   }
...
 itk_unlock(task);
...

Process B calls execve to exec a privileged suid binary:

ipc_task_reset(
 task_t  task)
{
...
 itk_lock(task); // (4)
...
 ip_lock(old_kport);
 ipc_kobject_set_atomically(old_kport, IKO_NULL, IKOT_NONE); // (5)
 task->exec_token += 1;
 ip_unlock(old_kport);

 ipc_kobject_set(new_kport, (ipc_kobject_t) task, IKOT_TASK);

 for (i = FIRST_EXCEPTION; i < EXC_TYPES_COUNT; i++) {
...
   if (!task->exc_actions[i].privileged) {
     old_exc_actions[i] = task->exc_actions[i].port;
     task->exc_actions[i].port = IP_NULL; // (6)
   }
 }

 itk_unlock(task); //(7)

We’re looking for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 following interleaving:

A:
task = convert_port_to_task(In0P->Head.msgh_request_port); // (1)

B:
itk_lock(task); // (4)

ipc_kobject_set_atomically(old_kport, IKO_NULL, IKOT_NONE); // (5)

task->exc_actions[i].port = IP_NULL; // (6)

itk_unlock(task); //(7)

A:
itk_lock(task); // (2)

task->exc_actions[i].port = ipc_port_copy_send(new_port); // (3)

This race condition is far easier to win than cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task_threads case as cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 locks make sure that everything lines up nicely for us. We just need to call task_set_exception_ports in a loop and hope that (1) gets called by A just before B takes cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task lock at (4). In practise cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit wins cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race in a few milliseconds.

The final trick is to actually make sure that if we win cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race we force cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 child to cause an exception and send us its task and thread ports. We can do this by calling setrlimit(RLIMIT_STACK) with a very small value just before execing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 suid target. This means that cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 binary will be run with a tiny stack and will almost immediately segfault.

In cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 parent once cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task_set_exception_port call has failed we try to receive on cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exception port with a short timeout. If a message is received cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365n we won cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 race and that message contains cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task and thread ports for an euid 0 process. In this case cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 exploit allocates some RWX memory in cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 task and copies a shellcode stub into cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re which does this:

 struct rlimit lim = {0x1000000, 0x1000000};
 setrlimit(RLIMIT_STACK, lim);
 setuid(0);
 char* argv[2] = {"/bin/bash", 0};
 execve("/bin/bash", argv, 0);

This shellcode sets cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 stack size back to a large value, does a setuid(0) to prevent bash dropping privileges and executes a shell.

This exploit should work reliably on all versions of MacOS/OS X 10.12.0 and below.

The final fix

This isn’t an easy bug class to fix. Due to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 design of XNU cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365re are task_t pointers everywhere and cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying issue affects more than just task_t; threads suffer from cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 same issue. Apple decided to refactor cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 execve code to allocate new task and thread structures when loading a binary which should fix cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 underlying issue. This is a considerable amount of work, kudos to Apple for cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 engineering effort cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365y put into fixing cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365se bugs and I look forward to cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 release of cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 MacOS 10.12.1 XNU source to see cá cược thể thao bet365_cách nạp tiền vào bet365_ đăng ký bet365 new code.