macOS XPC

Reading time: 14 minutes

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Basic Information

XPC, που σημαίνει XNU (ο πυρήνας που χρησιμοποιείται από το macOS) inter-Process Communication, είναι ένα πλαίσιο για επικοινωνία μεταξύ διεργασιών στο macOS και το iOS. Το XPC παρέχει έναν μηχανισμό για την πραγματοποίηση ασφαλών, ασύγχρονων κλήσεων μεθόδων μεταξύ διαφορετικών διεργασιών στο σύστημα. Είναι μέρος της ασφάλειας της Apple, επιτρέποντας τη δημιουργία εφαρμογών με διαχωρισμένα δικαιώματα όπου κάθε συστατικό εκτελείται με μόνο τα δικαιώματα που χρειάζεται για να κάνει τη δουλειά του, περιορίζοντας έτσι τη δυνητική ζημιά από μια συμβιβασμένη διεργασία.

Το XPC χρησιμοποιεί μια μορφή Επικοινωνίας Μεταξύ Διεργασιών (IPC), η οποία είναι ένα σύνολο μεθόδων για διαφορετικά προγράμματα που εκτελούνται στο ίδιο σύστημα να στέλνουν δεδομένα πίσω και μπροστά.

Τα κύρια οφέλη του XPC περιλαμβάνουν:

  1. Ασφάλεια: Με τον διαχωρισμό της εργασίας σε διαφορετικές διεργασίες, κάθε διεργασία μπορεί να έχει μόνο τα δικαιώματα που χρειάζεται. Αυτό σημαίνει ότι ακόμη και αν μια διεργασία συμβιβαστεί, έχει περιορισμένη ικανότητα να προκαλέσει ζημιά.
  2. Σταθερότητα: Το XPC βοηθά στην απομόνωση των κραστών στο συστατικό όπου συμβαίνουν. Αν μια διεργασία κρασάρει, μπορεί να επανεκκινηθεί χωρίς να επηρεάσει το υπόλοιπο σύστημα.
  3. Απόδοση: Το XPC επιτρέπει εύκολη ταυτόχρονη εκτέλεση, καθώς διαφορετικές εργασίες μπορούν να εκτελούνται ταυτόχρονα σε διαφορετικές διεργασίες.

Η μόνη ανεπιθύμητη συνέπεια είναι ότι ο διαχωρισμός μιας εφαρμογής σε πολλές διεργασίες που επικοινωνούν μέσω XPC είναι λιγότερο αποδοτικός. Αλλά στα σημερινά συστήματα αυτό δεν είναι σχεδόν αισθητό και τα οφέλη είναι καλύτερα.

Application Specific XPC services

Τα XPC συστατικά μιας εφαρμογής είναι μέσα στην ίδια την εφαρμογή. Για παράδειγμα, στο Safari μπορείτε να τα βρείτε σε /Applications/Safari.app/Contents/XPCServices. Έχουν επέκταση .xpc (όπως com.apple.Safari.SandboxBroker.xpc) και είναι επίσης πακέτα με το κύριο δυαδικό αρχείο μέσα σε αυτό: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker και ένα Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist

Όπως μπορεί να σκέφτεστε, ένα συστατικό XPC θα έχει διαφορετικά δικαιώματα και προνόμια από τα άλλα συστατικά XPC ή το κύριο δυαδικό αρχείο της εφαρμογής. ΕΚΤΟΣ αν μια υπηρεσία XPC έχει ρυθμιστεί με JoinExistingSession ρυθμισμένο σε “True” στο αρχείο Info.plist της. Σε αυτή την περίπτωση, η υπηρεσία XPC θα εκτελείται στην ίδια ασφαλή συνεδρία με την εφαρμογή που την κάλεσε.

