PoC che dimostra dyld come oracolo di firma PAC tramite fixup concatenati Mach-O creati manualmente su arm64e, ottenendo scritture di puntatori PAC-validi controllate ed esecuzione di callback di dispatch su iOS.
Un'esplorazione controllata del meccanismo di page-in linking e chained fixup di dyld come oracolo di firma PAC, nel contesto di CVE-2026-20700.
Su arm64e, ogni puntatore a funzione è autenticato via hardware. L'obiettivo qui è dimostrare che dyld stesso può essere guidato — tramite un Mach-O realizzato a mano — a produrre puntatori validi per PAC in slot scelti dall'attaccante, usando nient'altro che il suo normale meccanismo di fixup.
Testato su: iPhone 14 (iOS 18.5, arm64e).
__DATA.fixupPage64 — valori malformati di page_start / next spingono dyld fuori dal confine di pagina, dimostrando la raggiungibilità del branch.dispatch_source_t e chiamato naturalmente attraverso l'event loop, senza invocazione diretta dal codice del PoC.dyld-signing-oracle-poc/
├── Makefile ← orchestrates the full pipeline
├── src/
│ └── launcher.c ← iOS launcher (2 threads + dispatch chain-close)
├── generators/
│ ├── gen_exports.py ← generates exports.c (N dummy symbols)
│ ├── gen_client.py ← generates client.c (N imports, dyld gate stress)
│ └── gen_malformed_dylib.py ← generates libmalformed.dylib (hand-crafted Mach-O)
├── tools/
│ ├── scan_pointers.py ← classifies Mach-O pointer sections as W/R
│ └── inspect_fixups.py ← parses LC_DYLD_CHAINED_FIXUPS header
└── blog/
├── it/
│ └── dyld-signing-oracle.md ← write-up completo in italiano
└── en/
└── dyld-signing-oracle.md ← full write-up in English
| File | Generato da |
|---|---|
exports.c | generators/gen_exports.py + simboli sentinella del Makefile |
client.c | generators/gen_client.py |
libmalformed.dylib | generators/gen_malformed_dylib.py |
libexports.dylib | clang da exports.c |
libclient.dylib | clang da client.c |
PoCApp | clang da src/launcher.c |
PoCApp.ipa | step package del Makefile |
xcrun --sdk iphoneos --show-sdk-path)# Build completo predefinito (arm64, 99k simboli)
make
# Build rapida per iterazione
make SYMBOLS=10000
# Crash deterministico in fixupPage64 (prova di raggiungibilità del branch)
make stress
# Write-what-where stabile intra-immagine
make exploit
# dyld scrive un puntatore valido per PAC → dispatch lo chiama naturalmente
make chain_close
# Verifica il layout del blob di chained fixup generato
make verify
# Analizza l'header LC_DYLD_CHAINED_FIXUPS di libmalformed.dylib
make inspect
# Scansiona le sezioni puntatore (GOT/non-lazy) nei binari compilati
make scan
| Variabile | Default | Descrizione |
|---|---|---|
SYMBOLS | 99000 | Numero di bind target in libclient (< 100k per il gate pre-26.3, < 64k per 26.3+) |
STACK_KB | 128 | Dimensione dello stack dei thread worker in KB |
BURN_KB | 0 | KB di stack da consumare prima di dlopen (0 = automatico) |
MARGIN_KB | 24 | Margine per il burn automatico |
ARM64E | 0 | Usa il formato DYLD_CHAINED_PTR_ARM64E_USERLAND24 |
MALFORM_PAGEIN | 0 | Abilita la catena page-in malformata |
MALFORM_TARGET_OFFSET | — | Offset target all'interno di __DATA (es. 0x10) |
CHAIN_CLOSE | 0 | Secondo slot → _attacker_hook, abilita la demo dispatch |
# Firma ad-hoc (Sideloadly)
make chain_close
# trascina PoCApp.ipa in Sideloadly
# Certificato reale
make resign IDENTITY="iPhone Developer: ..." PROFILE=embedded.mobileprovision
# Monitora i log
idevicesyslog | grep "POC"
Ordine di caricamento a runtime all'interno di PoCApp:
libexports.dylib — caricata per prima (RTLD_GLOBAL), fornisce write_target_value e attacker_hook a qualsiasi successiva dlopen.libclient.dylib — caricata dal Thread B (stack da 128KB); ~99k bind target mettono sotto stress il gate di page-in linking di dyld.libmalformed.dylib — caricata dal Thread A; la catena chained-fixup realizzata a mano fa sì che dyld risolva e scriva _write_target_value (e opzionalmente _attacker_hook) in __DATA+0x10 / +0x20.chain_close, il main registra __DATA+0x20 come handler di timer dispatch_source_t — l'event loop lo chiama 1 secondo dopo, senza invocazione diretta dal codice del PoC.Analisi tecnica completa in italiano e inglese. Copre: meccaniche hardware del PAC, dyld come produttore di puntatori, codifica dei chained fixup da zero, meccaniche del gate di page-in linking, insidie ingegneristiche del Mach-O (sizeofcmds, numero di sezioni, stride), layout dei dati validato tramite canary, il gate dei 99k simboli, crash come prova di raggiungibilità, primitiva stabile intra-immagine, concetto di oracolo di firma su arm64e, chain-close con timer dispatch.