macOS xpc_connection_get_audit_token 攻击
Reading time: 17 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
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
有关更多信息请查看原文: https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/。以下为摘要:
Mach Messages 基本信息
如果你不知道什么是 Mach Messages,请先查阅此页面:
macOS IPC - Inter Process Communication
目前记住(来自此处的定义):
Mach messages 是通过 mach port 发送的,它是内核提供的一个 单接收者、多发送者通信 通道。多个进程可以向同一个 mach port 发送消息,但在任何时刻只有一个进程能从中读取。就像文件描述符和 sockets 一样,mach ports 由内核分配和管理,进程只看到一个整数,用以向内核指示它们想要使用哪个 mach port。
XPC Connection
如果你不知道 XPC connection 如何建立,请查看:
漏洞概述
对你重要的是,XPC 的抽象是点对点连接(one-to-one),但它构建在一个可以有多个发送者的技术之上,所以:
- Mach ports 是单接收者、多发送者的。
- 一个 XPC connection 的 audit token 是从最近接收的消息复制过来的 audit token。
- 获取 XPC connection 的 audit token 对许多 安全检查 至关重要。
尽管上述情况看起来有问题,但在某些场景下不会造成影响(来源):
- Audit tokens 常用于授权检查以决定是否接受连接。由于这是通过向 service port 发送消息来完成的,此时尚未建立连接。该端口上的更多消息只会被视为额外的连接请求。因此,任何在接受连接之前的检查都不易受到影响(这也意味着在
-listener:shouldAcceptNewConnection:
中 audit token 是安全的)。因此我们关注那些验证特定操作的 XPC 连接。 - XPC 事件处理器是同步处理的。这意味着一个消息的事件处理器必须完成后才能处理下一个消息,即便是在并发的 dispatch 队列上。因此在 XPC 事件处理器内部,audit token 不会被其他普通(非 reply)消息覆盖。
两种不同的方法可能被利用:
- Variant1:
- Exploit 连接到 service A 和 service B
- Service B 可以在 service A 中调用一个用户不能调用的 特权功能
- Service A 在不处于该连接的事件处理器内,而是在
dispatch_async
中调用xpc_connection_get_audit_token
- 因此,不同的消息可能会覆盖 Audit Token,因为它在事件处理器之外被异步调度。
- Exploit 将 对 svc A 的 SEND 权限 传递给 service B。
- 所以 svc B 实际上会发送消息到 svc A。
- Exploit 尝试调用该 特权操作。在一个 RC(竞态)情形中,svc A 在 svc B 覆盖 Audit token 时对该操作进行了授权检查(从而使 exploit 获得调用该特权操作的权限)。
- Variant 2:
- Service B 可以在 service A 中调用一个用户不能调用的 特权功能
- Exploit 与 service A 建立连接,service A 将向 exploit 发送一条期望在特定 reply port 上得到响应的消息。
- Exploit 将该 reply port 发送给 service B。
- 当 service B 回复时,它会将消息发送到 service A,与此同时 exploit 向 service A 发送另一条尝试触及特权功能的消息,并期望来自 service B 的回复在恰当时机覆盖 Audit token(竞态条件)。
Variant 1: 在事件处理器外调用 xpc_connection_get_audit_token
场景:
- 两个 mach services
A
和B
,我们都能连接到它们(基于 sandbox 配置和在接受连接前的授权检查)。 - A 必须对某个特定动作进行 授权检查,而
B
可以通过该检查(但我们的应用不能)。 - 例如,如果 B 有某些 entitlements 或以 root 身份运行,它可能被允许请求 A 执行一个特权操作。
- 在这个授权检查中,
A
异步获取 audit token,例如在dispatch_async
中调用xpc_connection_get_audit_token
。
caution
在这种情况下,攻击者可以触发一个竞态条件(Race Condition):攻击者让 exploit 多次请求 A 执行某个操作,同时让 B 向 A 发送消息。当竞态成功时,B 的 audit token 会在我们的 exploit 的请求被 A 处理时被复制到内存中,从而使 exploit 获得只有 B 能请求的特权操作的访问权限。
这曾发生在 A
是 smd
,B
是 diagnosticd
的情形。smb 中的函数 SMJobBless
可用于以 root 身份安装新的特权 helper tool。如果一个 以 root 运行的进程联系 smd
,则不会执行其他检查。
因此,service B 为 diagnosticd
,因为它以 root 身份运行且可用于 监控 进程,一旦开始监控,它会每秒发送多条消息。
攻击步骤:
- 使用标准 XPC 协议对名为
smd
的 service 发起 连接。 - 再对
diagnosticd
建立一个次要 连接。与常规流程不同,客户端端口的 send 权被替换为smd
连接所关联的 send right 的副本,而不是创建并发送两个全新的 mach ports。 - 结果是,XPC 消息可以发送到
diagnosticd
,但来自diagnosticd
的响应被重定向到了smd
。对smd
而言,看起来来自用户和diagnosticd
的消息都来自同一个连接。
- 接下来指示
diagnosticd
开始监控选定的进程(可能是用户自己的进程)。同时,向smd
发送大量常规的 1004 消息。目标是安装一个具有提升权限的工具。 - 该操作在
handle_bless
函数中触发了竞态条件。时间点非常关键:xpc_connection_get_pid
必须返回用户进程的 PID(因为特权工具位于用户的 app bundle 中)。但是,xpc_connection_get_audit_token
(特别是在connection_is_authorized
子例程中)必须引用属于diagnosticd
的 audit token。
Variant 2: reply forwarding
在 XPC(跨进程通信)环境中,尽管事件处理器不会并发执行,但 reply 消息的处理具有特殊行为。具体来说,存在两种不同的方法来发送期望回复的消息:
xpc_connection_send_message_with_reply
:在此方法中,XPC 消息会在指定的队列上被接收和处理。xpc_connection_send_message_with_reply_sync
:相反,在此方法中,XPC 消息会在当前的 dispatch 队列上被接收和处理。
这种区别很关键,因为它允许在 XPC 事件处理器执行的同时并发解析 reply 包的可能性。值得注意的是,尽管 _xpc_connection_set_creds
实现了加锁以防止 audit token 的部分覆盖,但它并未对整个 connection 对象提供保护。因此,这就产生了一个漏洞:audit token 可能在解析一个数据包与其事件处理器执行之间的时间窗口被替换。
要利用此漏洞,需要以下设置:
- 两个 mach services,称为
A
和B
,两者都能建立连接。 - Service
A
应对某个只有B
能执行(用户应用不能)的特定动作进行授权检查。 - Service
A
应发送一条期望回复的消息。 - 用户可以向
B
发送一条它会回复的消息。
利用过程如下:
- 等待 service
A
发送一条期望回复的消息。 - 不直接回复给
A
,而是劫持该 reply port 并用它向 serviceB
发送消息。 - 随后发送一条涉及被禁止动作的消息,期望它在与来自
B
的回复并发处理时被执行,从而利用 audit token 被覆盖的时机。
下面是该攻击场景的可视化表示:
 (1) (1) (1) (1) (1) (1).png)