Οι υπηρεσίες XPC ξεκινούνται από launchd όταν απαιτείται και κλείνουν μόλις ολοκληρωθούν όλες οι εργασίες για να απελευθερωθούν οι πόροι του συστήματος. Τα XPC συστατικά που σχετίζονται με την εφαρμογή μπορούν να χρησιμοποιηθούν μόνο από την εφαρμογή, μειώνοντας έτσι τον κίνδυνο που σχετίζεται με πιθανές ευπάθειες.

System Wide XPC services

Οι υπηρεσίες XPC σε επίπεδο συστήματος είναι προσβάσιμες σε όλους τους χρήστες. Αυτές οι υπηρεσίες, είτε launchd είτε τύπου Mach, πρέπει να είναι καθορισμένες σε αρχεία plist που βρίσκονται σε καθορισμένους καταλόγους όπως /System/Library/LaunchDaemons, /Library/LaunchDaemons, /System/Library/LaunchAgents, ή /Library/LaunchAgents.

Αυτά τα αρχεία plist θα έχουν ένα κλειδί που ονομάζεται MachServices με το όνομα της υπηρεσίας, και ένα κλειδί που ονομάζεται Program με τη διαδρομή προς το δυαδικό αρχείο:

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>

Οι διαδικασίες στο LaunchDameons εκτελούνται από τον root. Έτσι, αν μια διαδικασία χωρίς δικαιώματα μπορεί να επικοινωνήσει με μία από αυτές, θα μπορούσε να είναι σε θέση να κλιμακώσει τα δικαιώματα.

XPC Αντικείμενα

  • xpc_object_t

Κάθε μήνυμα XPC είναι ένα αντικείμενο λεξικού που απλοποιεί τη σειριοποίηση και την αποσειριοποίηση. Επιπλέον, η libxpc.dylib δηλώνει τους περισσότερους τύπους δεδομένων, οπότε είναι δυνατό να διασφαλιστεί ότι τα ληφθέντα δεδομένα είναι του αναμενόμενου τύπου. Στο C API, κάθε αντικείμενο είναι ένα xpc_object_t (και ο τύπος του μπορεί να ελεγχθεί χρησιμοποιώντας xpc_get_type(object)).
Επιπλέον, η συνάρτηση xpc_copy_description(object) μπορεί να χρησιμοποιηθεί για να αποκτήσει μια συμβολοσειρά αναπαράστασης του αντικειμένου που μπορεί να είναι χρήσιμη για σκοπούς αποσφαλμάτωσης.
Αυτά τα αντικείμενα έχουν επίσης κάποιες μεθόδους που μπορούν να κληθούν όπως xpc_<object>_copy, xpc_<object>_equal, xpc_<object>_hash, xpc_<object>_serialize, xpc_<object>_deserialize...

Τα xpc_object_t δημιουργούνται καλώντας τη συνάρτηση xpc_<objetType>_create, η οποία εσωτερικά καλεί το _xpc_base_create(Class, Size) όπου υποδεικνύεται ο τύπος της κλάσης του αντικειμένου (ένας από τους XPC_TYPE_*) και το μέγεθός του (κάποια επιπλέον 40B θα προστεθούν στο μέγεθος για μεταδεδομένα). Αυτό σημαίνει ότι τα δεδομένα του αντικειμένου θα ξεκινούν από την απόσταση 40B.
Επομένως, το xpc_<objectType>_t είναι κάπως μια υποκλάση του xpc_object_t, η οποία θα ήταν μια υποκλάση του os_object_t*.

warning

Σημειώστε ότι θα πρέπει να είναι ο προγραμματιστής που χρησιμοποιεί xpc_dictionary_[get/set]_<objectType> για να αποκτήσει ή να ορίσει τον τύπο και την πραγματική τιμή ενός κλειδιού.

  • xpc_pipe

