
Notas de engenharia reversa e um PoC funcional para o bug de seguimento de symlink do PackageKit do macOS (CVE-2026-28912), com diff de desmontagem da correção do 26.6.
Engenharia reversa independente do bug de seguimento de symlink do PackageKit do macOS (CVE-2026-28912), além de um PoC funcional que demonstra o instalador gravando um arquivo controlado pelo atacante como root através de um symlink de diretório no caminho de destino da instalação.
O bug é uma travessia de caminho não sincronizada na lógica de religação de arquivos do PKCoreShove. Um .pkg malicioso pode declarar um caminho de destino em um diretório sem privilégios, colocar um symlink de diretório em um dos componentes do caminho apontando para um local privilegiado, e fazer com que o instalador — executando como root — grave no alvo do symlink. A correção da Apple no 26.6 adiciona _PKSIPOpenPathSafely, que percorre cada componente do caminho com O_NOFOLLOW e rejeita qualquer symlink.
| Campo | Valor |
|---|---|
| CVE | CVE-2026-28912 |
| Componente | PackageKit (PKCoreShove, PKBundleComponent) |
| Afetado | macOS Tahoe 26.5 e anteriores |
| Corrigido em | macOS Tahoe 26.6 |
| Impacto do aviso | "Um app pode ser capaz de obter privilégios de root." |
| CVSS v3.1 | 7.8 (Alto) |
| Reportado por | Descobridores originais conforme o aviso da Apple |
O aviso da Apple para o CVE-2026-28912 documenta o impacto e a versão corrigida. Ele não documenta o mecanismo técnico:
Nenhum writeup técnico público foi encontrado no momento da escrita. Este repositório preenche essa lacuna com uma análise de engenharia reversa independente do PackageKit entre 26.4 e 26.6, e um PoC funcional que demonstra a primitiva ao vivo.
Isto não é uma reivindicação de descoberta. O CVE foi reportado pelos descobridores originais e corrigido pela Apple. A contribuição aqui é a análise técnica e a reprodução.
PKCoreShove _relinkFile:dest: no macOS 26.4 percorre o caminho de destino componente por componente ao criar um arquivo:
; 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 retorna 0 (falso) quando o processo chamador não pode modificar arquivos SIP — o caso normal para uma instalação iniciada pelo usuário. Isso desabilita RENAME_NOFOLLOW_ANY, então o rename segue qualquer symlink no caminho de destino.
A correção do 26.6 adiciona _PKSIPOpenPathSafely, chamada de PKBundleComponent initWithBundleAtPath:relativeToDestination::
; macOS 26.6, PackageKit
1aa6dfe18 bl _PKSIPOpenPathSafely ; walks each component with O_NOFOLLOW
_PKSIPOpenPathSafely:
Qualquer componente que seja um symlink apontando para fora do caminho pretendido é rejeitado com EPERM ou ELOOP, e a instalação falha.
Veja docs/ANALYSIS.md para o writeup completo e docs/ARTIFACTS.md para endereços e amostras de log.
link.sh — um PoC de arquivo único e autocontido:
O impacto demonstrado é a primitiva de seguimento de symlink — exatamente o comportamento que a correção do 26.6 fecha.
O PoC roda inteiramente no alvo. O symlink e o payload são ambos criados localmente. Isso mantém o PoC autocontido e reproduzível sem qualquer configuração de rede.
chmod +x link.sh
./link.sh
Ajuste opcional via variáveis de ambiente:
sudo CONTENT="test content" ./link.sh
CONTENT define o conteúdo gravado através do symlink. Por padrão é 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
Em um sistema corrigido (26.6), o mesmo PoC falha:
[✗] Not triggered
No file was created in /Users/nerd/cve-poc/real/
Veja docs/PATCH_DIFF.md para a comparação de desmontagem.
Dois detalhes no PoC foram descobertos durante o desenvolvimento e estão documentados aqui para que outros construindo ferramentas semelhantes não esbarrem neles:
O symlink deve estar em um componente do caminho que o instalador percorre. Um symlink em nível de arquivo na folha do destino é substituído por renamex_np; apenas um symlink de diretório no meio do caminho é seguido.
O caminho do payload deve espelhar o caminho de destino. Com pkgbuild --install-location /, o caminho do payload é o caminho de destino menos a / inicial. Se eles não corresponderem, o instalador nunca percorre o symlink.