macOS XPC

macOS XPC

AWSハッキングをゼロからヒーローまで学ぶには htARTE (HackTricks AWS Red Team Expert)をご覧ください!

HackTricksをサポートする他の方法:

基本情報

XPCは、macOSとiOSで使用されるXNU(macOSに使用されるカーネル)のプロセス間通信を意味し、プロセス間の通信のためのフレームワークです。XPCは、システム上の異なるプロセス間で安全で非同期のメソッド呼び出しを行うためのメカニズムを提供します。これはAppleのセキュリティパラダイムの一部であり、各コンポーネントがその仕事をするために必要な許可だけを持って実行される特権分離アプリケーションの作成を可能にし、侵害されたプロセスからの潜在的な損害を制限します。

XPCはプロセス間通信(IPC)の一形態を使用し、これは同じシステム上で実行されている異なるプログラムがデータを行き来させるための方法のセットです。

XPCの主な利点には以下が含まれます:

  1. セキュリティ: 異なるプロセスに作業を分離することで、各プロセスには必要な許可のみが与えられます。これは、プロセスが侵害されても、害を及ぼす能力が限定されることを意味します。

  2. 安定性: XPCはクラッシュを発生したコンポーネントに隔離するのに役立ちます。プロセスがクラッシュしても、システムの残りの部分に影響を与えることなく再起動することができます。

  3. パフォーマンス: XPCは簡単な並行処理を可能にし、異なるタスクを異なるプロセスで同時に実行できます。

唯一の欠点は、アプリケーションを複数のプロセスに分離し、XPCを介して通信させることが効率が低いことです。しかし、今日のシステムではほとんど気づかれず、利点の方が優れています。

アプリケーション固有のXPCサービス

アプリケーションのXPCコンポーネントはアプリケーション自体の内部にあります。 例えば、Safariでは /Applications/Safari.app/Contents/XPCServices で見つけることができます。拡張子は .xpc(例:com.apple.Safari.SandboxBroker.xpc)であり、メインバイナリが内部に含まれるバンドルでもあります:/Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/MacOS/com.apple.Safari.SandboxBrokerInfo.plist: /Applications/Safari.app/Contents/XPCServices/com.apple.Safari.SandboxBroker.xpc/Contents/Info.plist

お考えの通り、XPCコンポーネントは他のXPCコンポーネントやメインアプリバイナリとは異なる権限と特権を持っています。 ただし、XPCサービスがそのInfo.plistファイルでJoinExistingSessionを「True」に設定されている場合は例外です。この場合、XPCサービスはそれを呼び出したアプリケーションと同じセキュリティセッションで実行されます。

XPCサービスは必要に応じてlaunchdによって開始され、すべてのタスクが完了するとシステムリソースを解放するためにシャットダウンされます。 アプリケーション固有のXPCコンポーネントはアプリケーションによってのみ利用できるため、潜在的な脆弱性に関連するリスクが減少します。

システム全体のXPCサービス

システム全体のXPCサービスはすべてのユーザーがアクセスできます。これらのサービスは、launchdまたはMachタイプであり、/System/Library/LaunchDaemons/Library/LaunchDaemons/System/Library/LaunchAgents、または**/Library/LaunchAgentsなどの指定されたディレクトリにあるplistファイルで定義する必要があります。**

これらのplistファイルにはサービスの名前を示す**MachServicesというキーと、バイナリへのパスを示すProgram**というキーがあります:

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

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

**LaunchDameons**にあるものはrootによって実行されます。したがって、権限のないプロセスがこれらのいずれかと通信できる場合、権限を昇格させることができる可能性があります。

XPC イベントメッセージ

アプリケーションは異なるイベントメッセージ登録することができ、これにより、そのようなイベントが発生したときにオンデマンドで起動されるようになります。これらのサービスの設定は、前述のディレクトリと同じディレクトリにあるlaunchd plistファイルで行われ、追加の**LaunchEvent**キーが含まれています。

XPC 接続プロセスチェック

プロセスがXPC接続を介してメソッドを呼び出そうとするとき、XPCサービスはそのプロセスが接続を許可されているかどうかをチェックする必要があります。以下はそのチェックの一般的な方法とよくある落とし穴です:

pagemacOS XPC Connecting Process Check

XPC 認証

Appleはアプリがいくつかの権限を設定し、それらを取得する方法を設定することも許可しているので、呼び出しプロセスがそれらを持っていれば、XPCサービスからメソッドを呼び出すことが許可されます

pagemacOS XPC Authorization

XPC スニファー

