macOS XPC

Reading time: 13 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks

Informations de base

XPC, qui signifie XNU (le noyau utilisĂ© par macOS) inter-Process Communication, est un cadre pour la communication entre processus sur macOS et iOS. XPC fournit un mĂ©canisme pour effectuer des appels de mĂ©thode sĂ»rs et asynchrones entre diffĂ©rents processus sur le systĂšme. C'est une partie du paradigme de sĂ©curitĂ© d'Apple, permettant la crĂ©ation d'applications sĂ©parĂ©es par privilĂšges oĂč chaque composant fonctionne avec seulement les permissions dont il a besoin pour faire son travail, limitant ainsi les dommages potentiels d'un processus compromis.

XPC utilise une forme de communication inter-processus (IPC), qui est un ensemble de mĂ©thodes permettant Ă  diffĂ©rents programmes s'exĂ©cutant sur le mĂȘme systĂšme d'Ă©changer des donnĂ©es.

Les principaux avantages de XPC incluent :

  1. SĂ©curitĂ© : En sĂ©parant le travail en diffĂ©rents processus, chaque processus peut se voir accorder uniquement les permissions dont il a besoin. Cela signifie que mĂȘme si un processus est compromis, il a une capacitĂ© limitĂ©e Ă  causer des dommages.
  2. StabilitĂ© : XPC aide Ă  isoler les plantages au composant oĂč ils se produisent. Si un processus plante, il peut ĂȘtre redĂ©marrĂ© sans affecter le reste du systĂšme.
  3. Performance : XPC permet une concurrence facile, car diffĂ©rentes tĂąches peuvent ĂȘtre exĂ©cutĂ©es simultanĂ©ment dans diffĂ©rents processus.

Le seul inconvénient est que séparer une application en plusieurs processus les faisant communiquer via XPC est moins efficace. Mais dans les systÚmes d'aujourd'hui, cela n'est presque pas perceptible et les avantages sont meilleurs.

Services XPC spécifiques à l'application

Les composants XPC d'une application sont Ă  l'intĂ©rieur de l'application elle-mĂȘme. Par exemple, dans Safari, vous pouvez les trouver dans /Applications/Safari.app/Contents/XPCServices. Ils ont l'extension .xpc (comme com.apple.Safari.SandboxBroker.xpc) et sont Ă©galement des bundles avec le binaire principal Ă  l'intĂ©rieur : /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker et un Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist

Comme vous pourriez le penser, un composant XPC aura des droits et privilĂšges diffĂ©rents des autres composants XPC ou du binaire principal de l'application. SAUF si un service XPC est configurĂ© avec JoinExistingSession dĂ©fini sur “True” dans son fichier Info.plist. Dans ce cas, le service XPC s'exĂ©cutera dans la mĂȘme session de sĂ©curitĂ© que l'application qui l'a appelĂ©.

Les services XPC sont dĂ©marrĂ©s par launchd lorsque nĂ©cessaire et arrĂȘtĂ©s une fois toutes les tĂąches terminĂ©es pour libĂ©rer des ressources systĂšme. Les composants XPC spĂ©cifiques Ă  l'application ne peuvent ĂȘtre utilisĂ©s que par l'application, rĂ©duisant ainsi le risque associĂ© aux vulnĂ©rabilitĂ©s potentielles.

Services XPC Ă  l'Ă©chelle du systĂšme

Les services XPC Ă  l'Ă©chelle du systĂšme sont accessibles Ă  tous les utilisateurs. Ces services, soit launchd soit de type Mach, doivent ĂȘtre dĂ©finis dans des fichiers plist situĂ©s dans des rĂ©pertoires spĂ©cifiĂ©s tels que /System/Library/LaunchDaemons, /Library/LaunchDaemons, /System/Library/LaunchAgents, ou /Library/LaunchAgents.

Ces fichiers plist auront une clé appelée MachServices avec le nom du service, et une clé appelée Program avec le chemin vers le binaire :

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

