mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-22 04:33:28 +00:00
GITBOOK-4113: change request with no subject merged in GitBook
This commit is contained in:
parent
1b438ba055
commit
3c814157ac
6 changed files with 777 additions and 709 deletions
|
@ -161,10 +161,12 @@
|
|||
* [macOS Objective-C](macos-hardening/macos-security-and-privilege-escalation/macos-basic-objective-c.md)
|
||||
* [macOS Proces Abuse](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/README.md)
|
||||
* [macOS IPC - Inter Process Communication](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/README.md)
|
||||
* [macOS MIG - Mach Interface Generator](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md)
|
||||
* [macOS XPC](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/README.md)
|
||||
* [macOS XPC Authorization](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-authorization.md)
|
||||
* [macOS XPC Connecting Process Check](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/macos-xpc-connecting-process-check.md)
|
||||
* [macOS PID Reuse](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-pid-reuse.md)
|
||||
* [macOS Thread Injection via Task port](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-ipc-inter-process-communication/macos-thread-injection-via-task-port.md)
|
||||
* [macOS XPC Authorization](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-authorization.md)
|
||||
* [macOS XPC Connecting Process Check](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-ipc-inter-process-communication/macos-xpc-connecting-process-check.md)
|
||||
* [macOS Electron Applications Injection](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-electron-applications-injection.md)
|
||||
* [macOS Function Hooking](macos-hardening/macos-security-and-privilege-escalation/mac-os-architecture/macos-function-hooking.md)
|
||||
* [macOS .Net Applications Injection](macos-hardening/macos-security-and-privilege-escalation/macos-proces-abuse/macos-.net-applications-injection.md)
|
||||
|
|
|
@ -770,716 +770,21 @@ In this technique a thread of the process is hijacked:
|
|||
|
||||
XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for **communication between processes** on macOS and iOS. XPC provides a mechanism for making **safe, asynchronous method calls between different processes** on the system. It's a part of Apple's security paradigm, allowing for the **creation of privilege-separated applications** where each **component** runs with **only the permissions it needs** to do its job, thereby limiting the potential damage from a compromised process.
|
||||
|
||||
XPC uses a form of Inter-Process Communication (IPC), which is a set of methods for different programs running on the same system to send data back and forth.
|
||||
For more information about how this **communication work** on how it **could be vulnerable** check:
|
||||
|
||||
The primary benefits of XPC include:
|
||||
|
||||
1. **Security**: By separating work into different processes, each process can be granted only the permissions it needs. This means that even if a process is compromised, it has limited ability to do harm.
|
||||
2. **Stability**: XPC helps isolate crashes to the component where they occur. If a process crashes, it can be restarted without affecting the rest of the system.
|
||||
3. **Performance**: XPC allows for easy concurrency, as different tasks can be run simultaneously in different processes.
|
||||
|
||||
The only **drawback** is that **separating an application in several processes** making them communicate via XPC is **less efficient**. But in todays systems this isn't almost noticeable and the benefits are better.
|
||||
|
||||
### Application Specific XPC services
|
||||
|
||||
The XPC components of an application are **inside the application itself.** For example, in Safari you can find them in **`/Applications/Safari.app/Contents/XPCServices`**. They have extension **`.xpc`** (like **`com.apple.Safari.SandboxBroker.xpc`**) and are **also bundles** with the main binary inside of it: `/Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker` and an `Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist`
|
||||
|
||||
As you might be thinking a **XPC component will have different entitlements and privileges** than the other XPC components or the main app binary. EXCEPT if a XPC service is configured with [**JoinExistingSession**](https://developer.apple.com/documentation/bundleresources/information\_property\_list/xpcservice/joinexistingsession) set to “True” in its **Info.plist** file. In this case, the XPC service will run in the **same security session as the application** that called it.
|
||||
|
||||
XPC services are **started** by **launchd** when required and **shut down** once all tasks are **complete** to free system resources. **Application-specific XPC components can only be utilized by the application**, thereby reducing the risk associated with potential vulnerabilities.
|
||||
|
||||
### System Wide XPC services
|
||||
|
||||
System-wide XPC services are accessible to all users. These services, either launchd or Mach-type, need to be **defined in plist** files located in specified directories such as **`/System/Library/LaunchDaemons`**, **`/Library/LaunchDaemons`**, **`/System/Library/LaunchAgents`**, or **`/Library/LaunchAgents`**.
|
||||
|
||||
These plists files will have a key called **`MachServices`** with the name of the service, and a key called **`Program`** with the path to the binary:
|
||||
|
||||
```xml
|
||||
cat /Library/LaunchDaemons/com.jamf.management.daemon.plist
|
||||
|
||||
<?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>
|
||||
```
|
||||
|
||||
The ones in **`LaunchDameons`** are run by root. So if an unprivileged process can talk with one of these it could be able to escalate privileges.
|
||||
|
||||
### XPC Event Messages
|
||||
|
||||
Applications can **subscribe** to different event **messages**, enabling them to be **initiated on-demand** when such events happen. The **setup** for these services is done in l**aunchd plist files**, located in the **same directories as the previous ones** and containing an extra **`LaunchEvent`** key.
|
||||
|
||||
### XPC Connecting Process Check
|
||||
|
||||
When a process tries to call a method from via an XPC connection, the **XPC service should check if that process is allowed to connect**. Here are the common ways to check that and the common pitfalls:
|
||||
|
||||
{% content-ref url="macos-xpc-connecting-process-check.md" %}
|
||||
[macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md)
|
||||
{% content-ref url="../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/" %}
|
||||
[macos-xpc](../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-xpc/)
|
||||
{% endcontent-ref %}
|
||||
|
||||
### XPC Authorization
|
||||
|
||||
Apple also allows apps to **configure some rights and how to get them** so if the calling process have them it would be **allowed to call a method** from the XPC service:
|
||||
|
||||
{% content-ref url="macos-xpc-authorization.md" %}
|
||||
[macos-xpc-authorization.md](macos-xpc-authorization.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
### C Code Example
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="xpc_server.c" %}
|
||||
```c
|
||||
// gcc xpc_server.c -o xpc_server
|
||||
|
||||
#include <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;
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="xpc_client.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;
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% 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
|
||||
```
|
||||
|
||||
### ObjectiveC Code Example
|
||||
|
||||
{% 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
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% 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;
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% 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 inside a Dylib code
|
||||
|
||||
```
|
||||
// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib
|
||||
// gcc injection example:
|
||||
// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin
|
||||
|
||||
#import <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 - Mach Interface Generator
|
||||
|
||||
MIG was created to **simplify the process of Mach IPC** code creation. It basically **generates the needed code** for server and client to communicate with a given definition. Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before.
|
||||
|
||||
### Example
|
||||
For more info check:
|
||||
|
||||
Create a definition file, in this case with a very simple function:
|
||||
|
||||
{% code title="myipc.defs" %}
|
||||
```cpp
|
||||
subsystem myipc 500; // Arbitrary name and id
|
||||
|
||||
userprefix USERPREF; // Prefix for created functions in the client
|
||||
serverprefix SERVERPREF; // Prefix for created functions in the server
|
||||
|
||||
#include <mach/mach_types.defs>
|
||||
#include <mach/std_types.defs>
|
||||
|
||||
simpleroutine Subtract(
|
||||
server_port : mach_port_t;
|
||||
n1 : uint32_t;
|
||||
n2 : uint32_t);
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
Now use mig to generate the server and client code that will be able to comunicate within each other to call the Subtract function:
|
||||
|
||||
```bash
|
||||
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
|
||||
```
|
||||
|
||||
Several new files will be created in the current directory.
|
||||
|
||||
In the files **`myipcServer.c`** and **`myipcServer.h`** you can find the declaration and definition of the struct **`SERVERPREFmyipc_subsystem`**, which basically defines the function to call based on the received message ID (we indicated a starting number of 500):
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="myipcServer.c" %}
|
||||
```c
|
||||
/* Description of this subsystem, for use in direct RPC */
|
||||
const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = {
|
||||
myipc_server_routine,
|
||||
500, // start ID
|
||||
501, // end ID
|
||||
(mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem),
|
||||
(vm_address_t)0,
|
||||
{
|
||||
{ (mig_impl_routine_t) 0,
|
||||
// Function to call
|
||||
(mig_stub_routine_t) _XSubtract, 3, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)},
|
||||
}
|
||||
};
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="myipcServer.h" %}
|
||||
```c
|
||||
/* Description of this subsystem, for use in direct RPC */
|
||||
extern const struct SERVERPREFmyipc_subsystem {
|
||||
mig_server_routine_t server; /* Server routine */
|
||||
mach_msg_id_t start; /* Min routine number */
|
||||
mach_msg_id_t end; /* Max routine number + 1 */
|
||||
unsigned int maxsize; /* Max msg size */
|
||||
vm_address_t reserved; /* Reserved */
|
||||
struct routine_descriptor /* Array of routine descriptors */
|
||||
routine[1];
|
||||
} SERVERPREFmyipc_subsystem;
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
Based on the previous struct the function **`myipc_server_routine`** will get the **message ID** and return the proper function to call:
|
||||
|
||||
```c
|
||||
mig_external mig_routine_t myipc_server_routine
|
||||
(mach_msg_header_t *InHeadP)
|
||||
{
|
||||
int msgh_id;
|
||||
|
||||
msgh_id = InHeadP->msgh_id - 500;
|
||||
|
||||
if ((msgh_id > 0) || (msgh_id < 0))
|
||||
return 0;
|
||||
|
||||
return SERVERPREFmyipc_subsystem.routine[msgh_id].stub_routine;
|
||||
}
|
||||
```
|
||||
|
||||
In this example we only defined 1 function in the definitions, but if we would have defined more, the would have been inside the array of **`SERVERPREFmyipc_subsystem`** and the first one will be assigned to the ID **500**, the second one to the ID **501**...
|
||||
|
||||
Actually it's possible to identify this relation in the struct **`subsystem_to_name_map_myipc`** from **`myipcServer.h`**:
|
||||
|
||||
```c
|
||||
#ifndef subsystem_to_name_map_myipc
|
||||
#define subsystem_to_name_map_myipc \
|
||||
{ "Subtract", 500 }
|
||||
#endif
|
||||
```
|
||||
|
||||
Finally, another important function to make the server work will be **`myipc_server`**, which is the one that will actually **call the function** related to the received id:
|
||||
|
||||
<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;
|
||||
/* Minimal size: routine() will update it if different */
|
||||
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
|
||||
OutHeadP->msgh_local_port = MACH_PORT_NULL;
|
||||
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
|
||||
OutHeadP->msgh_reserved = 0;
|
||||
|
||||
if ((InHeadP->msgh_id > 500) || (InHeadP->msgh_id < 500) ||
|
||||
<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>
|
||||
|
||||
Check the following code to use the generated code to create a simple server and client where the client can call the functions Subtract from the server:
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="myipc_server.c" %}
|
||||
```c
|
||||
// gcc myipc_server.c myipcServer.c -o myipc_server
|
||||
|
||||
#include <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);
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="myipc_client.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);
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
## Binary Analysis
|
||||
|
||||
As many binaries now use MIG to expose mach ports, it's interesting to know how to **identify that MIG was used** and the **functions that MIG executes** with each message ID.
|
||||
|
||||
[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) can parse MIG information from a Mach-O binary indicating the message ID and identifying the function to execute:
|
||||
|
||||
```bash
|
||||
jtool2 -d __DATA.__const myipc_server | grep MIG
|
||||
```
|
||||
|
||||
It was previously mentioned that the function that will take care of **calling the correct function depending on the received message ID** was `myipc_server`. However, you usually won't have the symbols of the binary (no functions names), so it's interesting to **check how it looks like decompiled** as it will always be very similar (the code of this function is independent from the functions exposed):
|
||||
|
||||
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="myipc_server decompiled 1" %}
|
||||
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||||
var_10 = arg0;
|
||||
var_18 = arg1;
|
||||
// Initial instructions to find the proper function ponters
|
||||
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f;
|
||||
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
|
||||
*(int32_t *)(var_18 + 0x4) = 0x24;
|
||||
*(int32_t *)(var_18 + 0xc) = 0x0;
|
||||
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
|
||||
*(int32_t *)(var_18 + 0x10) = 0x0;
|
||||
if (*(int32_t *)(var_10 + 0x14) <= 0x1f4 && *(int32_t *)(var_10 + 0x14) >= 0x1f4) {
|
||||
rax = *(int32_t *)(var_10 + 0x14);
|
||||
// Call to sign_extend_64 that can help to identifyf this function
|
||||
// This stores in rax the pointer to the call that needs to be called
|
||||
// Check the used of the address 0x100004040 (functions addresses array)
|
||||
// 0x1f4 = 500 (the strating ID)
|
||||
<strong> rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
|
||||
</strong> var_20 = rax;
|
||||
// If - else, the if returns false, while the else call the correct function and returns true
|
||||
<strong> if (rax == 0x0) {
|
||||
</strong> *(var_18 + 0x18) = **_NDR_record;
|
||||
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||||
var_4 = 0x0;
|
||||
}
|
||||
else {
|
||||
// Calculated address that calls the proper function with 2 arguments
|
||||
<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 decompiled 2" %}
|
||||
This is the same function decompiled in a difefrent Hopper free version:
|
||||
|
||||
<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;
|
||||
// Initial instructions to find the proper function ponters
|
||||
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f | 0x0;
|
||||
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
|
||||
*(int32_t *)(var_18 + 0x4) = 0x24;
|
||||
*(int32_t *)(var_18 + 0xc) = 0x0;
|
||||
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
|
||||
*(int32_t *)(var_18 + 0x10) = 0x0;
|
||||
r8 = *(int32_t *)(var_10 + 0x14);
|
||||
r8 = r8 - 0x1f4;
|
||||
if (r8 > 0x0) {
|
||||
if (CPU_FLAGS & G) {
|
||||
r8 = 0x1;
|
||||
}
|
||||
}
|
||||
if ((r8 & 0x1) == 0x0) {
|
||||
r8 = *(int32_t *)(var_10 + 0x14);
|
||||
r8 = r8 - 0x1f4;
|
||||
if (r8 < 0x0) {
|
||||
if (CPU_FLAGS & L) {
|
||||
r8 = 0x1;
|
||||
}
|
||||
}
|
||||
if ((r8 & 0x1) == 0x0) {
|
||||
r8 = *(int32_t *)(var_10 + 0x14);
|
||||
// 0x1f4 = 500 (the strating ID)
|
||||
<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;
|
||||
}
|
||||
}
|
||||
// Same if else as in the previous version
|
||||
// Check the used of the address 0x100004040 (functions addresses array)
|
||||
<strong> if ((r8 & 0x1) == 0x0) {
|
||||
</strong><strong> *(var_18 + 0x18) = **0x100004000;
|
||||
</strong> *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||||
var_4 = 0x0;
|
||||
}
|
||||
else {
|
||||
// Call to the calculated address where the function should be
|
||||
<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 %}
|
||||
|
||||
Actually if you go to the function **`0x100004000`** you will find the array of **`routine_descriptor`** structs, the first element of the struct is the address where the function is implemented and the **struct takes 0x28 bytes**, so each 0x28 bytes (starting from byte 0) you can get 8 bytes and that be the **address of the function** that will be called:
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image.png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
This data can be extracted [**using this Hopper script**](https://github.com/knightsc/hopper/blob/master/scripts/MIG%20Detect.py).
|
||||
{% content-ref url="../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md" %}
|
||||
[macos-mig-mach-interface-generator.md](../../macos-proces-abuse/macos-ipc-inter-process-communication/macos-mig-mach-interface-generator.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
## References
|
||||
|
||||
|
|
|
@ -0,0 +1,358 @@
|
|||
# macOS MIG - Mach Interface Generator
|
||||
|
||||
<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>
|
||||
|
||||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||||
|
||||
</details>
|
||||
|
||||
MIG was created to **simplify the process of Mach IPC** code creation. It basically **generates the needed code** for server and client to communicate with a given definition. Even if the generated code is ugly, a developer will just need to import it and his code will be much simpler than before.
|
||||
|
||||
### Example
|
||||
|
||||
Create a definition file, in this case with a very simple function:
|
||||
|
||||
{% code title="myipc.defs" %}
|
||||
```cpp
|
||||
subsystem myipc 500; // Arbitrary name and id
|
||||
|
||||
userprefix USERPREF; // Prefix for created functions in the client
|
||||
serverprefix SERVERPREF; // Prefix for created functions in the server
|
||||
|
||||
#include <mach/mach_types.defs>
|
||||
#include <mach/std_types.defs>
|
||||
|
||||
simpleroutine Subtract(
|
||||
server_port : mach_port_t;
|
||||
n1 : uint32_t;
|
||||
n2 : uint32_t);
|
||||
```
|
||||
{% endcode %}
|
||||
|
||||
Now use mig to generate the server and client code that will be able to comunicate within each other to call the Subtract function:
|
||||
|
||||
```bash
|
||||
mig -header myipcUser.h -sheader myipcServer.h myipc.defs
|
||||
```
|
||||
|
||||
Several new files will be created in the current directory.
|
||||
|
||||
In the files **`myipcServer.c`** and **`myipcServer.h`** you can find the declaration and definition of the struct **`SERVERPREFmyipc_subsystem`**, which basically defines the function to call based on the received message ID (we indicated a starting number of 500):
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="myipcServer.c" %}
|
||||
```c
|
||||
/* Description of this subsystem, for use in direct RPC */
|
||||
const struct SERVERPREFmyipc_subsystem SERVERPREFmyipc_subsystem = {
|
||||
myipc_server_routine,
|
||||
500, // start ID
|
||||
501, // end ID
|
||||
(mach_msg_size_t)sizeof(union __ReplyUnion__SERVERPREFmyipc_subsystem),
|
||||
(vm_address_t)0,
|
||||
{
|
||||
{ (mig_impl_routine_t) 0,
|
||||
// Function to call
|
||||
(mig_stub_routine_t) _XSubtract, 3, 0, (routine_arg_descriptor_t)0, (mach_msg_size_t)sizeof(__Reply__Subtract_t)},
|
||||
}
|
||||
};
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="myipcServer.h" %}
|
||||
```c
|
||||
/* Description of this subsystem, for use in direct RPC */
|
||||
extern const struct SERVERPREFmyipc_subsystem {
|
||||
mig_server_routine_t server; /* Server routine */
|
||||
mach_msg_id_t start; /* Min routine number */
|
||||
mach_msg_id_t end; /* Max routine number + 1 */
|
||||
unsigned int maxsize; /* Max msg size */
|
||||
vm_address_t reserved; /* Reserved */
|
||||
struct routine_descriptor /* Array of routine descriptors */
|
||||
routine[1];
|
||||
} SERVERPREFmyipc_subsystem;
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
Based on the previous struct the function **`myipc_server_routine`** will get the **message ID** and return the proper function to call:
|
||||
|
||||
```c
|
||||
mig_external mig_routine_t myipc_server_routine
|
||||
(mach_msg_header_t *InHeadP)
|
||||
{
|
||||
int msgh_id;
|
||||
|
||||
msgh_id = InHeadP->msgh_id - 500;
|
||||
|
||||
if ((msgh_id > 0) || (msgh_id < 0))
|
||||
return 0;
|
||||
|
||||
return SERVERPREFmyipc_subsystem.routine[msgh_id].stub_routine;
|
||||
}
|
||||
```
|
||||
|
||||
In this example we only defined 1 function in the definitions, but if we would have defined more, the would have been inside the array of **`SERVERPREFmyipc_subsystem`** and the first one will be assigned to the ID **500**, the second one to the ID **501**...
|
||||
|
||||
Actually it's possible to identify this relation in the struct **`subsystem_to_name_map_myipc`** from **`myipcServer.h`**:
|
||||
|
||||
```c
|
||||
#ifndef subsystem_to_name_map_myipc
|
||||
#define subsystem_to_name_map_myipc \
|
||||
{ "Subtract", 500 }
|
||||
#endif
|
||||
```
|
||||
|
||||
Finally, another important function to make the server work will be **`myipc_server`**, which is the one that will actually **call the function** related to the received id:
|
||||
|
||||
<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;
|
||||
/* Minimal size: routine() will update it if different */
|
||||
OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t);
|
||||
OutHeadP->msgh_local_port = MACH_PORT_NULL;
|
||||
OutHeadP->msgh_id = InHeadP->msgh_id + 100;
|
||||
OutHeadP->msgh_reserved = 0;
|
||||
|
||||
if ((InHeadP->msgh_id > 500) || (InHeadP->msgh_id < 500) ||
|
||||
<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>
|
||||
|
||||
Check the following code to use the generated code to create a simple server and client where the client can call the functions Subtract from the server:
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="myipc_server.c" %}
|
||||
```c
|
||||
// gcc myipc_server.c myipcServer.c -o myipc_server
|
||||
|
||||
#include <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);
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="myipc_client.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);
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
{% endtabs %}
|
||||
|
||||
### Binary Analysis
|
||||
|
||||
As many binaries now use MIG to expose mach ports, it's interesting to know how to **identify that MIG was used** and the **functions that MIG executes** with each message ID.
|
||||
|
||||
[**jtool2**](../../macos-apps-inspecting-debugging-and-fuzzing/#jtool2) can parse MIG information from a Mach-O binary indicating the message ID and identifying the function to execute:
|
||||
|
||||
```bash
|
||||
jtool2 -d __DATA.__const myipc_server | grep MIG
|
||||
```
|
||||
|
||||
It was previously mentioned that the function that will take care of **calling the correct function depending on the received message ID** was `myipc_server`. However, you usually won't have the symbols of the binary (no functions names), so it's interesting to **check how it looks like decompiled** as it will always be very similar (the code of this function is independent from the functions exposed):
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="myipc_server decompiled 1" %}
|
||||
<pre class="language-c"><code class="lang-c">int _myipc_server(int arg0, int arg1) {
|
||||
var_10 = arg0;
|
||||
var_18 = arg1;
|
||||
// Initial instructions to find the proper function ponters
|
||||
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f;
|
||||
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
|
||||
*(int32_t *)(var_18 + 0x4) = 0x24;
|
||||
*(int32_t *)(var_18 + 0xc) = 0x0;
|
||||
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
|
||||
*(int32_t *)(var_18 + 0x10) = 0x0;
|
||||
if (*(int32_t *)(var_10 + 0x14) <= 0x1f4 && *(int32_t *)(var_10 + 0x14) >= 0x1f4) {
|
||||
rax = *(int32_t *)(var_10 + 0x14);
|
||||
// Call to sign_extend_64 that can help to identifyf this function
|
||||
// This stores in rax the pointer to the call that needs to be called
|
||||
// Check the used of the address 0x100004040 (functions addresses array)
|
||||
// 0x1f4 = 500 (the strating ID)
|
||||
<strong> rax = *(sign_extend_64(rax - 0x1f4) * 0x28 + 0x100004040);
|
||||
</strong> var_20 = rax;
|
||||
// If - else, the if returns false, while the else call the correct function and returns true
|
||||
<strong> if (rax == 0x0) {
|
||||
</strong> *(var_18 + 0x18) = **_NDR_record;
|
||||
*(int32_t *)(var_18 + 0x20) = 0xfffffffffffffed1;
|
||||
var_4 = 0x0;
|
||||
}
|
||||
else {
|
||||
// Calculated address that calls the proper function with 2 arguments
|
||||
<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 decompiled 2" %}
|
||||
This is the same function decompiled in a difefrent Hopper free version:
|
||||
|
||||
<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;
|
||||
// Initial instructions to find the proper function ponters
|
||||
*(int32_t *)var_18 = *(int32_t *)var_10 & 0x1f | 0x0;
|
||||
*(int32_t *)(var_18 + 0x8) = *(int32_t *)(var_10 + 0x8);
|
||||
*(int32_t *)(var_18 + 0x4) = 0x24;
|
||||
*(int32_t *)(var_18 + 0xc) = 0x0;
|
||||
*(int32_t *)(var_18 + 0x14) = *(int32_t *)(var_10 + 0x14) + 0x64;
|
||||
*(int32_t *)(var_18 + 0x10) = 0x0;
|
||||
r8 = *(int32_t *)(var_10 + 0x14);
|
||||
r8 = r8 - 0x1f4;
|
||||
if (r8 > 0x0) {
|
||||
if (CPU_FLAGS & G) {
|
||||
r8 = 0x1;
|
||||
}
|
||||
}
|
||||
if ((r8 & 0x1) == 0x0) {
|
||||
r8 = *(int32_t *)(var_10 + 0x14);
|
||||
r8 = r8 - 0x1f4;
|
||||
if (r8 < 0x0) {
|
||||
if (CPU_FLAGS & L) {
|
||||
r8 = 0x1;
|
||||
}
|
||||
}
|
||||
if ((r8 & 0x1) == 0x0) {
|
||||
r8 = *(int32_t *)(var_10 + 0x14);
|
||||
// 0x1f4 = 500 (the strating ID)
|
||||
<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;
|
||||
}
|
||||
}
|
||||
// Same if else as in the previous version
|
||||
// Check the used of the address 0x100004040 (functions addresses array)
|
||||
<strong> if ((r8 & 0x1) == 0x0) {
|
||||
</strong><strong> *(var_18 + 0x18) = **0x100004000;
|
||||
</strong> *(int32_t *)(var_18 + 0x20) = 0xfffffed1;
|
||||
var_4 = 0x0;
|
||||
}
|
||||
else {
|
||||
// Call to the calculated address where the function should be
|
||||
<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 %}
|
||||
|
||||
Actually if you go to the function **`0x100004000`** you will find the array of **`routine_descriptor`** structs, the first element of the struct is the address where the function is implemented and the **struct takes 0x28 bytes**, so each 0x28 bytes (starting from byte 0) you can get 8 bytes and that be the **address of the function** that will be called:
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image.png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
This data can be extracted [**using this Hopper script**](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>
|
||||
|
||||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||||
|
||||
</details>
|
|
@ -0,0 +1,403 @@
|
|||
# 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>
|
||||
|
||||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||||
|
||||
</details>
|
||||
|
||||
## Basic Information
|
||||
|
||||
XPC, which stands for XNU (the kernel used by macOS) inter-Process Communication, is a framework for **communication between processes** on macOS and iOS. XPC provides a mechanism for making **safe, asynchronous method calls between different processes** on the system. It's a part of Apple's security paradigm, allowing for the **creation of privilege-separated applications** where each **component** runs with **only the permissions it needs** to do its job, thereby limiting the potential damage from a compromised process.
|
||||
|
||||
XPC uses a form of Inter-Process Communication (IPC), which is a set of methods for different programs running on the same system to send data back and forth.
|
||||
|
||||
The primary benefits of XPC include:
|
||||
|
||||
1. **Security**: By separating work into different processes, each process can be granted only the permissions it needs. This means that even if a process is compromised, it has limited ability to do harm.
|
||||
2. **Stability**: XPC helps isolate crashes to the component where they occur. If a process crashes, it can be restarted without affecting the rest of the system.
|
||||
3. **Performance**: XPC allows for easy concurrency, as different tasks can be run simultaneously in different processes.
|
||||
|
||||
The only **drawback** is that **separating an application in several processes** making them communicate via XPC is **less efficient**. But in todays systems this isn't almost noticeable and the benefits are better.
|
||||
|
||||
## Application Specific XPC services
|
||||
|
||||
The XPC components of an application are **inside the application itself.** For example, in Safari you can find them in **`/Applications/Safari.app/Contents/XPCServices`**. They have extension **`.xpc`** (like **`com.apple.Safari.SandboxBroker.xpc`**) and are **also bundles** with the main binary inside of it: `/Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBroker` and an `Info.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist`
|
||||
|
||||
As you might be thinking a **XPC component will have different entitlements and privileges** than the other XPC components or the main app binary. EXCEPT if a XPC service is configured with [**JoinExistingSession**](https://developer.apple.com/documentation/bundleresources/information\_property\_list/xpcservice/joinexistingsession) set to “True” in its **Info.plist** file. In this case, the XPC service will run in the **same security session as the application** that called it.
|
||||
|
||||
XPC services are **started** by **launchd** when required and **shut down** once all tasks are **complete** to free system resources. **Application-specific XPC components can only be utilized by the application**, thereby reducing the risk associated with potential vulnerabilities.
|
||||
|
||||
## System Wide XPC services
|
||||
|
||||
System-wide XPC services are accessible to all users. These services, either launchd or Mach-type, need to be **defined in plist** files located in specified directories such as **`/System/Library/LaunchDaemons`**, **`/Library/LaunchDaemons`**, **`/System/Library/LaunchAgents`**, or **`/Library/LaunchAgents`**.
|
||||
|
||||
These plists files will have a key called **`MachServices`** with the name of the service, and a key called **`Program`** with the path to the binary:
|
||||
|
||||
```xml
|
||||
cat /Library/LaunchDaemons/com.jamf.management.daemon.plist
|
||||
|
||||
<?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>
|
||||
```
|
||||
|
||||
The ones in **`LaunchDameons`** are run by root. So if an unprivileged process can talk with one of these it could be able to escalate privileges.
|
||||
|
||||
## XPC Event Messages
|
||||
|
||||
Applications can **subscribe** to different event **messages**, enabling them to be **initiated on-demand** when such events happen. The **setup** for these services is done in l**aunchd plist files**, located in the **same directories as the previous ones** and containing an extra **`LaunchEvent`** key.
|
||||
|
||||
### XPC Connecting Process Check
|
||||
|
||||
When a process tries to call a method from via an XPC connection, the **XPC service should check if that process is allowed to connect**. Here are the common ways to check that and the common pitfalls:
|
||||
|
||||
{% content-ref url="macos-xpc-connecting-process-check.md" %}
|
||||
[macos-xpc-connecting-process-check.md](macos-xpc-connecting-process-check.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
## XPC Authorization
|
||||
|
||||
Apple also allows apps to **configure some rights and how to get them** so if the calling process have them it would be **allowed to call a method** from the XPC service:
|
||||
|
||||
{% content-ref url="macos-xpc-authorization.md" %}
|
||||
[macos-xpc-authorization.md](macos-xpc-authorization.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
## C Code Example
|
||||
|
||||
{% tabs %}
|
||||
{% tab title="xpc_server.c" %}
|
||||
```c
|
||||
// gcc xpc_server.c -o xpc_server
|
||||
|
||||
#include <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;
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% tab title="xpc_client.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;
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% 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
|
||||
```
|
||||
|
||||
## Objective-C Code Example
|
||||
|
||||
{% 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
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% 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;
|
||||
}
|
||||
```
|
||||
{% endtab %}
|
||||
|
||||
{% 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 inside a Dylb code
|
||||
|
||||
```objectivec
|
||||
// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib
|
||||
// gcc injection example:
|
||||
// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin
|
||||
|
||||
#import <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>
|
||||
|
||||
* Do you work in a **cybersecurity company**? Do you want to see your **company advertised in HackTricks**? or do you want to have access to the **latest version of the PEASS or download HackTricks in PDF**? Check the [**SUBSCRIPTION PLANS**](https://github.com/sponsors/carlospolop)!
|
||||
* Discover [**The PEASS Family**](https://opensea.io/collection/the-peass-family), our collection of exclusive [**NFTs**](https://opensea.io/collection/the-peass-family)
|
||||
* Get the [**official PEASS & HackTricks swag**](https://peass.creator-spring.com)
|
||||
* **Join the** [**💬**](https://emojipedia.org/speech-balloon/) [**Discord group**](https://discord.gg/hRep4RUj7f) or the [**telegram group**](https://t.me/peass) or **follow** me on **Twitter** [**🐦**](https://github.com/carlospolop/hacktricks/tree/7af18b62b3bdc423e11444677a6a73d4043511e9/\[https:/emojipedia.org/bird/README.md)[**@carlospolopm**](https://twitter.com/hacktricks\_live)**.**
|
||||
* **Share your hacking tricks by submitting PRs to the** [**hacktricks repo**](https://github.com/carlospolop/hacktricks) **and** [**hacktricks-cloud repo**](https://github.com/carlospolop/hacktricks-cloud).
|
||||
|
||||
</details>
|
|
@ -302,7 +302,7 @@ authenticate-session-owner, authenticate-session-owner-or-admin, authenticate-se
|
|||
|
||||
If you find the function: **`[HelperTool checkAuthorization:command:]`** it's probably the the process is using the previously mentioned schema for authorization:
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
<figure><img src="../../../../../.gitbook/assets/image (1) (1) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
Thisn, if this function is calling functions such as `AuthorizationCreateFromExternalForm`, `authorizationRightForCommand`, `AuthorizationCopyRights`, `AuhtorizationFree`, it's using [**EvenBetterAuthorizationSample**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L101-L154).
|
||||
|
||||
|
@ -314,7 +314,7 @@ Then, you need to find the protocol schema in order to be able to establish a co
|
|||
|
||||
The function **`shouldAcceptNewConnection`** indicates the protocol being exported:
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image (3) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
<figure><img src="../../../../../.gitbook/assets/image (3) (1).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
In this case, we have the same as in EvenBetterAuthorizationSample, [**check this line**](https://github.com/brenwell/EvenBetterAuthorizationSample/blob/e1052a1855d3a5e56db71df5f04e790bfd4389c4/HelperTool/HelperTool.m#L94).
|
||||
|
||||
|
@ -338,7 +338,7 @@ Lastly, we just need to know the **name of the exposed Mach Service** in order t
|
|||
|
||||
* In the **`[HelperTool init]`** where you can see the Mach Service being used:
|
||||
|
||||
<figure><img src="../../../../.gitbook/assets/image (4).png" alt=""><figcaption></figcaption></figure>
|
||||
<figure><img src="../../../../../.gitbook/assets/image (4).png" alt=""><figcaption></figcaption></figure>
|
||||
|
||||
* In the launchd plist:
|
||||
|
|
@ -32,8 +32,8 @@ When a connection is stablished to an XPC service, the server will check if the
|
|||
|
||||
For more information about the PID reuse attack check:
|
||||
|
||||
{% content-ref url="macos-pid-reuse.md" %}
|
||||
[macos-pid-reuse.md](macos-pid-reuse.md)
|
||||
{% content-ref url="../../../mac-os-architecture/macos-ipc-inter-process-communication/macos-pid-reuse.md" %}
|
||||
[macos-pid-reuse.md](../../../mac-os-architecture/macos-ipc-inter-process-communication/macos-pid-reuse.md)
|
||||
{% endcontent-ref %}
|
||||
|
||||
### Trustcache - Downgrade Attacks Prevention
|
Loading…
Reference in a new issue