XPCメッセージをスニッフするには、Fridaを使用するxpcspyを使用できます。

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

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

XPC通信C言語コード例

// gcc xpc_server.c -o xpc_server

#include <xpc/xpc.h>

static void handle_event(xpc_object_t event) {
if (xpc_get_type(event) == XPC_TYPE_DICTIONARY) {
// Print received message
const char* received_message = xpc_dictionary_get_string(event, "message");
printf("Received message: %s\n", received_message);

// Create a response dictionary
xpc_object_t response = xpc_dictionary_create(NULL, NULL, 0);
xpc_dictionary_set_string(response, "received", "received");

// Send response
xpc_connection_t remote = xpc_dictionary_get_remote_connection(event);
xpc_connection_send_message(remote, response);

// Clean up
xpc_release(response);
}
}

static void handle_connection(xpc_connection_t connection) {
xpc_connection_set_event_handler(connection, ^(xpc_object_t event) {
handle_event(event);
});
xpc_connection_resume(connection);
}

int main(int argc, const char *argv[]) {
xpc_connection_t service = xpc_connection_create_mach_service("xyz.hacktricks.service",
dispatch_get_main_queue(),
XPC_CONNECTION_MACH_SERVICE_LISTENER);
if (!service) {
fprintf(stderr, "Failed to create service.\n");
exit(EXIT_FAILURE);
}

xpc_connection_set_event_handler(service, ^(xpc_object_t event) {
xpc_type_t type = xpc_get_type(event);
if (type == XPC_TYPE_CONNECTION) {
handle_connection(event);
}
});

xpc_connection_resume(service);
dispatch_main();

return 0;
}

</div>

<div data-gb-custom-block data-tag="tab" data-title='xpc_client.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;
}
# Compile the server & client
gcc xpc_server.c -o xpc_server
gcc xpc_client.c -o xpc_client

# Save server on it's location
cp xpc_server /tmp

# Load daemon
sudo cp xyz.hacktricks.service.plist /Library/LaunchDaemons
sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.service.plist

# Call client
./xpc_client

# Clean
sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.service.plist
sudo rm /Library/LaunchDaemons/xyz.hacktricks.service.plist /tmp/xpc_server

XPC通信 Objective-C コード例

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

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

@interface MyXPCObject : NSObject <MyXPCProtocol>
@end


@implementation MyXPCObject
- (void)sayHello:(NSString *)some_string withReply:(void (^)(NSString *))reply {
NSLog(@"Received message: %@", some_string);
NSString *response = @"Received";
reply(response);
}
@end

@interface MyDelegate : NSObject <NSXPCListenerDelegate>
@end


@implementation MyDelegate

- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)];

MyXPCObject *my_object = [MyXPCObject new];

newConnection.exportedObject = my_object;

[newConnection resume];
return YES;
}
@end

int main(void) {

NSXPCListener *listener = [[NSXPCListener alloc] initWithMachServiceName:@"xyz.hacktricks.svcoc"];

id <NSXPCListenerDelegate> delegate = [MyDelegate new];
listener.delegate = delegate;
[listener resume];

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

```bash # Compile the server & client gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client

Save server on it's location

cp oc_xpc_server /tmp

Load daemon

sudo cp xyz.hacktricks.svcoc.plist /Library/LaunchDaemons sudo launchctl load /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist

Call client

./oc_xpc_client

Clean

sudo launchctl unload /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist sudo rm /Library/LaunchDaemons/xyz.hacktricks.svcoc.plist /tmp/oc_xpc_server

## Dylb コード内のクライアント
```objectivec
// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib
// gcc injection example:
// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin

#import <Foundation/Foundation.h>

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

__attribute__((constructor))
static void customConstructor(int argc, const char **argv)
{
NSString*  _serviceName = @"xyz.hacktricks.svcoc";

NSXPCConnection* _agentConnection = [[NSXPCConnection alloc] initWithMachServiceName:_serviceName options:4096];

[_agentConnection setRemoteObjectInterface:[NSXPCInterface interfaceWithProtocol:@protocol(MyXPCProtocol)]];

[_agentConnection resume];

[[_agentConnection remoteObjectProxyWithErrorHandler:^(NSError* error) {
(void)error;
NSLog(@"Connection Failure");
}] sayHello:@"Hello, Server!" withReply:^(NSString *response) {
NSLog(@"Received response: %@", response);
}    ];
NSLog(@"Done!");

return;
}
htARTE (HackTricks AWS Red Team Expert)でAWSハッキングをゼロからヒーローまで学ぶ

HackTricksをサポートする他の方法:

Last updated