对 macOS PackageKit 符号链接跟随漏洞(CVE-2026-28912)的独立逆向工程,以及一个可用的 PoC,演示安装器如何通过安装目标路径中的目录符号链接,以 root 身份写入攻击者控制的文件。
该漏洞是 PKCoreShove 的文件重链接逻辑中未同步的路径遍历。一个恶意的 .pkg 可以在非特权目录中声明目标路径,在路径的某个组件处放置一个指向特权位置的目录符号链接,并导致以 root 身份运行的安装器写入符号链接目标。Apple 的 26.6 修复添加了 _PKSIPOpenPathSafely,它使用 O_NOFOLLOW 遍历每个路径组件并拒绝任何符号链接。
| 字段 | 值 |
|---|---|
| CVE | CVE-2026-28912 |
| 组件 | PackageKit (PKCoreShove, PKBundleComponent) |
| 受影响 | macOS Tahoe 26.5 及更早版本 |
| 修复于 | macOS Tahoe 26.6 |
| 公告影响 | “一个应用可能能够获得 root 权限。” |
| CVSS v3.1 | 7.8 (High) |
| 报告者 | 根据 Apple 公告的原始发现者 |
Apple 针对 CVE-2026-28912 的公告记录了影响和修复版本。它没有记录技术机制:
在撰写本文时,未找到公开的技术说明。本仓库通过 26.4 到 26.6 之间 PackageKit 的独立逆向工程分析,以及一个演示该原语实际效果的可用 PoC,填补了这一空白。
这不是发现声明。该 CVE 由原始发现者报告,并由 Apple 修复。这里的贡献是技术分析和复现。
macOS 26.4 中的 PKCoreShove _relinkFile:dest: 在创建文件时逐组件遍历目标路径:
; macOS 26.4, PackageKit
1a9fb14f8 _relinkFile:dest:
...
bl _linkResolutionProhibitted ; returns 0 in the normal case
mov w8, 0x10 ; RENAME_NOFOLLOW_ANY
cmp w0, 0
csel w22, w8, wzr, ne ; w22 = 0x10 if prohibited, else 0
...
mov x2, x22
bl _renamex_np ; uses w22 as flags
当调用进程无法修改 SIP 文件时,_linkResolutionProhibitted 返回 0(false)——这是用户发起安装的正常情况。这会禁用 RENAME_NOFOLLOW_ANY,因此重命名会跟随目标路径中的任何符号链接。
26.6 修复添加了 _PKSIPOpenPathSafely,由 PKBundleComponent 的 initWithBundleAtPath:relativeToDestination: 调用:
; macOS 26.6, PackageKit
1aa6dfe18 bl _PKSIPOpenPathSafely ; walks each component with O_NOFOLLOW
_PKSIPOpenPathSafely:
任何指向预期路径之外的符号链接组件都会被以 EPERM 或 ELOOP 拒绝,安装失败。
完整说明见 docs/ANALYSIS.md,地址和日志样本见 docs/ARTIFACTS.md。
link.sh — 一个单文件、自包含的 PoC:
演示的影响是符号链接跟随原语——正是 26.6 修复所关闭的行为。
PoC 完全在目标上运行。符号链接和 payload 都在本地创建。这使 PoC 保持自包含且可复现,无需任何网络配置。
chmod +x link.sh
./link.sh
通过环境变量进行可选调整:
sudo CONTENT="test content" ./link.sh
CONTENT 设置通过符号链接写入的内容。默认是 hello。
[*] System information:
ProductName: macOS
ProductVersion: 26.4
BuildVersion: 25E246
[*] Setup...
Symlink: /Users/nerd/cve-poc/target → /Users/nerd/cve-poc/real
[*] Build .pkg...
Payload: /Users/nerd/cve-poc/payload/Users/nerd/cve-poc/target/poc.txt
Package: /Users/nerd/cve-poc/poc.pkg
[*] Install (password prompt expected)...
installer: Package name is poc
installer: Installing at base path /
installer: The install was successful.
[✓] VULNERABILITY CONFIRMED
Landed at /Users/nerd/cve-poc/real/poc.txt
Owner: root:wheel
Content: hello
CVE-2026-28912 trigger SUCCESSFUL
在已修补的系统(26.6)上,相同的 PoC 会失败:
[✗] Not triggered
No file was created in /Users/nerd/cve-poc/real/
反汇编比较见 docs/PATCH_DIFF.md。
PoC 中的两个细节是在开发过程中发现的,记录在此,以便构建类似工具的人不会遇到它们:
符号链接必须位于安装器遍历的路径组件处。目标叶子处的文件级符号链接会被 renamex_np 替换;只有路径中间的目录符号链接会被跟随。
payload 路径必须镜像目标路径。使用 pkgbuild --install-location / 时,payload 路径是目标路径去掉开头的 /。如果它们不匹配,安装器永远不会遍历符号链接。
两者都记录在 docs/ANALYSIS.md 中。
CVE-2026-28912 需要用户密码才能以 root 运行安装器。该密码用于安装,而不是用于符号链接写入。用户授权写入 $HOME/cve-poc/target/,而漏洞将写入重定向到符号链接指向的任何位置。这两个路径不同,用户永远不会看到差异。
本仓库仅供防御性安全研究和教育使用。
MIT。见 LICENSE。