macOS XPC

Reading time: 19 minutes

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする

基本情報

XPCは、macOSで使用されるカーネルであるXNUのプロセス間通信の略で、macOSおよびiOS上のプロセス間の通信のためのフレームワークです。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.SandboxBrokerおよびInfo.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**というキーがあります:

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

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

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

XPCオブジェクト

  • xpc_object_t

すべてのXPCメッセージは、シリアル化とデシリアル化を簡素化する辞書オブジェクトです。さらに、libxpc.dylibはほとんどのデータ型を宣言しているため、受信したデータが期待される型であることを確認できます。C APIでは、すべてのオブジェクトはxpc_object_tであり(その型はxpc_get_type(object)を使用して確認できます)。
さらに、xpc_copy_description(object)関数を使用して、デバッグ目的に役立つオブジェクトの文字列表現を取得できます。
これらのオブジェクトには、xpc_<object>_copyxpc_<object>_equalxpc_<object>_hashxpc_<object>_serializexpc_<object>_deserializeなどの呼び出し可能なメソッドもあります。

xpc_object_tは、xpc_<objetType>_create関数を呼び出すことで作成され、内部的に_xpc_base_create(Class, Size)を呼び出し、オブジェクトのクラスの型(XPC_TYPE_*のいずれか)とそのサイズ(メタデータ用に追加の40Bがサイズに加算されます)が指定されます。つまり、オブジェクトのデータはオフセット40Bから始まります。
したがって、xpc_<objectType>_txpc_object_tのサブクラスのようなものであり、os_object_t*のサブクラスになります。

warning

xpc_dictionary_[get/set]_<objectType>を使用して、キーの型と実際の値を取得または設定するのは開発者であるべきです。

  • xpc_pipe

**xpc_pipe**は、プロセスが通信するために使用できるFIFOパイプです(通信はMachメッセージを使用します)。
特定のMachポートを使用して作成するために、xpc_pipe_create()またはxpc_pipe_create_from_port()を呼び出すことでXPCサーバーを作成できます。次に、メッセージを受信するには、xpc_pipe_receiveおよびxpc_pipe_try_receiveを呼び出すことができます。

**xpc_pipeオブジェクトは、使用される2つのMachポートと名前(ある場合)の情報をその構造体に持つxpc_object_t**です。たとえば、plist /System/Library/LaunchDaemons/com.apple.secinitd.plist内のデーモンsecinitdの名前は、com.apple.secinitdと呼ばれるパイプを構成します。

xpc_pipeの例は、launchdによって作成されたbootstrap pipeで、Machポートの共有を可能にします。

  • NSXPC*

これらは、XPC接続の抽象化を可能にするObjective-Cの高レベルオブジェクトです。
さらに、これらのオブジェクトは、前のものよりもDTraceでデバッグしやすくなっています。

  • GCD Queues

XPCはメッセージを渡すためにGCDを使用し、さらにxpc.transactionqxpc.ioxpc-events.add-listenerqxpc.service-instanceなどの特定のディスパッチキューを生成します。

XPCサービス

これらは、他のプロジェクトの**XPCServicesフォルダー内にある.xpc拡張子を持つバンドルであり、Info.plistではCFBundlePackageTypeXPC!**に設定されています。
このファイルには、Application、User、System、またはサンドボックスを定義できる_SandboxProfile、またはサービスに連絡するために必要な権限やIDを示す可能性のある_AllowedClientsなど、他の構成キーがあります。これらおよび他の構成オプションは、サービスが起動されるときにサービスを構成するのに役立ちます。

サービスの開始

アプリは、xpc_connection_create_mach_serviceを使用してXPCサービスに接続しようとし、その後launchdがデーモンを見つけて**xpcproxyを起動します。xpcproxy**は構成された制限を強制し、提供されたFDとMachポートでサービスを生成します。

XPCサービスの検索速度を向上させるために、キャッシュが使用されます。

xpcproxyのアクションをトレースすることができます:

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

XPCライブラリは、xpc_ktrace_pid0およびxpc_ktrace_pid1を呼び出すアクションをログするためにkdebugを使用します。使用されるコードは文書化されていないため、/usr/share/misc/trace.codesに追加する必要があります。これらのコードは0x29のプレフィックスを持ち、例えば0x29000004: XPC_serializer_packがあります。
ユーティリティxpcproxy0x22のプレフィックスを使用し、例えば0x2200001c: xpcproxy:will_do_preexecがあります。

XPCイベントメッセージ

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

XPC接続プロセスチェック

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

macOS XPC Connecting Process Check

XPC認可

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

macOS XPC Authorization

XPCスニファー

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

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

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

別の使用可能なツールは XPoCe2 です。

XPC 通信 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;
}
bash
# Compile the server & client
gcc xpc_server.c -o xpc_server
gcc xpc_client.c -o xpc_client

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

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

# Call client
./xpc_client

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

XPCコミュニケーション Objective-C コード例

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

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

@interface MyXPCObject : NSObject <MyXPCProtocol>
@end


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

@interface MyDelegate : NSObject <NSXPCListenerDelegate>
@end


@implementation MyDelegate

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

MyXPCObject *my_object = [MyXPCObject new];

newConnection.exportedObject = my_object;

[newConnection resume];
return YES;
}
@end

int main(void) {

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

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

sleep(10); // Fake something is done and then it ends
}
bash
# Compile the server & client
gcc -framework Foundation oc_xpc_server.m -o oc_xpc_server
gcc -framework Foundation oc_xpc_client.m -o oc_xpc_client

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

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

# Call client
./oc_xpc_client

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

Dylbコード内のクライアント

objectivec
// gcc -dynamiclib -framework Foundation oc_xpc_client.m -o oc_xpc_client.dylib
// gcc injection example:
// DYLD_INSERT_LIBRARIES=oc_xpc_client.dylib /path/to/vuln/bin

#import <Foundation/Foundation.h>

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

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

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

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

[_agentConnection resume];

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

return;
}

Remote XPC

この機能は RemoteXPC.frameworklibxpcから)によって提供され、異なるホスト間でXPCを介して通信することができます。
リモートXPCをサポートするサービスは、plistにUsesRemoteXPCキーを持っており、これは/System/Library/LaunchDaemons/com.apple.SubmitDiagInfo.plistのケースのようです。しかし、サービスはlaunchdで登録されますが、機能を提供するのはUserEventAgentで、プラグインcom.apple.remoted.plugincom.apple.remoteservicediscovery.events.pluginです。

さらに、RemoteServiceDiscovery.frameworkは、com.apple.remoted.pluginから情報を取得することを可能にし、get_deviceget_unique_deviceconnectなどの関数を公開しています。

一度connectが使用され、サービスのソケットfdが収集されると、remote_xpc_connection_*クラスを使用することが可能です。

リモートサービスに関する情報は、次のようなパラメータを使用してCLIツール/usr/libexec/remotectlを使用することで取得できます:

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

BridgeOSとホスト間の通信は、専用のIPv6インターフェースを介して行われます。MultiverseSupport.frameworkは、通信に使用されるfdを持つソケットを確立することを可能にします。
netstatnettop、またはオープンソースのオプションであるnetbottomを使用して、これらの通信を見つけることができます。

tip

AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE) Azureハッキングを学び、実践する:HackTricks Training Azure Red Team Expert (AzRTE)

HackTricksをサポートする