Ένα xpc_pipe είναι ένας σωλήνας FIFO που οι διαδικασίες μπορούν να χρησιμοποιήσουν για να επικοινωνήσουν (η επικοινωνία χρησιμοποιεί μηνύματα Mach).
Είναι δυνατό να δημιουργηθεί ένας XPC server καλώντας xpc_pipe_create() ή xpc_pipe_create_from_port() για να τον δημιουργήσει χρησιμοποιώντας μια συγκεκριμένη θύρα Mach. Στη συνέχεια, για να λάβει μηνύματα, είναι δυνατό να καλέσει xpc_pipe_receive και xpc_pipe_try_receive.

Σημειώστε ότι το αντικείμενο xpc_pipe είναι ένα xpc_object_t με πληροφορίες στη δομή του σχετικά με τις δύο θύρες Mach που χρησιμοποιούνται και το όνομα (αν υπάρχει). Το όνομα, για παράδειγμα, ο daemon secinitd στο plist του /System/Library/LaunchDaemons/com.apple.secinitd.plist ρυθμίζει τον σωλήνα που ονομάζεται com.apple.secinitd.

Ένα παράδειγμα ενός xpc_pipe είναι ο bootstrap pipe που δημιουργείται από τον launchd καθιστώντας δυνατή την κοινή χρήση θύρων Mach.

  • NSXPC*

Αυτά είναι αντικείμενα υψηλού επιπέδου Objective-C που επιτρέπουν την αφαίρεση των συνδέσεων XPC.
Επιπλέον, είναι πιο εύκολο να αποσφαλματωθούν αυτά τα αντικείμενα με το DTrace από τα προηγούμενα.

  • GCD Queues

Το XPC χρησιμοποιεί GCD για να περάσει μηνύματα, επιπλέον δημιουργεί ορισμένες ουρές εκτέλεσης όπως xpc.transactionq, xpc.io, xpc-events.add-listenerq, xpc.service-instance...

Υπηρεσίες XPC

Αυτές είναι πακέτα με επέκταση .xpc που βρίσκονται μέσα στον φάκελο XPCServices άλλων έργων και στο Info.plist έχουν τον τύπο πακέτου CFBundlePackageType ρυθμισμένο σε XPC!.
Αυτό το αρχείο έχει άλλες ρυθμιστικές κλειδιά όπως ServiceType που μπορεί να είναι Application, User, System ή _SandboxProfile που μπορεί να ορίσει ένα sandbox ή _AllowedClients που μπορεί να υποδεικνύει δικαιώματα ή ID που απαιτούνται για να επικοινωνήσουν με την υπηρεσία. Αυτές και άλλες ρυθμιστικές επιλογές θα είναι χρήσιμες για να ρυθμίσουν την υπηρεσία κατά την εκκίνηση.

Εκκίνηση μιας Υπηρεσίας

Η εφαρμογή προσπαθεί να συνδεθεί με μια υπηρεσία XPC χρησιμοποιώντας xpc_connection_create_mach_service, στη συνέχεια ο launchd εντοπίζει τον daemon και εκκινεί τον xpcproxy. Ο xpcproxy επιβάλλει τις ρυθμισμένες περιορισμούς και δημιουργεί την υπηρεσία με τις παρεχόμενες FDs και θύρες Mach.

Για να βελτιωθεί η ταχύτητα αναζήτησης της υπηρεσίας XPC, χρησιμοποιείται μια κρυφή μνήμη.

Είναι δυνατό να παρακολουθήσετε τις ενέργειες του xpcproxy χρησιμοποιώντας:

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

Η βιβλιοθήκη XPC χρησιμοποιεί kdebug για να καταγράψει ενέργειες καλώντας xpc_ktrace_pid0 και xpc_ktrace_pid1. Οι κωδικοί που χρησιμοποιεί δεν είναι τεκμηριωμένοι, επομένως είναι απαραίτητο να τους προσθέσετε στο /usr/share/misc/trace.codes. Έχουν το πρόθεμα 0x29 και για παράδειγμα ένας είναι 0x29000004: XPC_serializer_pack.
Το εργαλείο xpcproxy χρησιμοποιεί το πρόθεμα 0x22, για παράδειγμα: 0x2200001c: xpcproxy:will_do_preexec.

