From 3c814157acd62470a4bdab20af935e44141317c4 Mon Sep 17 00:00:00 2001 From: CPol Date: Thu, 5 Oct 2023 22:21:18 +0000 Subject: [PATCH] GITBOOK-4113: change request with no subject merged in GitBook --- SUMMARY.md | 6 +- .../README.md | 709 +----------------- .../macos-mig-mach-interface-generator.md | 358 +++++++++ .../macos-xpc/README.md | 403 ++++++++++ .../macos-xpc}/macos-xpc-authorization.md | 6 +- .../macos-xpc-connecting-process-check.md | 4 +- 6 files changed, 777 insertions(+), 709 deletions(-) create mode 100644 macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md create mode 100644 macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/README.md rename macos-hardening/macos-security-and-privilege-escalation/{mac-os-architecture/macos-ipc-inter-process-communication => macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc}/macos-xpc-authorization.md (98%) rename macos-hardening/macos-security-and-privilege-escalation/{mac-os-architecture/macos-ipc-inter-process-communication => macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc}/macos-xpc-connecting-process-check.md (97%) diff --git a/SUMMARY.md b/SUMMARY.md index 16ed5de94..4e04f9119 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -161,10 +161,12 @@ * [macOS Objective-C](macos-hardening/macos-security-and-privilege-escalation/macos-basic-objective-c.md) * [macOS Proces Abuse](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/README.md) * [macOS IPC - Inter Process Communication](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md) + * [macOS MIG - Mach Interface Generator](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md) + * [macOS XPC](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/README.md) + * [macOS XPC Authorization](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-authorization.md) + * [macOS XPC Connecting Process Check](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-connecting-process-check.md) * [macOS PID Reuse](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-pid-reuse.md) * [macOS Thread Injection via Task port](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-thread-injection-via-task-port.md) - * [macOS XPC Authorization](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-authorization.md) - * [macOS XPC Connecting Process Check](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-connecting-process-check.md) * [macOS Electron Applications Injection](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md) * [macOS Function Hooking](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-function-hooking.md) * [macOS .Net Applications Injection](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-.net-applications-injection.md) diff --git a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md b/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md index 9d56f95a9..922f66e03 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md +++ b/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md @@ -770,716 +770,21 @@ In this technique a thread of the process is hijacked: XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for **communication between processes** on macOS and iOS. XPC provides a mechanism for making **safe, asynchronous method calls between different processes** on the system. It's a part of Apple's security paradigm, allowing for the **creation of privilege-separated applications** where each **component** runs with **only the permissions it needs** to do its job, thereby limiting the potential damage from a compromised process. -XPC uses a form of Inter-Process Communication (IPC), which is a set of methods for different programs running on the same system to send data back and forth. +For more information about how this **communication work** on how it **could be vulnerable** check: -The primary benefits of XPC include: - -1. **Security**: By separating work into different processes, each process can be granted only the permissions it needs. This means that even if a process is compromised, it has limited ability to do harm. -2. **Stability**: XPC helps isolate crashes to the component where they occur. If a process crashes, it can be restarted without affecting the rest of the system. -3. **Performance**: XPC allows for easy concurrency, as different tasks can be run simultaneously in different processes. - -The only **drawback** is that **separating an application in several processes** making them communicate via XPC is **less efficient**. But in todays systems this isn't almost noticeable and the benefits are better. - -### Application Specific XPC services - -The XPC components of an application are **inside the application itself.** For example, in Safari you can find them in **`/Applications/Safari.app/Contents/XPCServices`**. They have extension **`.xpc`** (like **`com.apple.Safari.SandboxBroker.xpc`**) and are **also bundles** with the main binary inside of it: `/Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker` and an `Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist` - -As you might be thinking a **XPC component will have different entitlements and privileges** than the other XPC components or the main app binary. EXCEPT if a XPC service is configured with [**JoinExistingSession**](https://developer.apple.com/documentation/bundleresources/information\_property\_list/xpcservice/joinexistingsession) set to “True” in its **Info.plist** file. In this case, the XPC service will run in the **same security session as the application** that called it. - -XPC services are **started** by **launchd** when required and **shut down** once all tasks are **complete** to free system resources. **Application-specific XPC components can only be utilized by the application**, thereby reducing the risk associated with potential vulnerabilities. - -### System Wide XPC services - -System-wide XPC services are accessible to all users. These services, either launchd or Mach-type, need to be **defined in plist** files located in specified directories such as **`/System/Library/LaunchDaemons`**, **`/Library/LaunchDaemons`**, **`/System/Library/LaunchAgents`**, or **`/Library/LaunchAgents`**. - -These plists files will have a key called **`MachServices`** with the name of the service, and a key called **`Program`** with the path to the binary: - -```xml -cat /Library/LaunchDaemons/com.jamf.management.daemon.plist - - - - - - Program - /Library/Application Support/JAMF/Jamf.app/Contents/MacOS/JamfDaemon.app/Contents/MacOS/JamfDaemon - AbandonProcessGroup - - KeepAlive - - Label - com.jamf.management.daemon - MachServices - - com.jamf.management.daemon.aad - - com.jamf.management.daemon.agent - - com.jamf.management.daemon.binary - - com.jamf.management.daemon.selfservice - - com.jamf.management.daemon.service - - - RunAtLoad - - - -``` - -The ones in **`LaunchDameons`** are run by root. So if an unprivileged process can talk with one of these it could be able to escalate privileges. - -### XPC Event Messages - -Applications can **subscribe** to different event **messages**, enabling them to be **initiated on-demand** when such events happen. The **setup** for these services is done in l**aunchd plist files**, located in the **same directories as the previous ones** and containing an extra **`LaunchEvent`** key. - -### XPC Connecting Process Check - -When a process tries to call a method from via an XPC connection, the **XPC service should check if that process is allowed to connect**. Here are the common ways to check that and the common pitfalls: - -{% content-ref url="macos-xpc-connecting-process-check.md" %} -[macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md) +{% content-ref url="../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/" %} +[macos-xpc](../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/) {% endcontent-ref %} -### XPC Authorization - -Apple also allows apps to **configure some rights and how to get them** so if the calling process have them it would be **allowed to call a method** from the XPC service: - -{% content-ref url="macos-xpc-authorization.md" %} -[macos-xpc-authorization.md](macos-xpc-authorization.md) -{% endcontent-ref %} - -### C Code Example - -{% tabs %} -{% tab title="xpc_server.c" %} -```c -// gcc xpc_server.c -o xpc_server - -#include - -static void handle_event(xpc_object_t event) { - if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) { - // Print received message - const char* received_message = xpc_dictionary_get_string(event, "message"); - printf("Received message: %s\n", received_message); - - // Create a response dictionary - xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0); - xpc_dictionary_set_string(response, "received", "received"); - - // Send response - xpc_connection_t remote = xpc_dictionary_get_remote_connection(event); - xpc_connection_send_message(remote, response); - - // Clean up - xpc_release(response); - } -} - -static void handle_connection(xpc_connection_t connection) { - xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { - handle_event(event); - }); - xpc_connection_resume(connection); -} - -int main(int argc, const char *argv[]) { - xpc_connection_t service = xpc_connection_create_mach_service("xyz.hacktricks.service", - dispatch_get_main_queue(), - XPC_CONNECTION_MACH_SERVICE_LISTENER); - if (!service) { - fprintf(stderr, "Failed to create service.\n"); - exit(EXIT_FAILURE); - } - - xpc_connection_set_event_handler(service, ^(xpc_object_t event) { - xpc_type_t type = xpc_get_type(event); - if (type == XPC_TYPE_CONNECTION) { - handle_connection(event); - } - }); - - xpc_connection_resume(service); - dispatch_main(); - - return 0; -} -``` -{% endtab %} - -{% tab title="xpc_client.c" %} -```c -// gcc xpc_client.c -o xpc_client - -#include - -int main(int argc, const char *argv[]) { - xpc_connection_t connection = xpc_connection_create_mach_service("xyz.hacktricks.service", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); - - xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { - if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) { - // Print received message - const char* received_message = xpc_dictionary_get_string(event, "received"); - printf("Received message: %s\n", received_message); - } - }); - - xpc_connection_resume(connection); - - xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); - xpc_dictionary_set_string(message, "message", "Hello, Server!"); - - xpc_connection_send_message(connection, message); - - dispatch_main(); - - return 0; -} -``` -{% endtab %} - -{% tab title="xyz.hacktricks.service.plist" %} -```xml - - - -Label -xyz.hacktricks.service -MachServices - - xyz.hacktricks.service - - -Program - /tmp/xpc_server - ProgramArguments - - /tmp/xpc_server - - - -``` -{% endtab %} -{% endtabs %} - -```bash -# Compile the server & client -gcc xpc_server.c -o xpc_server -gcc xpc_client.c -o xpc_client - -# Save server on it's location -cp xpc_server /tmp - -# Load daemon -sudo cp xyz.hacktricks.service.plist /Library/LaunchDaemons -sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.service.plist - -# Call client -./xpc_client - -# Clean -sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.service.plist -sudo rm /Library/LaunchDaemons/xyz.hacktricks.service.plist /tmp/xpc_server -``` - -### ObjectiveC Code Example - -{% tabs %} -{% tab title="oc_xpc_server.m" %} -```objectivec -// gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server -#include - -@protocol MyXPCProtocol -- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply; -@end - -@interface MyXPCObject : NSObject -@end - - -@implementation MyXPCObject -- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply { - NSLog(@"Received message: %@", some_string); - NSString *response = @"Received"; - reply(response); -} -@end - -@interface MyDelegate : NSObject -@end - - -@implementation MyDelegate - -- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { - newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]; - - MyXPCObject *my_object = [MyXPCObject new]; - - newConnection.exportedObject = my_object; - - [newConnection resume]; - return YES; -} -@end - -int main(void) { - - NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:@"xyz.hacktricks.svcoc"]; - - id delegate = [MyDelegate new]; - listener.delegate = delegate; - [listener resume]; - - sleep(10); // Fake something is done and then it ends -} -``` -{% endtab %} - -{% tab title="oc_xpc_client.m" %} -```objectivec -// gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client -#include - -@protocol MyXPCProtocol -- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply; -@end - -int main(void) { - NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:@"xyz.hacktricks.svcoc" options:NSXPCConnectionPrivileged]; - connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]; - [connection resume]; - - [[connection remoteObjectProxy] sayHello:@"Hello, Server!" withReply:^(NSString *response) { - NSLog(@"Received response: %@", response); - }]; - - [[NSRunLoop currentRunLoop] run]; - - return 0; -} -``` -{% endtab %} - -{% tab title="xyz.hacktricks.svcoc.plist" %} -```xml - - - -Label -xyz.hacktricks.svcoc -MachServices - - xyz.hacktricks.svcoc - - -Program - /tmp/oc_xpc_server - ProgramArguments - - /tmp/oc_xpc_server - - - -``` -{% endtab %} -{% endtabs %} - -```bash -# Compile the server & client -gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server -gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client - -# Save server on it's location -cp oc_xpc_server /tmp - -# Load daemon -sudo cp xyz.hacktricks.svcoc.plist /Library/LaunchDaemons -sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist - -# Call client -./oc_xpc_client - -# Clean -sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist -sudo rm /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist /tmp/oc_xpc_server -``` - -### Client inside a Dylib code - -``` -// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib -// gcc injection example: -// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin - -#import - -@protocol MyXPCProtocol -- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply; -@end - -__attribute__((constructor)) -static void customConstructor(int argc, const char **argv) -{ - NSString* _serviceName = @"xyz.hacktricks.svcoc"; - - NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096]; - - [_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]]; - - [_agentConnection resume]; - - [[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) { - (void)error; - NSLog(@"Connection Failure"); - }] sayHello:@"Hello, Server!" withReply:^(NSString *response) { - NSLog(@"Received response: %@", response); - } ]; - NSLog(@"Done!"); - - return; -} -``` - ## MIG - Mach Interface Generator MIG was created to **simplify the process of Mach IPC** code creation. It basically **generates the needed code** for server and client to communicate with a given definition. Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before. -### Example +For more info check: -Create a definition file, in this case with a very simple function: - -{% code title="myipc.defs" %} -```cpp -subsystem myipc 500; // Arbitrary name and id - -userprefix USERPREF; // Prefix for created functions in the client -serverprefix SERVERPREF; // Prefix for created functions in the server - -#include -#include - -simpleroutine Subtract( - server_port : mach_port_t; - n1 : uint32_t; - n2 : uint32_t); -``` -{% endcode %} - -Now use mig to generate the server and client code that will be able to comunicate within each other to call the Subtract function: - -```bash -mig -header myipcUser.h -sheader myipcServer.h myipc.defs -``` - -Several new files will be created in the current directory. - -In the files **`myipcServer.c`** and **`myipcServer.h`** you can find the declaration and definition of the struct **`SERVERPREFmyipc_subsystem`**, which basically defines the function to call based on the received message ID (we indicated a starting number of 500): - -{% tabs %} -{% tab title="myipcServer.c" %} -```c -/* Description of this subsystem, for use in direct RPC */ -const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = { - myipc_server_routine, - 500, // start ID - 501, // end ID - (mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem), - (vm_address_t)0, - { - { (mig_impl_routine_t) 0, - // Function to call - (mig_stub_routine_t) _XSubtract, 3, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)}, - } -}; -``` -{% endtab %} - -{% tab title="myipcServer.h" %} -```c -/* Description of this subsystem, for use in direct RPC */ -extern const struct SERVERPREFmyipc_subsystem { - mig_server_routine_t server; /* Server routine */ - mach_msg_id_t start; /* Min routine number */ - mach_msg_id_t end; /* Max routine number + 1 */ - unsigned int maxsize; /* Max msg size */ - vm_address_t reserved; /* Reserved */ - struct routine_descriptor /* Array of routine descriptors */ - routine[1]; -} SERVERPREFmyipc_subsystem; -``` -{% endtab %} -{% endtabs %} - -Based on the previous struct the function **`myipc_server_routine`** will get the **message ID** and return the proper function to call: - -```c -mig_external mig_routine_t myipc_server_routine - (mach_msg_header_t *InHeadP) -{ - int msgh_id; - - msgh_id = InHeadP->msgh_id - 500; - - if ((msgh_id > 0) || (msgh_id < 0)) - return 0; - - return SERVERPREFmyipc_subsystem.routine[msgh_id].stub_routine; -} -``` - -In this example we only defined 1 function in the definitions, but if we would have defined more, the would have been inside the array of **`SERVERPREFmyipc_subsystem`** and the first one will be assigned to the ID **500**, the second one to the ID **501**... - -Actually it's possible to identify this relation in the struct **`subsystem_to_name_map_myipc`** from **`myipcServer.h`**: - -```c -#ifndef subsystem_to_name_map_myipc -#define subsystem_to_name_map_myipc \ - { "Subtract", 500 } -#endif -``` - -Finally, another important function to make the server work will be **`myipc_server`**, which is the one that will actually **call the function** related to the received id: - -
mig_external boolean_t myipc_server
-	(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
-{
-	/*
-	 * typedef struct {
-	 * 	mach_msg_header_t Head;
-	 * 	NDR_record_t NDR;
-	 * 	kern_return_t RetCode;
-	 * } mig_reply_error_t;
-	 */
-
-	mig_routine_t routine;
-
-	OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0);
-	OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port;
-	/* Minimal size: routine() will update it if different */
-	OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
-	OutHeadP->msgh_local_port = MACH_PORT_NULL;
-	OutHeadP->msgh_id = InHeadP->msgh_id + 100;
-	OutHeadP->msgh_reserved = 0;
-
-	if ((InHeadP->msgh_id > 500) || (InHeadP->msgh_id < 500) ||
-	    ((routine = SERVERPREFmyipc_subsystem.routine[InHeadP->msgh_id - 500].stub_routine) == 0)) {
-		((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
-		((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
-		return FALSE;
-	}
-	(*routine) (InHeadP, OutHeadP);
-	return TRUE;
-}
-
- -Check the following code to use the generated code to create a simple server and client where the client can call the functions Subtract from the server: - -{% tabs %} -{% tab title="myipc_server.c" %} -```c -// gcc myipc_server.c myipcServer.c -o myipc_server - -#include -#include -#include -#include "myipcServer.h" - -kern_return_t SERVERPREFSubtract(mach_port_t server_port, uint32_t n1, uint32_t n2) -{ - printf("Received: %d - %d = %d\n", n1, n2, n1 - n2); - return KERN_SUCCESS; -} - -int main() { - - mach_port_t port; - kern_return_t kr; - - // Register the mach service - kr = bootstrap_check_in(bootstrap_port, "xyz.hacktricks.mig", &port); - if (kr != KERN_SUCCESS) { - printf("bootstrap_check_in() failed with code 0x%x\n", kr); - return 1; - } - - // myipc_server is the function that handles incoming messages (check previous exlpanation) - mach_msg_server(myipc_server, sizeof(union __RequestUnion__SERVERPREFmyipc_subsystem), port, MACH_MSG_TIMEOUT_NONE); -} -``` -{% endtab %} - -{% tab title="myipc_client.c" %} -```c -// gcc myipc_client.c myipcUser.c -o myipc_client - -#include -#include -#include - -#include -#include -#include "myipcUser.h" - -int main() { - - // Lookup the receiver port using the bootstrap server. - mach_port_t port; - kern_return_t kr = bootstrap_look_up(bootstrap_port, "xyz.hacktricks.mig", &port); - if (kr != KERN_SUCCESS) { - printf("bootstrap_look_up() failed with code 0x%x\n", kr); - return 1; - } - printf("Port right name %d\n", port); - USERPREFSubtract(port, 40, 2); -} -``` -{% endtab %} -{% endtabs %} - -## Binary Analysis - -As many binaries now use MIG to expose mach ports, it's interesting to know how to **identify that MIG was used** and the **functions that MIG executes** with each message ID. - -[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) can parse MIG information from a Mach-O binary indicating the message ID and identifying the function to execute: - -```bash -jtool2 -d __DATA.__const myipc_server | grep MIG -``` - -It was previously mentioned that the function that will take care of **calling the correct function depending on the received message ID** was `myipc_server`. However, you usually won't have the symbols of the binary (no functions names), so it's interesting to **check how it looks like decompiled** as it will always be very similar (the code of this function is independent from the functions exposed): - - - -{% tabs %} -{% tab title="myipc_server decompiled 1" %} -
int _myipc_server(int arg0, int arg1) {
-    var_10 = arg0;
-    var_18 = arg1;
-    // Initial instructions to find the proper function ponters
-    *(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f;
-    *(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
-    *(int32_t *)(var_18 + 0x4) = 0x24;
-    *(int32_t *)(var_18 + 0xc) = 0x0;
-    *(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
-    *(int32_t *)(var_18 + 0x10) = 0x0;
-    if (*(int32_t *)(var_10 + 0x14) <= 0x1f4 && *(int32_t *)(var_10 + 0x14) >= 0x1f4) {
-            rax = *(int32_t *)(var_10 + 0x14);
-            // Call to sign_extend_64 that can help to identifyf this function
-            // This stores in rax the pointer to the call that needs to be called
-            // Check the used of the address 0x100004040 (functions addresses array)
-            // 0x1f4 = 500 (the strating ID)
-            rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
-            var_20 = rax;
-            // If - else, the if returns false, while the else call the correct function and returns true
-            if (rax == 0x0) {
-                    *(var_18 + 0x18) = **_NDR_record;
-                    *(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
-                    var_4 = 0x0;
-            }
-            else {
-                    // Calculated address that calls the proper function with 2 arguments
-                    (var_20)(var_10, var_18);
-                    var_4 = 0x1;
-            }
-    }
-    else {
-            *(var_18 + 0x18) = **_NDR_record;
-            *(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
-            var_4 = 0x0;
-    }
-    rax = var_4;
-    return rax;
-}
-
-{% endtab %} - -{% tab title="myipc_server decompiled 2" %} -This is the same function decompiled in a difefrent Hopper free version: - -
int _myipc_server(int arg0, int arg1) {
-    r31 = r31 - 0x40;
-    saved_fp = r29;
-    stack[-8] = r30;
-    var_10 = arg0;
-    var_18 = arg1;
-    // Initial instructions to find the proper function ponters
-    *(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f | 0x0;
-    *(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
-    *(int32_t *)(var_18 + 0x4) = 0x24;
-    *(int32_t *)(var_18 + 0xc) = 0x0;
-    *(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
-    *(int32_t *)(var_18 + 0x10) = 0x0;
-    r8 = *(int32_t *)(var_10 + 0x14);
-    r8 = r8 - 0x1f4;
-    if (r8 > 0x0) {
-            if (CPU_FLAGS & G) {
-                    r8 = 0x1;
-            }
-    }
-    if ((r8 & 0x1) == 0x0) {
-            r8 = *(int32_t *)(var_10 + 0x14);
-            r8 = r8 - 0x1f4;
-            if (r8 < 0x0) {
-                    if (CPU_FLAGS & L) {
-                            r8 = 0x1;
-                    }
-            }
-            if ((r8 & 0x1) == 0x0) {
-                    r8 = *(int32_t *)(var_10 + 0x14);
-                    // 0x1f4 = 500 (the strating ID)
-                    r8 = r8 - 0x1f4;
-                    asm { smaddl     x8, w8, w9, x10 };
-                    r8 = *(r8 + 0x8);
-                    var_20 = r8;
-                    r8 = r8 - 0x0;
-                    if (r8 != 0x0) {
-                            if (CPU_FLAGS & NE) {
-                                    r8 = 0x1;
-                            }
-                    }
-                    // Same if else as in the previous version
-                    // Check the used of the address 0x100004040 (functions addresses array)
-                    if ((r8 & 0x1) == 0x0) {
-                            *(var_18 + 0x18) = **0x100004000;
-                            *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
-                            var_4 = 0x0;
-                    }
-                    else {
-                            // Call to the calculated address where the function should be
-                            (var_20)(var_10, var_18);
-                            var_4 = 0x1;
-                    }
-            }
-            else {
-                    *(var_18 + 0x18) = **0x100004000;
-                    *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
-                    var_4 = 0x0;
-            }
-    }
-    else {
-            *(var_18 + 0x18) = **0x100004000;
-            *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
-            var_4 = 0x0;
-    }
-    r0 = var_4;
-    return r0;
-}
-
-
-{% endtab %} -{% endtabs %} - -Actually if you go to the function **`0x100004000`** you will find the array of **`routine_descriptor`** structs, the first element of the struct is the address where the function is implemented and the **struct takes 0x28 bytes**, so each 0x28 bytes (starting from byte 0) you can get 8 bytes and that be the **address of the function** that will be called: - -
- -
- -This data can be extracted [**using this Hopper script**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py). +{% content-ref url="../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md" %} +[macos-mig-mach-interface-generator.md](../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md) +{% endcontent-ref %} ## References diff --git a/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md new file mode 100644 index 000000000..c3b668fba --- /dev/null +++ b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md @@ -0,0 +1,358 @@ +# macOS MIG - Mach Interface Generator + +
+ +☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 + +* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! +* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) +* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) +* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** +* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud). + +
+ +MIG was created to **simplify the process of Mach IPC** code creation. It basically **generates the needed code** for server and client to communicate with a given definition. Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before. + +### Example + +Create a definition file, in this case with a very simple function: + +{% code title="myipc.defs" %} +```cpp +subsystem myipc 500; // Arbitrary name and id + +userprefix USERPREF; // Prefix for created functions in the client +serverprefix SERVERPREF; // Prefix for created functions in the server + +#include +#include + +simpleroutine Subtract( + server_port : mach_port_t; + n1 : uint32_t; + n2 : uint32_t); +``` +{% endcode %} + +Now use mig to generate the server and client code that will be able to comunicate within each other to call the Subtract function: + +```bash +mig -header myipcUser.h -sheader myipcServer.h myipc.defs +``` + +Several new files will be created in the current directory. + +In the files **`myipcServer.c`** and **`myipcServer.h`** you can find the declaration and definition of the struct **`SERVERPREFmyipc_subsystem`**, which basically defines the function to call based on the received message ID (we indicated a starting number of 500): + +{% tabs %} +{% tab title="myipcServer.c" %} +```c +/* Description of this subsystem, for use in direct RPC */ +const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = { + myipc_server_routine, + 500, // start ID + 501, // end ID + (mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem), + (vm_address_t)0, + { + { (mig_impl_routine_t) 0, + // Function to call + (mig_stub_routine_t) _XSubtract, 3, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)}, + } +}; +``` +{% endtab %} + +{% tab title="myipcServer.h" %} +```c +/* Description of this subsystem, for use in direct RPC */ +extern const struct SERVERPREFmyipc_subsystem { + mig_server_routine_t server; /* Server routine */ + mach_msg_id_t start; /* Min routine number */ + mach_msg_id_t end; /* Max routine number + 1 */ + unsigned int maxsize; /* Max msg size */ + vm_address_t reserved; /* Reserved */ + struct routine_descriptor /* Array of routine descriptors */ + routine[1]; +} SERVERPREFmyipc_subsystem; +``` +{% endtab %} +{% endtabs %} + +Based on the previous struct the function **`myipc_server_routine`** will get the **message ID** and return the proper function to call: + +```c +mig_external mig_routine_t myipc_server_routine + (mach_msg_header_t *InHeadP) +{ + int msgh_id; + + msgh_id = InHeadP->msgh_id - 500; + + if ((msgh_id > 0) || (msgh_id < 0)) + return 0; + + return SERVERPREFmyipc_subsystem.routine[msgh_id].stub_routine; +} +``` + +In this example we only defined 1 function in the definitions, but if we would have defined more, the would have been inside the array of **`SERVERPREFmyipc_subsystem`** and the first one will be assigned to the ID **500**, the second one to the ID **501**... + +Actually it's possible to identify this relation in the struct **`subsystem_to_name_map_myipc`** from **`myipcServer.h`**: + +```c +#ifndef subsystem_to_name_map_myipc +#define subsystem_to_name_map_myipc \ + { "Subtract", 500 } +#endif +``` + +Finally, another important function to make the server work will be **`myipc_server`**, which is the one that will actually **call the function** related to the received id: + +
mig_external boolean_t myipc_server
+	(mach_msg_header_t *InHeadP, mach_msg_header_t *OutHeadP)
+{
+	/*
+	 * typedef struct {
+	 * 	mach_msg_header_t Head;
+	 * 	NDR_record_t NDR;
+	 * 	kern_return_t RetCode;
+	 * } mig_reply_error_t;
+	 */
+
+	mig_routine_t routine;
+
+	OutHeadP->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REPLY(InHeadP->msgh_bits), 0);
+	OutHeadP->msgh_remote_port = InHeadP->msgh_reply_port;
+	/* Minimal size: routine() will update it if different */
+	OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
+	OutHeadP->msgh_local_port = MACH_PORT_NULL;
+	OutHeadP->msgh_id = InHeadP->msgh_id + 100;
+	OutHeadP->msgh_reserved = 0;
+
+	if ((InHeadP->msgh_id > 500) || (InHeadP->msgh_id < 500) ||
+	    ((routine = SERVERPREFmyipc_subsystem.routine[InHeadP->msgh_id - 500].stub_routine) == 0)) {
+		((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
+		((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
+		return FALSE;
+	}
+	(*routine) (InHeadP, OutHeadP);
+	return TRUE;
+}
+
+ +Check the following code to use the generated code to create a simple server and client where the client can call the functions Subtract from the server: + +{% tabs %} +{% tab title="myipc_server.c" %} +```c +// gcc myipc_server.c myipcServer.c -o myipc_server + +#include +#include +#include +#include "myipcServer.h" + +kern_return_t SERVERPREFSubtract(mach_port_t server_port, uint32_t n1, uint32_t n2) +{ + printf("Received: %d - %d = %d\n", n1, n2, n1 - n2); + return KERN_SUCCESS; +} + +int main() { + + mach_port_t port; + kern_return_t kr; + + // Register the mach service + kr = bootstrap_check_in(bootstrap_port, "xyz.hacktricks.mig", &port); + if (kr != KERN_SUCCESS) { + printf("bootstrap_check_in() failed with code 0x%x\n", kr); + return 1; + } + + // myipc_server is the function that handles incoming messages (check previous exlpanation) + mach_msg_server(myipc_server, sizeof(union __RequestUnion__SERVERPREFmyipc_subsystem), port, MACH_MSG_TIMEOUT_NONE); +} +``` +{% endtab %} + +{% tab title="myipc_client.c" %} +```c +// gcc myipc_client.c myipcUser.c -o myipc_client + +#include +#include +#include + +#include +#include +#include "myipcUser.h" + +int main() { + + // Lookup the receiver port using the bootstrap server. + mach_port_t port; + kern_return_t kr = bootstrap_look_up(bootstrap_port, "xyz.hacktricks.mig", &port); + if (kr != KERN_SUCCESS) { + printf("bootstrap_look_up() failed with code 0x%x\n", kr); + return 1; + } + printf("Port right name %d\n", port); + USERPREFSubtract(port, 40, 2); +} +``` +{% endtab %} +{% endtabs %} + +### Binary Analysis + +As many binaries now use MIG to expose mach ports, it's interesting to know how to **identify that MIG was used** and the **functions that MIG executes** with each message ID. + +[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) can parse MIG information from a Mach-O binary indicating the message ID and identifying the function to execute: + +```bash +jtool2 -d __DATA.__const myipc_server | grep MIG +``` + +It was previously mentioned that the function that will take care of **calling the correct function depending on the received message ID** was `myipc_server`. However, you usually won't have the symbols of the binary (no functions names), so it's interesting to **check how it looks like decompiled** as it will always be very similar (the code of this function is independent from the functions exposed): + +{% tabs %} +{% tab title="myipc_server decompiled 1" %} +
int _myipc_server(int arg0, int arg1) {
+    var_10 = arg0;
+    var_18 = arg1;
+    // Initial instructions to find the proper function ponters
+    *(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f;
+    *(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
+    *(int32_t *)(var_18 + 0x4) = 0x24;
+    *(int32_t *)(var_18 + 0xc) = 0x0;
+    *(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
+    *(int32_t *)(var_18 + 0x10) = 0x0;
+    if (*(int32_t *)(var_10 + 0x14) <= 0x1f4 && *(int32_t *)(var_10 + 0x14) >= 0x1f4) {
+            rax = *(int32_t *)(var_10 + 0x14);
+            // Call to sign_extend_64 that can help to identifyf this function
+            // This stores in rax the pointer to the call that needs to be called
+            // Check the used of the address 0x100004040 (functions addresses array)
+            // 0x1f4 = 500 (the strating ID)
+            rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
+            var_20 = rax;
+            // If - else, the if returns false, while the else call the correct function and returns true
+            if (rax == 0x0) {
+                    *(var_18 + 0x18) = **_NDR_record;
+                    *(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
+                    var_4 = 0x0;
+            }
+            else {
+                    // Calculated address that calls the proper function with 2 arguments
+                    (var_20)(var_10, var_18);
+                    var_4 = 0x1;
+            }
+    }
+    else {
+            *(var_18 + 0x18) = **_NDR_record;
+            *(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
+            var_4 = 0x0;
+    }
+    rax = var_4;
+    return rax;
+}
+
+{% endtab %} + +{% tab title="myipc_server decompiled 2" %} +This is the same function decompiled in a difefrent Hopper free version: + +
int _myipc_server(int arg0, int arg1) {
+    r31 = r31 - 0x40;
+    saved_fp = r29;
+    stack[-8] = r30;
+    var_10 = arg0;
+    var_18 = arg1;
+    // Initial instructions to find the proper function ponters
+    *(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f | 0x0;
+    *(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
+    *(int32_t *)(var_18 + 0x4) = 0x24;
+    *(int32_t *)(var_18 + 0xc) = 0x0;
+    *(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
+    *(int32_t *)(var_18 + 0x10) = 0x0;
+    r8 = *(int32_t *)(var_10 + 0x14);
+    r8 = r8 - 0x1f4;
+    if (r8 > 0x0) {
+            if (CPU_FLAGS & G) {
+                    r8 = 0x1;
+            }
+    }
+    if ((r8 & 0x1) == 0x0) {
+            r8 = *(int32_t *)(var_10 + 0x14);
+            r8 = r8 - 0x1f4;
+            if (r8 < 0x0) {
+                    if (CPU_FLAGS & L) {
+                            r8 = 0x1;
+                    }
+            }
+            if ((r8 & 0x1) == 0x0) {
+                    r8 = *(int32_t *)(var_10 + 0x14);
+                    // 0x1f4 = 500 (the strating ID)
+                    r8 = r8 - 0x1f4;
+                    asm { smaddl     x8, w8, w9, x10 };
+                    r8 = *(r8 + 0x8);
+                    var_20 = r8;
+                    r8 = r8 - 0x0;
+                    if (r8 != 0x0) {
+                            if (CPU_FLAGS & NE) {
+                                    r8 = 0x1;
+                            }
+                    }
+                    // Same if else as in the previous version
+                    // Check the used of the address 0x100004040 (functions addresses array)
+                    if ((r8 & 0x1) == 0x0) {
+                            *(var_18 + 0x18) = **0x100004000;
+                            *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
+                            var_4 = 0x0;
+                    }
+                    else {
+                            // Call to the calculated address where the function should be
+                            (var_20)(var_10, var_18);
+                            var_4 = 0x1;
+                    }
+            }
+            else {
+                    *(var_18 + 0x18) = **0x100004000;
+                    *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
+                    var_4 = 0x0;
+            }
+    }
+    else {
+            *(var_18 + 0x18) = **0x100004000;
+            *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
+            var_4 = 0x0;
+    }
+    r0 = var_4;
+    return r0;
+}
+
+
+{% endtab %} +{% endtabs %} + +Actually if you go to the function **`0x100004000`** you will find the array of **`routine_descriptor`** structs, the first element of the struct is the address where the function is implemented and the **struct takes 0x28 bytes**, so each 0x28 bytes (starting from byte 0) you can get 8 bytes and that be the **address of the function** that will be called: + +
+ +
+ +This data can be extracted [**using this Hopper script**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py). + +
+ +☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 + +* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! +* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) +* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) +* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** +* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud). + +
diff --git a/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/README.md b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/README.md new file mode 100644 index 000000000..0a98c7df9 --- /dev/null +++ b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/README.md @@ -0,0 +1,403 @@ +# macOS XPC + +
+ +☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 + +* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! +* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) +* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) +* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** +* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud). + +
+ +## Basic Information + +XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for **communication between processes** on macOS and iOS. XPC provides a mechanism for making **safe, asynchronous method calls between different processes** on the system. It's a part of Apple's security paradigm, allowing for the **creation of privilege-separated applications** where each **component** runs with **only the permissions it needs** to do its job, thereby limiting the potential damage from a compromised process. + +XPC uses a form of Inter-Process Communication (IPC), which is a set of methods for different programs running on the same system to send data back and forth. + +The primary benefits of XPC include: + +1. **Security**: By separating work into different processes, each process can be granted only the permissions it needs. This means that even if a process is compromised, it has limited ability to do harm. +2. **Stability**: XPC helps isolate crashes to the component where they occur. If a process crashes, it can be restarted without affecting the rest of the system. +3. **Performance**: XPC allows for easy concurrency, as different tasks can be run simultaneously in different processes. + +The only **drawback** is that **separating an application in several processes** making them communicate via XPC is **less efficient**. But in todays systems this isn't almost noticeable and the benefits are better. + +## Application Specific XPC services + +The XPC components of an application are **inside the application itself.** For example, in Safari you can find them in **`/Applications/Safari.app/Contents/XPCServices`**. They have extension **`.xpc`** (like **`com.apple.Safari.SandboxBroker.xpc`**) and are **also bundles** with the main binary inside of it: `/Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker` and an `Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist` + +As you might be thinking a **XPC component will have different entitlements and privileges** than the other XPC components or the main app binary. EXCEPT if a XPC service is configured with [**JoinExistingSession**](https://developer.apple.com/documentation/bundleresources/information\_property\_list/xpcservice/joinexistingsession) set to “True” in its **Info.plist** file. In this case, the XPC service will run in the **same security session as the application** that called it. + +XPC services are **started** by **launchd** when required and **shut down** once all tasks are **complete** to free system resources. **Application-specific XPC components can only be utilized by the application**, thereby reducing the risk associated with potential vulnerabilities. + +## System Wide XPC services + +System-wide XPC services are accessible to all users. These services, either launchd or Mach-type, need to be **defined in plist** files located in specified directories such as **`/System/Library/LaunchDaemons`**, **`/Library/LaunchDaemons`**, **`/System/Library/LaunchAgents`**, or **`/Library/LaunchAgents`**. + +These plists files will have a key called **`MachServices`** with the name of the service, and a key called **`Program`** with the path to the binary: + +```xml +cat /Library/LaunchDaemons/com.jamf.management.daemon.plist + + + + + + Program + /Library/Application Support/JAMF/Jamf.app/Contents/MacOS/JamfDaemon.app/Contents/MacOS/JamfDaemon + AbandonProcessGroup + + KeepAlive + + Label + com.jamf.management.daemon + MachServices + + com.jamf.management.daemon.aad + + com.jamf.management.daemon.agent + + com.jamf.management.daemon.binary + + com.jamf.management.daemon.selfservice + + com.jamf.management.daemon.service + + + RunAtLoad + + + +``` + +The ones in **`LaunchDameons`** are run by root. So if an unprivileged process can talk with one of these it could be able to escalate privileges. + +## XPC Event Messages + +Applications can **subscribe** to different event **messages**, enabling them to be **initiated on-demand** when such events happen. The **setup** for these services is done in l**aunchd plist files**, located in the **same directories as the previous ones** and containing an extra **`LaunchEvent`** key. + +### XPC Connecting Process Check + +When a process tries to call a method from via an XPC connection, the **XPC service should check if that process is allowed to connect**. Here are the common ways to check that and the common pitfalls: + +{% content-ref url="macos-xpc-connecting-process-check.md" %} +[macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md) +{% endcontent-ref %} + +## XPC Authorization + +Apple also allows apps to **configure some rights and how to get them** so if the calling process have them it would be **allowed to call a method** from the XPC service: + +{% content-ref url="macos-xpc-authorization.md" %} +[macos-xpc-authorization.md](macos-xpc-authorization.md) +{% endcontent-ref %} + +## C Code Example + +{% tabs %} +{% tab title="xpc_server.c" %} +```c +// gcc xpc_server.c -o xpc_server + +#include + +static void handle_event(xpc_object_t event) { + if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) { + // Print received message + const char* received_message = xpc_dictionary_get_string(event, "message"); + printf("Received message: %s\n", received_message); + + // Create a response dictionary + xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_string(response, "received", "received"); + + // Send response + xpc_connection_t remote = xpc_dictionary_get_remote_connection(event); + xpc_connection_send_message(remote, response); + + // Clean up + xpc_release(response); + } +} + +static void handle_connection(xpc_connection_t connection) { + xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { + handle_event(event); + }); + xpc_connection_resume(connection); +} + +int main(int argc, const char *argv[]) { + xpc_connection_t service = xpc_connection_create_mach_service("xyz.hacktricks.service", + dispatch_get_main_queue(), + XPC_CONNECTION_MACH_SERVICE_LISTENER); + if (!service) { + fprintf(stderr, "Failed to create service.\n"); + exit(EXIT_FAILURE); + } + + xpc_connection_set_event_handler(service, ^(xpc_object_t event) { + xpc_type_t type = xpc_get_type(event); + if (type == XPC_TYPE_CONNECTION) { + handle_connection(event); + } + }); + + xpc_connection_resume(service); + dispatch_main(); + + return 0; +} +``` +{% endtab %} + +{% tab title="xpc_client.c" %} +```c +// gcc xpc_client.c -o xpc_client + +#include + +int main(int argc, const char *argv[]) { + xpc_connection_t connection = xpc_connection_create_mach_service("xyz.hacktricks.service", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED); + + xpc_connection_set_event_handler(connection, ^(xpc_object_t event) { + if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) { + // Print received message + const char* received_message = xpc_dictionary_get_string(event, "received"); + printf("Received message: %s\n", received_message); + } + }); + + xpc_connection_resume(connection); + + xpc_object_t message = xpc_dictionary_create(NULL, NULL, 0); + xpc_dictionary_set_string(message, "message", "Hello, Server!"); + + xpc_connection_send_message(connection, message); + + dispatch_main(); + + return 0; +} +``` +{% endtab %} + +{% tab title="xyz.hacktricks.service.plist" %} +```xml + + + +Label +xyz.hacktricks.service +MachServices + + xyz.hacktricks.service + + +Program + /tmp/xpc_server + ProgramArguments + + /tmp/xpc_server + + + +``` +{% endtab %} +{% endtabs %} + +```bash +# Compile the server & client +gcc xpc_server.c -o xpc_server +gcc xpc_client.c -o xpc_client + +# Save server on it's location +cp xpc_server /tmp + +# Load daemon +sudo cp xyz.hacktricks.service.plist /Library/LaunchDaemons +sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.service.plist + +# Call client +./xpc_client + +# Clean +sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.service.plist +sudo rm /Library/LaunchDaemons/xyz.hacktricks.service.plist /tmp/xpc_server +``` + +## Objective-C Code Example + +{% tabs %} +{% tab title="oc_xpc_server.m" %} +```objectivec +// gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server +#include + +@protocol MyXPCProtocol +- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply; +@end + +@interface MyXPCObject : NSObject +@end + + +@implementation MyXPCObject +- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply { + NSLog(@"Received message: %@", some_string); + NSString *response = @"Received"; + reply(response); +} +@end + +@interface MyDelegate : NSObject +@end + + +@implementation MyDelegate + +- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection { + newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]; + + MyXPCObject *my_object = [MyXPCObject new]; + + newConnection.exportedObject = my_object; + + [newConnection resume]; + return YES; +} +@end + +int main(void) { + + NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:@"xyz.hacktricks.svcoc"]; + + id delegate = [MyDelegate new]; + listener.delegate = delegate; + [listener resume]; + + sleep(10); // Fake something is done and then it ends +} +``` +{% endtab %} + +{% tab title="oc_xpc_client.m" %} +```objectivec +// gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client +#include + +@protocol MyXPCProtocol +- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply; +@end + +int main(void) { + NSXPCConnection *connection = [[NSXPCConnection alloc] initWithMachServiceName:@"xyz.hacktricks.svcoc" options:NSXPCConnectionPrivileged]; + connection.remoteObjectInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]; + [connection resume]; + + [[connection remoteObjectProxy] sayHello:@"Hello, Server!" withReply:^(NSString *response) { + NSLog(@"Received response: %@", response); + }]; + + [[NSRunLoop currentRunLoop] run]; + + return 0; +} +``` +{% endtab %} + +{% tab title="xyz.hacktricks.svcoc.plist" %} +```xml + + + +Label +xyz.hacktricks.svcoc +MachServices + + xyz.hacktricks.svcoc + + +Program + /tmp/oc_xpc_server + ProgramArguments + + /tmp/oc_xpc_server + + + +``` +{% endtab %} +{% endtabs %} + +```bash +# Compile the server & client +gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server +gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client + +# Save server on it's location +cp oc_xpc_server /tmp + +# Load daemon +sudo cp xyz.hacktricks.svcoc.plist /Library/LaunchDaemons +sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist + +# Call client +./oc_xpc_client + +# Clean +sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist +sudo rm /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist /tmp/oc_xpc_server +``` + +## Client inside a Dylb code + +```objectivec +// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib +// gcc injection example: +// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin + +#import + +@protocol MyXPCProtocol +- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply; +@end + +__attribute__((constructor)) +static void customConstructor(int argc, const char **argv) +{ + NSString* _serviceName = @"xyz.hacktricks.svcoc"; + + NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096]; + + [_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]]; + + [_agentConnection resume]; + + [[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) { + (void)error; + NSLog(@"Connection Failure"); + }] sayHello:@"Hello, Server!" withReply:^(NSString *response) { + NSLog(@"Received response: %@", response); + } ]; + NSLog(@"Done!"); + + return; +} +``` + +
+ +☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥 + +* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)! +* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family) +* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com) +* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.** +* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud). + +
diff --git a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-authorization.md b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-authorization.md similarity index 98% rename from macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-authorization.md rename to macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-authorization.md index 5e21dcea7..5edc84714 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-authorization.md +++ b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-authorization.md @@ -302,7 +302,7 @@ authenticate-session-owner, authenticate-session-owner-or-admin, authenticate-se If you find the function: **`[HelperTool checkAuthorization:command:]`** it's probably the the process is using the previously mentioned schema for authorization: -
+
Thisn, if this function is calling functions such as `AuthorizationCreateFromExternalForm`, `authorizationRightForCommand`, `AuthorizationCopyRights`, `AuhtorizationFree`, it's using [**EvenBetterAuthorizationSample**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L101-L154). @@ -314,7 +314,7 @@ Then, you need to find the protocol schema in order to be able to establish a co The function **`shouldAcceptNewConnection`** indicates the protocol being exported: -
+
In this case, we have the same as in EvenBetterAuthorizationSample, [**check this line**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L94). @@ -338,7 +338,7 @@ Lastly, we just need to know the **name of the exposed Mach Service** in order t * In the **`[HelperTool init]`** where you can see the Mach Service being used: -
+
* In the launchd plist: diff --git a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-connecting-process-check.md b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-connecting-process-check.md similarity index 97% rename from macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-connecting-process-check.md rename to macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-connecting-process-check.md index d02fef35f..24fb31650 100644 --- a/macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-connecting-process-check.md +++ b/macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-connecting-process-check.md @@ -32,8 +32,8 @@ When a connection is stablished to an XPC service, the server will check if the For more information about the PID reuse attack check: -{% content-ref url="macos-pid-reuse.md" %} -[macos-pid-reuse.md](macos-pid-reuse.md) +{% content-ref url="../../../mac-os-architecture/macos-ipc-inter-process-communication/macos-pid-reuse.md" %} +[macos-pid-reuse.md](../../../mac-os-architecture/macos-ipc-inter-process-communication/macos-pid-reuse.md) {% endcontent-ref %} ### Trustcache - Downgrade Attacks Prevention