.png)
发现问题的难点
- 定位实例困难:查找
xpc_connection_get_audit_token
的使用实例既困难于静态分析,也困难于动态分析。 - 方法论:使用 Frida hook
xpc_connection_get_audit_token
,并过滤那些非来自事件处理器的调用路径。然而,这种方法仅限于被 hook 的进程且需要进程处于活跃使用状态。 - 分析工具:使用 IDA/Ghidra 检查可达的 mach services,但该过程耗时且复杂,尤其是涉及 dyld shared cache 调用时。
- 脚本化限制:尝试脚本化对来自
dispatch_async
块调用xpc_connection_get_audit_token
的分析受限于解析 blocks 的复杂性以及与 dyld shared cache 的交互。
修复情况
- 已上报的问题:已向 Apple 提交报告,说明在
smd
中发现的一般性和具体问题。 - Apple 的回应:Apple 在
smd
中用xpc_dictionary_get_audit_token
替换了xpc_connection_get_audit_token
。 - 修复的性质:
xpc_dictionary_get_audit_token
被认为是安全的,因为它直接从与接收到的 XPC 消息关联的 mach message 中获取 audit token。不过,它并不是公开 API 的一部分,和xpc_connection_get_audit_token
类似。 - 没有更广泛的修复:尚不清楚 Apple 为什么没有实施更全面的修复,比如丢弃那些与连接保存的 audit token 不一致的消息。某些场景下 audit token 合法改变(例如使用
setuid
)的可能性可能是一个因素。 - 当前状态:该问题在 iOS 17 和 macOS 14 中仍然存在,这使得定位和理解该问题具有挑战性。
在实践中查找易受影响的代码路径(2024–2025)
审计 XPC services 时,重点关注在消息的事件处理器之外或在 reply 处理并发期间执行的授权检查。
静态初筛提示:
- 搜索可从通过
dispatch_async
/dispatch_after
队列排入的 blocks 或其他在消息处理器外运行的工作队列中到达的xpc_connection_get_audit_token
调用。 - 查找混合连接级和消息级状态的授权 helper(例如从
xpc_connection_get_pid
获取 PID,但从xpc_connection_get_audit_token
获取 audit token)。 - 在 NSXPC 代码中,确认检查是在
-listener:shouldAcceptNewConnection:
中完成的,或者对于按消息的检查,确保实现使用按消息的 audit token(例如在底层代码中通过消息的 dictionary 使用xpc_dictionary_get_audit_token
)。
动态初筛技巧:
- Hook
xpc_connection_get_audit_token
并标记那些其用户栈不包含事件递送路径(例如_xpc_connection_mach_event
)的调用。示例 Frida hook:
Interceptor.attach(Module.getExportByName(null, 'xpc_connection_get_audit_token'), {
onEnter(args) {
const bt = Thread.backtrace(this.context, Backtracer.ACCURATE)
.map(DebugSymbol.fromAddress).join('\n');
if (!bt.includes('_xpc_connection_mach_event')) {
console.log('[!] xpc_connection_get_audit_token outside handler\n' + bt);
}
}
});
注意事项:
- 在 macOS 上,对受保护的/Apple 二进制文件进行插装可能需要禁用 SIP 或使用开发环境;建议优先在你自己的构建或用户态服务(userland services)上进行测试。
- 对于 reply-forwarding races (Variant 2),通过 fuzzing
xpc_connection_send_message_with_reply
与正常请求的时序来监控回复数据包的并发解析,并检查在授权期间使用的有效 audit token 是否可以被影响。
你可能需要的利用原语
- Multi-sender setup (Variant 1): 创建到 A 和 B 的连接;复制 A 的 client port 的 send right 并将其作为 B 的 client port 使用,使得 B 的回复被送达至 A。
// Duplicate a SEND right you already hold
mach_port_t dup;
mach_port_insert_right(mach_task_self(), a_client, a_client, MACH_MSG_TYPE_MAKE_SEND);
dup = a_client; // use `dup` when crafting B’s connect packet instead of a fresh client port
- Reply hijack (Variant 2): 从 A 的 pending request (reply port) 中捕获 send-once right,然后使用该 reply port 向 B 发送伪造消息,使得 B 的回复在你的特权请求被解析时落到 A 上。
这些方法需要对低级别 mach message 进行构造,以适配 XPC bootstrap 和消息格式;请查看本节中的 mach/XPC primer 页面以获取精确的数据包布局和标志。
Useful tooling
- XPC sniffing/dynamic inspection: gxpc (open-source XPC sniffer) 可帮助枚举连接并观察流量,以验证 multi-sender 设置和时序。示例:
gxpc -p <PID> --whitelist <service-name>
. - Classic dyld interposing for libxpc: 对
xpc_connection_send_message*
和xpc_connection_get_audit_token
进行 interpose,以在 black-box testing 期间记录调用位置和调用栈。
References
- Sector 7 – Don’t Talk All at Once! Elevating Privileges on macOS by Audit Token Spoofing: https://sector7.computest.nl/post/2023-10-xpc-audit-token-spoofing/
- Apple – About the security content of macOS Ventura 13.4 (CVE‑2023‑32405): https://support.apple.com/en-us/106333
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
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。