XPC Event Messages

Οι εφαρμογές μπορούν να εγγραφούν σε διάφορα γεγονότα μηνυμάτων, επιτρέποντάς τους να ξεκινούν κατόπιν αιτήματος όταν συμβαίνουν τέτοια γεγονότα. Η ρύθμιση για αυτές τις υπηρεσίες γίνεται σε αρχεία plist του launchd, που βρίσκονται στους ίδιους καταλόγους με τους προηγούμενους και περιέχουν ένα επιπλέον LaunchEvent κλειδί.

XPC Connecting Process Check

Όταν μια διαδικασία προσπαθεί να καλέσει μια μέθοδο μέσω μιας σύνδεσης XPC, η υπηρεσία XPC θα πρέπει να ελέγξει αν αυτή η διαδικασία επιτρέπεται να συνδεθεί. Ακολουθούν οι κοινές μέθοδοι για να το ελέγξετε και οι κοινές παγίδες:

macOS XPC Connecting Process Check

XPC Authorization

Η Apple επιτρέπει επίσης στις εφαρμογές να ρυθμίζουν ορισμένα δικαιώματα και πώς να τα αποκτούν, έτσι ώστε αν η καλούσα διαδικασία τα έχει, θα επιτρέπεται να καλέσει μια μέθοδο από την υπηρεσία XPC:

macOS XPC Authorization

XPC Sniffer

Για να καταγράψετε τα μηνύματα XPC, μπορείτε να χρησιμοποιήσετε xpcspy που χρησιμοποιεί 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

Ένα άλλο πιθανό εργαλείο που μπορείτε να χρησιμοποιήσετε είναι το XPoCe2.

Παράδειγμα Κώδικα C για Επικοινωνία 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

XPC Επικοινωνία Παράδειγμα Κώδικα Objective-C

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

Πελάτης μέσα σε έναν κώδικα 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

Αυτή η λειτουργία που παρέχεται από το RemoteXPC.framework (από το libxpc) επιτρέπει την επικοινωνία μέσω XPC μέσω διαφορετικών hosts.
Οι υπηρεσίες που υποστηρίζουν το remote XPC θα έχουν στο plist τους το κλειδί UsesRemoteXPC όπως είναι η περίπτωση του /System/Library/LaunchDaemons/com.apple.SubmitDiagInfo.plist. Ωστόσο, αν και η υπηρεσία θα είναι καταχωρημένη με το launchd, είναι το UserEventAgent με τα plugins com.apple.remoted.plugin και com.apple.remoteservicediscovery.events.plugin που παρέχει τη λειτουργικότητα.

Επιπλέον, το RemoteServiceDiscovery.framework επιτρέπει την απόκτηση πληροφοριών από το com.apple.remoted.plugin εκθέτοντας συναρτήσεις όπως get_device, get_unique_device, connect...

Μόλις χρησιμοποιηθεί το connect και συγκεντρωθεί το socket fd της υπηρεσίας, είναι δυνατή η χρήση της κλάσης remote_xpc_connection_*.

Είναι δυνατή η απόκτηση πληροφοριών σχετικά με τις απομακρυσμένες υπηρεσίες χρησιμοποιώντας το cli εργαλείο /usr/libexec/remotectl χρησιμοποιώντας παραμέτρους όπως:

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

Η επικοινωνία μεταξύ του BridgeOS και του κεντρικού υπολογιστή πραγματοποιείται μέσω μιας ειδικής διεπαφής IPv6. Το MultiverseSupport.framework επιτρέπει τη δημιουργία υποδοχών των οποίων το fd θα χρησιμοποιηθεί για την επικοινωνία.
Είναι δυνατή η εύρεση αυτών των επικοινωνιών χρησιμοποιώντας το netstat, nettop ή την ανοιχτού κώδικα επιλογή, netbottom.

tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Μάθετε & εξασκηθείτε στο Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks