macOS XPC

macOS XPC

Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Información Básica

XPC, que significa XNU (el kernel utilizado por macOS) inter-Process Communication, es un marco de trabajo para la comunicación entre procesos en macOS e iOS. XPC proporciona un mecanismo para realizar llamadas a métodos seguras y asíncronas entre diferentes procesos en el sistema. Es parte del paradigma de seguridad de Apple, permitiendo la creación de aplicaciones con separación de privilegios donde cada componente funciona con solo los permisos que necesita para realizar su trabajo, limitando así el daño potencial de un proceso comprometido.

XPC utiliza una forma de Comunicación Inter-Procesos (IPC), que es un conjunto de métodos para que diferentes programas que se ejecutan en el mismo sistema envíen y reciban datos.

Los beneficios principales de XPC incluyen:

  1. Seguridad: Al separar el trabajo en diferentes procesos, a cada proceso se le pueden otorgar solo los permisos que necesita. Esto significa que incluso si un proceso está comprometido, tiene una capacidad limitada para causar daño.

  2. Estabilidad: XPC ayuda a aislar los fallos al componente donde ocurren. Si un proceso se bloquea, puede reiniciarse sin afectar al resto del sistema.

  3. Rendimiento: XPC permite una fácil concurrencia, ya que diferentes tareas pueden ejecutarse simultáneamente en diferentes procesos.

El único inconveniente es que separar una aplicación en varios procesos que se comunican a través de XPC es menos eficiente. Pero en los sistemas actuales esto casi no se nota y los beneficios son mejores.

Servicios XPC Específicos de Aplicaciones

Los componentes XPC de una aplicación están dentro de la propia aplicación. Por ejemplo, en Safari puedes encontrarlos en /Applications/Safari.app/Contents/XPCServices. Tienen la extensión .xpc (como com.apple.Safari.SandboxBroker.xpc) y son también paquetes con el binario principal dentro de él: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker y un Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist

Como podrías estar pensando, un componente XPC tendrá diferentes derechos y privilegios que los otros componentes XPC o el binario principal de la aplicación. EXCEPTO si un servicio XPC está configurado con JoinExistingSession establecido en "True" en su archivo Info.plist. En este caso, el servicio XPC se ejecutará en la misma sesión de seguridad que la aplicación que lo llamó.

Los servicios XPC son iniciados por launchd cuando se requieren y se cierran una vez que todas las tareas están completas para liberar recursos del sistema. Los componentes XPC específicos de la aplicación solo pueden ser utilizados por la aplicación, reduciendo así el riesgo asociado con posibles vulnerabilidades.

Servicios XPC de Ámbito del Sistema

Los servicios XPC de ámbito del sistema están accesibles para todos los usuarios. Estos servicios, ya sean de tipo launchd o Mach, necesitan estar definidos en archivos plist ubicados en directorios especificados como /System/Library/LaunchDaemons, /Library/LaunchDaemons, /System/Library/LaunchAgents, o /Library/LaunchAgents.

Estos archivos plist tendrán una clave llamada MachServices con el nombre del servicio, y una clave llamada Program con la ruta al binario:

cat /Library/LaunchDaemons/com.jamf.management.daemon.plist

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Program</key>
<string>/Library/Application Support/JAMF/Jamf.app/Contents/MacOS/JamfDaemon.app/Contents/MacOS/JamfDaemon</string>
<key>AbandonProcessGroup</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>Label</key>
<string>com.jamf.management.daemon</string>
<key>MachServices</key>
<dict>
<key>com.jamf.management.daemon.aad</key>
<true/>
<key>com.jamf.management.daemon.agent</key>
<true/>
<key>com.jamf.management.daemon.binary</key>
<true/>
<key>com.jamf.management.daemon.selfservice</key>
<true/>
<key>com.jamf.management.daemon.service</key>
<true/>
</dict>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>

Los que están en LaunchDameons son ejecutados por root. Por lo tanto, si un proceso no privilegiado puede comunicarse con uno de estos, podría ser capaz de escalar privilegios.

Mensajes de Eventos XPC

Las aplicaciones pueden suscribirse a diferentes mensajes de eventos, lo que les permite ser iniciadas bajo demanda cuando ocurren dichos eventos. La configuración de estos servicios se realiza en archivos plist de launchd, ubicados en los mismos directorios que los anteriores y contienen una clave extra LaunchEvent.

Verificación del Proceso de Conexión XPC

Cuando un proceso intenta llamar a un método a través de una conexión XPC, el servicio XPC debería verificar si ese proceso tiene permiso para conectarse. Aquí están las formas comunes de verificarlo y las trampas comunes:

pagemacOS XPC Connecting Process Check

Autorización XPC

Apple también permite que las aplicaciones configuren algunos derechos y cómo obtenerlos para que, si el proceso que llama los tiene, se le permita llamar a un método del servicio XPC:

pagemacOS XPC Authorization

Sniffer XPC

Para espiar los mensajes XPC podrías usar xpcspy que utiliza Frida.

# Install
pip3 install xpcspy
pip3 install xpcspy --no-deps # To not make xpcspy install Frida 15 and downgrade your Frida installation

# Start sniffing
xpcspy -U -r -W <bundle-id>
## Using filters (i: for input, o: for output)
xpcspy -U <prog-name> -t 'i:com.apple.*' -t 'o:com.apple.*' -r

Ejemplo de Código C para Comunicación XPC

// gcc xpc_server.c -o xpc_server

#include <xpc/xpc.h>

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;
}
// gcc xpc_client.c -o xpc_client

#include <xpc/xpc.h>

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;
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0">
<dict>
<key>Label</key>
<string>xyz.hacktricks.service</string>
<key>MachServices</key>
<dict>
<key>xyz.hacktricks.service</key>
<true/>
</dict>
<key>Program</key>
<string>/tmp/xpc_server</string>
<key>ProgramArguments</key>
<array>
<string>/tmp/xpc_server</string>
</array>
</dict>
</plist>

No hay contenido en inglés proporcionado para traducir al español. Por favor, proporcione el texto en inglés que necesita ser traducido.

# 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

Ejemplo de Código en Objective-C para Comunicación XPC

// gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server
#include <Foundation/Foundation.h>

@protocol MyXPCProtocol
- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply;
@end

@interface MyXPCObject : NSObject <MyXPCProtocol>
@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 <NSXPCListenerDelegate>
@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 <NSXPCListenerDelegate> delegate = [MyDelegate new];
listener.delegate = delegate;
[listener resume];

sleep(10); // Fake something is done and then it ends
}

```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

## Cliente dentro de un código Dylb
```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 <Foundation/Foundation.h>

@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;
}
Aprende hacking en AWS de cero a héroe con htARTE (HackTricks AWS Red Team Expert)!

Otras formas de apoyar a HackTricks:

Última actualización