macOS XPC

从零到英雄学习AWS黑客技术,通过 htARTE (HackTricks AWS Red Team Expert)

支持HackTricks的其他方式:

基本信息

XPC,代表XNU(macOS使用的内核)进程间通信,是macOS和iOS上进程间通信的框架。XPC提供了一种机制,用于在系统上的不同进程之间进行安全的、异步的方法调用。它是苹果安全范式的一部分,允许创建权限分离的应用程序,其中每个组件仅运行具有执行其工作所需的权限,从而限制了被攻破进程的潜在损害。

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类型,都需要在指定目录中的plist文件中定义,例如**/System/Library/LaunchDaemons/Library/LaunchDaemons/System/Library/LaunchAgents/Library/LaunchAgents**。

这些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 服务应该检查该进程是否被允许连接。以下是常见的检查方式和常见的陷阱:

XPC 授权

苹果还允许应用程序配置一些权限以及如何获取它们,所以如果调用进程拥有这些权限,它将被允许调用 XPC 服务的方法:

XPC 嗅探器

要嗅探 XPC 消息,你可以使用 xpcspy,它使用了 Frida

# 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;
}

最后更新于