Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cve-2026-28912 — Reverse engineering notes and a working PoC for the macOS PackageKit symlink-following bug (CVE-2026-28912), with disassembly diff of the 26.6 fix. | Kitploit
Tools/GitHubGitHub/jvidhan/cve-2026-28912
Privilege EscalationStatic AnalysisVulnerability AnalysisExploitationReverse EngineeringBinary AnalysisPapers & ResearchLearning & Education
GitHubjvidhan/cve-2026-28912

cve-2026-28912

Reverse engineering notes and a working PoC for the macOS PackageKit symlink-following bug (CVE-2026-28912), with disassembly diff of the 26.6 fix.

View Repository
114 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-28912 — Reverse engineering notes and reproduction

Independent reverse engineering of the macOS PackageKit symlink-following bug (CVE-2026-28912), plus a working PoC that demonstrates the installer writing an attacker-controlled file as root through a directory symlink in the install destination path.

The bug is an unsynchronized path walk in PKCoreShove's file-relink logic. A malicious .pkg can declare a destination path in an unprivileged directory, place a directory symlink at one of the path components pointing at a privileged location, and cause the installer — running as root — to write into the symlink target. Apple's 26.6 fix adds _PKSIPOpenPathSafely which walks each path component with O_NOFOLLOW and rejects any symlink.


CVE at a glance

FieldValue
CVECVE-2026-28912
ComponentPackageKit (PKCoreShove, PKBundleComponent)
AffectedmacOS Tahoe 26.5 and earlier
Patched inmacOS Tahoe 26.6
Advisory impact"An app may be able to gain root privileges."
CVSS v3.17.8 (High)
Reported byOriginal finders per Apple's advisory

Why this writeup exists

Apple's advisory for CVE-2026-28912 documents the impact and the patched version. It does not document the technical mechanism:

  • Which function in PackageKit follows the symlink
  • Why the installer walks the destination path without checking each component
  • Which function was added in 26.6 to close the bug
  • Why the fix inserts an O_NOFOLLOW walk at exactly that point
  • Why the PoC must use a directory symlink rather than a file symlink
  • Why a file-level symlink at the destination is replaced instead of followed

No public technical writeup was found at the time of writing. This repository fills that gap with an independent reverse engineering analysis of PackageKit between 26.4 and 26.6, and a working PoC that demonstrates the primitive live.

This is not a discovery claim. The CVE was reported by the original finders and patched by Apple. The contribution here is the technical analysis and the reproduction.


Summary of the vulnerability

PKCoreShove _relinkFile:dest: in macOS 26.4 walks the destination path component-by-component when creating a file:

; 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 returns 0 (false) when the calling process cannot modify SIP files — the normal case for a user-initiated install. That disables RENAME_NOFOLLOW_ANY, so the rename follows any symlink in the destination path.

The 26.6 fix adds _PKSIPOpenPathSafely, called from PKBundleComponent initWithBundleAtPath:relativeToDestination::

; macOS 26.6, PackageKit
1aa6dfe18   bl   _PKSIPOpenPathSafely    ; walks each component with O_NOFOLLOW

_PKSIPOpenPathSafely:

  1. Opens each path component with O_NOFOLLOW (0x4)
  2. Detects symlinks via S_IFLNK (0xa000 in st_mode)
  3. Checks SIP protection via _PKSIPFullyProtected
  4. Reads SF_RESTRICTED via fgetattrlist
  5. Uses close_drop_np to drop the sandbox extension on cleanup

Any component that is a symlink pointing outside the intended path is rejected with EPERM or ELOOP, and the install fails.


What this repository contains

Reverse engineering

  • Disassembly diff of PKCoreShove _relinkFile:dest: and _linkResolutionProhibitted between 26.4 and 26.6
  • Identification of the vulnerable field: _linkResolutionProhibitted returning 0 for non-SIP-modifying processes
  • Identification of the fix: _PKSIPOpenPathSafely added to PKBundleComponent, per-component O_NOFOLLOW walk
  • Syscall analysis: installer uses renamex_np with RENAME_NOFOLLOW_ANY = 0x10 only when the caller can modify SIP files
  • Runtime confirmation: fs_usage shows the installer stat/listxattr on the symlink target and writes through the directory symlink

See docs/ANALYSIS.md for the full writeup and docs/ARTIFACTS.md for addresses and log samples.

Reproduction

  • link.sh — a single-file, self-contained PoC:

    1. Creates a directory symlink at $HOME/cve-poc/target → $HOME/cve-poc/real
    2. Builds a .pkg whose payload declares a file at $HOME/cve-poc/target/poc.txt
    3. Installs it with sudo installer
    4. Verifies the file landed in $HOME/cve-poc/real/poc.txt — with root:wheel ownership

What this PoC demonstrates

  • A .pkg payload declaring a file under an unprivileged directory
  • The installer following a directory symlink at that path
  • A root-owned file being created outside the declared destination
  • Live capture of the write via fs_usage

What this PoC does NOT demonstrate

  • A file-level symlink at the destination. File symlinks are replaced by renamex_np, not followed — the write must be through a directory symlink in the path.
  • Code execution, privilege escalation, or a shell on the victim
  • Weaponization: the PoC defaults to $HOME/cve-poc/real/, not /etc/sudoers.d/ or /Library/LaunchDaemons/

The demonstrated impact is the symlink-following primitive — the exact behavior the 26.6 fix closes.


Requirements

Target (victim) host

  • macOS Tahoe 26.5 or earlier (vulnerable PackageKit)
  • pkgbuild, installer, fs_usage
  • Root (for the installer)

No separate attacker host needed

The PoC runs entirely on the target. The symlink and payload are both created locally. This keeps the PoC self-contained and reproducible without any network configuration.


Usage

chmod +x link.sh
./link.sh

Optional tuning via environment variables:

sudo CONTENT="test content" ./link.sh

CONTENT sets the content written through the symlink. By default it is hello.

Expected output

[*] 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

Verification of the patch

On a patched system (26.6), the same PoC fails:

[✗] Not triggered
No file was created in /Users/nerd/cve-poc/real/

See docs/PATCH_DIFF.md for the disassembly comparison.


.pkg gotchas (for reproducibility)

Two details in the PoC were discovered during development and are documented here so others building similar tooling don't hit them:

  1. The symlink must be at a path component the installer walks. A file-level symlink at the destination leaf is replaced by renamex_np; only a directory symlink in the middle of the path is followed.

  2. The payload path must mirror the destination path. With pkgbuild --install-location /, the payload path is the destination path minus the leading /. If they don't match, the installer never walks through the symlink.

Both are documented in docs/ANALYSIS.md.


A note on why the password is not the mitigation

CVE-2026-28912 requires the user's password to run the installer as root. That password is for the install, not for the symlink write. The user authorizes writing to $HOME/cve-poc/target/, and the vulnerability redirects the write to whatever the symlink points to. The two paths differ, and the user never sees the difference.


Credits

Download Tool