Les Ă©lĂ©ments dans LaunchDameons sont exĂ©cutĂ©s par root. Donc, si un processus non privilĂ©giĂ© peut communiquer avec l'un d'eux, il pourrait ĂȘtre capable d'escalader les privilĂšges.

Objets XPC

  • xpc_object_t

Chaque message XPC est un objet dictionnaire qui simplifie la sĂ©rialisation et la dĂ©sĂ©rialisation. De plus, libxpc.dylib dĂ©clare la plupart des types de donnĂ©es, il est donc possible de s'assurer que les donnĂ©es reçues sont du type attendu. Dans l'API C, chaque objet est un xpc_object_t (et son type peut ĂȘtre vĂ©rifiĂ© en utilisant xpc_get_type(object)).
De plus, la fonction xpc_copy_description(object) peut ĂȘtre utilisĂ©e pour obtenir une reprĂ©sentation sous forme de chaĂźne de l'objet, ce qui peut ĂȘtre utile Ă  des fins de dĂ©bogage.
Ces objets ont également certaines méthodes à appeler comme xpc_<object>_copy, xpc_<object>_equal, xpc_<object>_hash, xpc_<object>_serialize, xpc_<object>_deserialize...

Les xpc_object_t sont crĂ©Ă©s en appelant la fonction xpc_<objetType>_create, qui appelle en interne _xpc_base_create(Class, Size) oĂč le type de la classe de l'objet (l'un de XPC_TYPE_*) et sa taille sont indiquĂ©s (40B supplĂ©mentaires seront ajoutĂ©s Ă  la taille pour les mĂ©tadonnĂ©es). Ce qui signifie que les donnĂ©es de l'objet commenceront Ă  l'offset de 40B.
Par conséquent, le xpc_<objectType>_t est en quelque sorte une sous-classe du xpc_object_t qui serait une sous-classe de os_object_t*.

warning

Notez qu'il devrait ĂȘtre le dĂ©veloppeur qui utilise xpc_dictionary_[get/set]_<objectType> pour obtenir ou dĂ©finir le type et la valeur rĂ©elle d'une clĂ©.

  • xpc_pipe

Un xpc_pipe est un tuyau FIFO que les processus peuvent utiliser pour communiquer (la communication utilise des messages Mach).
Il est possible de créer un serveur XPC en appelant xpc_pipe_create() ou xpc_pipe_create_from_port() pour le créer en utilisant un port Mach spécifique. Ensuite, pour recevoir des messages, il est possible d'appeler xpc_pipe_receive et xpc_pipe_try_receive.

Notez que l'objet xpc_pipe est un xpc_object_t avec des informations dans sa structure sur les deux ports Mach utilisés et le nom (le cas échéant). Le nom, par exemple, le démon secinitd dans son plist /System/Library/LaunchDaemons/com.apple.secinitd.plist configure le tuyau appelé com.apple.secinitd.

Un exemple de xpc_pipe est le bootstrap pipe créé par launchd rendant possible le partage des ports Mach.

  • NSXPC*

Ce sont des objets de haut niveau Objective-C qui permettent l'abstraction des connexions XPC.
De plus, il est plus facile de déboguer ces objets avec DTrace que les précédents.

  • GCD Queues

XPC utilise GCD pour passer des messages, de plus, il génÚre certaines files d'attente de dispatch comme xpc.transactionq, xpc.io, xpc-events.add-listenerq, xpc.service-instance...

Services XPC

Ce sont des bundles avec l'extension .xpc situés dans le dossier XPCServices d'autres projets et dans le Info.plist, ils ont le CFBundlePackageType défini sur XPC!.
Ce fichier a d'autres clĂ©s de configuration comme ServiceType qui peut ĂȘtre Application, User, System ou _SandboxProfile qui peut dĂ©finir un sandbox ou _AllowedClients qui pourrait indiquer des droits ou des ID requis pour contacter le service. Ces options de configuration et d'autres seront utiles pour configurer le service lors de son lancement.

DĂ©marrer un Service

