
PoC pour CVE-2026-20700 montrant le page-in linking de dyld comme oracle de signature PAC sur arm64e. Inclut des générateurs de Mach-O façonnés à la main, des outils d'analyse de chained-fixup, et des presets de crash/exploit déterministes pour iOS 18.5.
Une exploration contrôlée du mécanisme de liaison page-in et de correction chaînée de dyld en tant qu'oracle de signature PAC, dans le contexte de CVE-2026-20700.
Sur arm64e, chaque pointeur de fonction est authentifié par le matériel. L'objectif ici est de montrer que dyld lui-même peut être dirigé — via un Mach-O fabriqué à la main — pour produire des pointeurs valides PAC dans des emplacements choisis par l'attaquant, en utilisant uniquement son propre mécanisme de correction normal.
Testé sur : iPhone 14 (iOS 18.5, arm64e).
__DATA.fixupPage64 — des valeurs page_start / next malformées font sortir dyld des limites de la page, prouvant l'atteignabilité des branches.dispatch_source_t et appelé naturellement via la boucle d'événements, sans invocation directe depuis le code 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
xcrun --sdk iphoneos --show-sdk-path)# Default full build (arm64, 99k symbols)
make
# Fast build for iteration
make SYMBOLS=10000
# Deterministic crash in fixupPage64 (branch reachability proof)
make stress
# Stable intra-image write-what-where
make exploit
# dyld writes PAC-valid pointer → dispatch calls it naturally
make chain_close
# Verify generated chained-fixup blob layout
make verify
# Parse LC_DYLD_CHAINED_FIXUPS header of libmalformed.dylib
make inspect
# Scan pointer sections (GOT/non-lazy) across compiled binaries
make scan
# Ad-hoc signed (Sideloadly)
make chain_close
# drag PoCApp.ipa into Sideloadly
# Real certificate
make resign IDENTITY="iPhone Developer: ..." PROFILE=embedded.mobileprovision
# Monitor logs
idevicesyslog | grep "POC"
Ordre de chargement à l'exécution dans PoCApp :
libexports.dylib — chargé en premier (RTLD_GLOBAL), fournit write_target_value et attacker_hook à tout dlopen ultérieur.libclient.dylib — chargé par le Thread B (pile de 128 Ko) ; environ 99k cibles de liaison stressent la passerelle de liaison page-in de dyld.libmalformed.dylib — chargé par le Thread A ; une chaîne de correction chaînée fabriquée à la main fait que dyld résout et écrit _write_target_value (et optionnellement _attacker_hook) dans __DATA+0x10 / +0x20.chain_close, le main enregistre __DATA+0x20 comme gestionnaire de timer — la boucle d'événements l'appelle 1 seconde plus tard sans invocation directe depuis le code PoC.Article technique complet en italien et en anglais. Couvre : mécanismes matériels PAC, dyld comme producteur de pointeurs, encodage de correction chaînée à partir de zéro, mécanismes de la passerelle de liaison page-in, pièges d'ingénierie Mach-O (sizeofcmds, nombre de sections, stride), disposition des données validées par canari, la passerelle des 99k symboles, preuve de crash d'atteignabilité, primitive intra-image stable, concept d'oracle de signature arm64e, fermeture de chaîne par timer dispatch.
| Fichier | Généré par |
|---|
exports.c | generators/gen_exports.py + symboles sentinelles Makefile |
client.c | generators/gen_client.py |
libmalformed.dylib | generators/gen_malformed_dylib.py |
libexports.dylib | clang à partir de exports.c |
libclient.dylib | clang à partir de client.c |
PoCApp | clang à partir de src/launcher.c |
PoCApp.ipa | Étape package du Makefile |
| Variable | Défaut | Description |
|---|
SYMBOLS | 99000 | Nombre de cibles de liaison dans libclient (< 100k pour la passerelle pre-26.3, < 64k pour 26.3+) |
STACK_KB | 128 | Taille de la pile du thread worker en Ko |
BURN_KB | 0 | Ko de pile à consommer avant dlopen (0 = auto) |
MARGIN_KB | 24 | Marge auto-burn |
ARM64E | 0 | Utiliser le format DYLD_CHAINED_PTR_ARM64E_USERLAND24 |
MALFORM_PAGEIN | 0 | Activer la chaîne page-in malformée |
MALFORM_TARGET_OFFSET | — | Décalage cible dans __DATA (ex. 0x10) |
CHAIN_CLOSE | 0 | Deuxième emplacement → _attacker_hook, activer la démo dispatch |
dispatch_source_t