
PoC для CVE-2026-20700, демонстрирующий page-in linking в dyld как оракул подписи PAC на arm64e. Включает собственноручно созданные генераторы Mach-O, инструменты анализа chained-fixup и детерминированные пресеты краша/эксплойта для iOS 18.5.
Контролируемое исследование подкачки страниц dyld и механизма цепочечных исправлений как оракула подписи PAC, в контексте CVE-2026-20700.
На arm64e каждый указатель на функцию аппаратно аутентифицирован. Цель здесь — показать, что dyld можно заставить — с помощью специально созданного Mach-O — создавать PAC-валидные указатели на выбранные атакующим слоты, используя только свой собственный нормальный механизм исправлений.
Протестировано на: iPhone 14 (iOS 18.5, arm64e).
__DATA.fixupPage64 — некорректные значения page_start / next выводят dyld за границы страницы, что доказывает достижимость ветвления.dispatch_source_t и вызывается естественным образом через цикл событий, без прямого вызова из кода PoC.dyld-signing-oracle-poc/
├── Makefile ← управляет полным конвейером
├── src/
│ └── launcher.c ← загрузчик iOS (2 потока + цепочка диспетчера)
├── generators/
│ ├── gen_exports.py ← генерирует exports.c (N фиктивных символов)
│ ├── gen_client.py ← генерирует client.c (N импортов, нагрузка на ворота dyld)
│ └── gen_malformed_dylib.py ← генерирует libmalformed.dylib (специально созданный Mach-O)
├── tools/
│ ├── scan_pointers.py ← классифицирует секции указателей Mach-O как W/R
│ └── inspect_fixups.py ← разбирает заголовок LC_DYLD_CHAINED_FIXUPS
└── blog/
├── it/
│ └── dyld-signing-oracle.md ← подробное описание на итальянском
└── en/
└── dyld-signing-oracle.md ← подробное описание на английском
xcrun --sdk iphoneos --show-sdk-path)# Полная сборка по умолчанию (arm64, 99k символов)
make
# Быстрая сборка для итераций
make SYMBOLS=10000
# Детерминированный сбой в fixupPage64 (доказательство достижимости ветвления)
make stress
# Стабильная запись-куда-угодно внутри образа
make exploit
# dyld записывает PAC-валидный указатель → диспетчер вызывает его естественным образом
make chain_close
# Проверить макет сгенерированного блока цепочечных исправлений
make verify
# Разобрать заголовок LC_DYLD_CHAINED_FIXUPS libmalformed.dylib
make inspect
# Сканировать секции указателей (GOT/неленивые) в скомпилированных бинарниках
make scan
# Ad-hoc подпись (Sideloadly)
make chain_close
# перетащите PoCApp.ipa в Sideloadly
# Настоящий сертификат
make resign IDENTITY="iPhone Developer: ..." PROFILE=embedded.mobileprovision
# Мониторинг логов
idevicesyslog | grep "POC"
Порядок загрузки в рантайме внутри PoCApp:
libexports.dylib — загружается первым (RTLD_GLOBAL), предоставляет write_target_value и attacker_hook для любого последующего dlopen.libclient.dylib — загружается потоком B (стек 128 КБ); около 99k целей связывания нагружают ворота подкачки страниц dyld.libmalformed.dylib — загружается потоком A; специально созданная цепочка цепочечных исправлений заставляет dyld разрешить и записать _write_target_value (и, опционально, _attacker_hook) в __DATA+0x10 / +0x20.chain_close главный поток регистрирует __DATA+0x20 как обработчик таймера — цикл событий вызывает его через 1 секунду без прямого вызова из кода PoC.Полное техническое описание на итальянском и английском. Охватывает: аппаратные механизмы PAC, dyld как производитель указателей, кодирование цепочечных исправлений с нуля, механику ворот подкачки страниц, тонкости создания Mach-O (sizeofcmds, количество секций, шаг), макет данных с проверкой канареек, ворота 99k символов, доказательство достижимости сбоя, стабильный примитив внутри образа, концепцию оракула подписи arm64e, цепочку закрытия таймера диспетчера.
| Файл | Создан с помощью |
|---|
exports.c | generators/gen_exports.py + символы-маяки Makefile |
client.c | generators/gen_client.py |
libmalformed.dylib | generators/gen_malformed_dylib.py |
libexports.dylib | clang из exports.c |
libclient.dylib | clang из client.c |
PoCApp | clang из src/launcher.c |
PoCApp.ipa | шаг package Makefile |
| Переменная | По умолчанию | Описание |
|---|
SYMBOLS | 99000 | Количество целей связывания в libclient (< 100k для ворот до 26.3, < 64k для 26.3+) |
STACK_KB | 128 | Размер стека рабочего потока в КБ |
BURN_KB | 0 | КБ стека для потребления перед dlopen (0 = авто) |
MARGIN_KB | 24 | Запас для автоматического потребления |
ARM64E | 0 | Использовать формат DYLD_CHAINED_PTR_ARM64E_USERLAND24 |
MALFORM_PAGEIN | 0 | Включить некорректную цепочку подкачки страниц |
MALFORM_TARGET_OFFSET | — | Целевое смещение внутри __DATA (например, 0x10) |
CHAIN_CLOSE | 0 | Второй слот → _attacker_hook, включить демонстрацию диспетчера |
dispatch_source_t