L'application tente de se connecter à un service XPC en utilisant xpc_connection_create_mach_service, puis launchd localise le démon et démarre xpcproxy. xpcproxy applique les restrictions configurées et crée le service avec les FDs et ports Mach fournis.

Afin d'améliorer la vitesse de recherche du service XPC, un cache est utilisé.

Il est possible de tracer les actions de xpcproxy en utilisant :

bash
supraudit S -C -o /tmp/output /dev/auditpipe

La bibliothÚque XPC utilise kdebug pour enregistrer des actions en appelant xpc_ktrace_pid0 et xpc_ktrace_pid1. Les codes qu'elle utilise ne sont pas documentés, il est donc nécessaire de les ajouter dans /usr/share/misc/trace.codes. Ils ont le préfixe 0x29 et par exemple, l'un d'eux est 0x29000004: XPC_serializer_pack.
L'utilitaire xpcproxy utilise le préfixe 0x22, par exemple : 0x2200001c: xpcproxy:will_do_preexec.

Messages d'événements XPC

Les applications peuvent s'abonner Ă  diffĂ©rents messages d'Ă©vĂ©nements, leur permettant d'ĂȘtre initiĂ©s Ă  la demande lorsque de tels Ă©vĂ©nements se produisent. La configuration de ces services se fait dans les fichiers plist launchd, situĂ©s dans les mĂȘmes rĂ©pertoires que les prĂ©cĂ©dents et contenant une clĂ© LaunchEvent supplĂ©mentaire.

VĂ©rification du processus de connexion XPC

Lorsqu'un processus essaie d'appeler une méthode via une connexion XPC, le service XPC doit vérifier si ce processus est autorisé à se connecter. Voici les méthodes courantes pour vérifier cela et les piÚges courants :

macOS XPC Connecting Process Check

Autorisation XPC

Apple permet également aux applications de configurer certains droits et comment les obtenir, donc si le processus appelant les a, il serait autorisé à appeler une méthode du service XPC :

macOS XPC Authorization

Sniffer XPC

Pour intercepter les messages XPC, vous pouvez utiliser xpcspy qui utilise Frida.

bash
# 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

Un autre outil possible Ă  utiliser est XPoCe2.

Exemple de code C pour la communication XPC

c
// 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;
}
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

Exemple de code Objective-C pour la communication XPC

objectivec
// 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

Client à l'intérieur d'un code 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;
}

Remote XPC

Cette fonctionnalité fournie par RemoteXPC.framework (de libxpc) permet de communiquer via XPC entre différents hÎtes.
Les services qui prennent en charge le XPC distant auront dans leur plist la clé UsesRemoteXPC comme c'est le cas de /System/Library/LaunchDaemons/com.apple.SubmitDiagInfo.plist. Cependant, bien que le service soit enregistré avec launchd, c'est UserEventAgent avec les plugins com.apple.remoted.plugin et com.apple.remoteservicediscovery.events.plugin qui fournit la fonctionnalité.

De plus, le RemoteServiceDiscovery.framework permet d'obtenir des informations Ă  partir du com.apple.remoted.plugin exposant des fonctions telles que get_device, get_unique_device, connect...

Une fois que connect est utilisé et que le socket fd du service est récupéré, il est possible d'utiliser la classe remote_xpc_connection_*.

Il est possible d'obtenir des informations sur les services distants en utilisant l'outil cli /usr/libexec/remotectl avec des paramĂštres tels que :

bash
/usr/libexec/remotectl list # Get bridge devices
/usr/libexec/remotectl show ...# Get device properties and services
/usr/libexec/remotectl dumpstate # Like dump withuot indicateing a servie
/usr/libexec/remotectl [netcat|relay] ... # Expose a service in a port
...

La communication entre BridgeOS et l'hÎte se fait via une interface IPv6 dédiée. Le MultiverseSupport.framework permet d'établir des sockets dont le fd sera utilisé pour communiquer.
Il est possible de trouver ces communications en utilisant netstat, nettop ou l'option open source, netbottom.

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)

Soutenir HackTricks