Експлуатація iOS
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.
Захисти iOS від експлойтів
1. Code Signing / Runtime Signature Verification
Введено рано (iPhone OS → iOS) Це один з фундаментальних захистів: весь виконуваний код (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) має бути криптографічно підписаним сертифікатним ланцюжком, що корениться в довірі Apple. Під час виконання, перед завантаженням бінарника в пам’ять (або перед переходами через певні кордони), система перевіряє його підпис. Якщо код змінено (зміни бітів, патчі) або він не підписаний, завантаження не відбувається.
- Перешкоджає: етапу «classic payload drop + execute» в ланцюгах експлойтів; довільному інжекшену коду; модифікації існуючого бінарника для вставки шкідливої логіки.
- Деталі механізму:
- Mach-O loader (і dynamic linker) перевіряють сторінки коду, сегменти, entitlements, team IDs, і що підпис покриває вміст файлу.
- Для регіонів пам’яті, як-от JIT caches або динамічно згенерований код, Apple вимагає, щоб сторінки були підписані або перевірені через спеціальні API (наприклад,
mprotectз перевірками code-sign). - Підпис включає entitlements та ідентифікатори; ОС контролює, що доступ до певних API або привілейованих можливостей вимагає конкретних entitlements, які неможливо підробити.
Приклад
Припустимо, що експлойт отримує виконання коду в процесі і намагається записати shellcode в heap і перейти на нього. На iOS ця сторінка повинна бути позначена як executable **і** відповідати обмеженням code-signature. Оскільки shellcode не підписаний сертифікатом Apple, перехід не вдається або система відмовляє у встановленні цієї області пам’яті як виконуваної.2. CoreTrust
Введено приблизно в еру iOS 14+ (або поступово на новіших пристроях / пізніших iOS) CoreTrust — це підсистема, яка виконує runtime signature validation бінарників (включно із системними та користувацькими бінарниками) проти root certificate Apple, замість покладання на кешовані userland trust stores.
- Перешкоджає: післяінсталяційним змінам бінарників, технікам jailbreak, які намагаються замінити або запатчити системні бібліотеки чи user apps; обману системи шляхом підміни довірених бінарників на шкідливі.
- Деталі механізму:
- Замість довіри локальній базі довіри або кешу сертифікатів, CoreTrust звертається безпосередньо до кореневого сертифіката Apple або перевіряє проміжні сертифікати у захищеному ланцюжку.
- Воно гарантує, що модифікації (наприклад у файловій системі) існуючих бінарників виявляються і відхиляються.
- Воно зв’язує entitlements, team IDs, code signing flags та іншу метадані з бінарником при завантаженні.
Приклад
Jailbreak може спробувати замінити `SpringBoard` або `libsystem` на запатчену версію, щоб отримати персистентність. Але коли loader ОС або CoreTrust перевіряє, він помічає невідповідність підпису (або змінені entitlements) і відмовляє в виконанні.3. Data Execution Prevention (DEP / NX / W^X)
Введено в багатьох ОС раніше; iOS довго мав NX-bit / w^x DEP забезпечує, що сторінки, позначені як writable (для даних), є не виконуваними, а сторінки, позначені як executable, — не записуваними. Неможливо просто записати shellcode у heap або stack і виконати його.
- Перешкоджає: прямому виконанню shellcode; класичному buffer-overflow → перехід на injected shellcode.
- Деталі механізму:
- MMU / прапори захисту пам’яті (через page tables) забезпечують розділення.
- Будь-яка спроба позначити writable-сторінку як executable викликає системну перевірку (і забороняється або вимагає схвалення code-sign).
- У багатьох випадках зробити сторінки виконуваними можна лише через ОС API, що накладають додаткові обмеження або перевірки.
Приклад
Переповнення записує shellcode в heap. Атакуючий намагається виконати `mprotect(heap_addr, size, PROT_EXEC)`, щоб зробити його виконуваним. Але система відмовляє або вимагає, щоб нова сторінка пройшла code-sign перевірки (яких shellcode не пройде).4. Address Space Layout Randomization (ASLR)
Введено приблизно в еру iOS 4–5 ASLR рандомізує базові адреси ключових регіонів пам’яті: бібліотек, heap, stack тощо при кожному запуску процесу. Адреси гаджетів змінюються між запусками.
- Перешкоджає: хардкодингу адрес гаджетів для ROP/JOP; статичним ланцюгам експлойтів; сліпим переходам на відомі офсети.
- Деталі механізму:
- Кожна завантажена бібліотека / динамічний модуль перебазовується за випадковим офсетом.
- Бази стеку і heap також рандомізуються (в межах певної ентропії).
- Іноді інші регіони (наприклад mmap-розподіли) теж рандомізуються.
- У поєднанні з заходами проти information-leak, це змушує атакуючого спочатку виявити або leak адресу/вказівник, щоб отримати базові адреси під час виконання.
Приклад
ROP-ланцюг очікує гаджет на `0x….lib + offset`. Але оскільки `lib` перерелокований щоразу по-різному, хардкодний ланцюг не працює. Експлойт повинен спочатку leak базову адресу модуля, перш ніж порахувати адреси гаджетів.5. Kernel Address Space Layout Randomization (KASLR)
Введено приблизно в еру iOS 5–6 Аналогічно користувацькому ASLR, KASLR рандомізує базу kernel text і інші структури ядра під час завантаження.
- Перешкоджає: експлойтам на рівні ядра, що покладаються на фіксовані локації коду або даних ядра; статичним kernel-експлойтам.
- Деталі механізму:
- При кожному завантаженні базова адреса ядра рандомізується (в межах діапазону).
- Структури даних ядра (наприклад
task_structs,vm_mapтощо) також можуть бути переміщені або мати зсуви. - Атакуючим потрібно спочатку leak-нути вказівники ядра або використати вразливість для витоку інформації, аби порахувати офсети перед тим, як підміняти структури або код ядра.
Приклад
Локальна уразливість прагне пошкодити вказівник функції ядра (наприклад у `vtable`) за адресою `KERN_BASE + offset`. Але оскільки `KERN_BASE` невідомий, атакуючий повинен спочатку leak-нути його (наприклад через read-примітив), перш ніж правильно порахувати адресу для корупції.6. Kernel Patch Protection (KPP / AMCC)
Введено в новіших iOS / A-series апаратурі (приблизно після iOS 15–16 або на новіших чіпах) KPP (також відоме як AMCC) безперервно моніторить цілісність kernel text-сторінок (через хеш або контрольну суму). Якщо виявлено підміни (патчі, inline hooks, зміни коду) поза дозволеними вікнами, воно спричиняє kernel panic або перезавантаження.
- Перешкоджає: персистентному патчингу ядра (зміні інструкцій ядра), inline hooks, статичним перезаписам функцій.
- Деталі механізму:
- Апаратний або firmware-модуль моніторить регіон kernel text.
- Він періодично або за запитом повторно хешує сторінки і порівнює з очікуваними значеннями.
- Якщо збіги відсутні поза бенінними вікнами оновлення, пристрій панікує (щоб уникнути персистентної шкідливої модифікації).
- Атакуючі повинні або уникати вікон виявлення, або використовувати легітимні шляхи для патчів.
Приклад
Експлойт намагається запатчити пролог функції ядра (наприклад `memcmp`), щоб перехоплювати виклики. Але KPP помічає, що хеш сторінки коду більше не збігається з очікуваним і спричиняє kernel panic, що крашить пристрій до того, як патч усталиться.7. Kernel Text Read‐Only Region (KTRR)
Введено в сучасних SoC (приблизно після A12 / на новішому апаратному забезпеченні) KTRR — апаратно-примусовий механізм: як тільки kernel text блокується на ранній стадії завантаження, він стає read-only з EL1 (ядро), що забороняє подальші записи в сторінки коду.
- Перешкоджає: будь-яким модифікаціям коду ядра після завантаження (наприклад, патчинг, інлайнове інжектування коду) на привілейованому рівні EL1.
- Деталі механізму:
- Під час завантаження (у secure/bootloader stage) memory controller (або захищений hardware unit) позначає фізичні сторінки, що містять kernel text, як read-only.
- Навіть якщо експлойт отримує повні права ядра, він не може записати в ці сторінки, щоб патчити інструкції.
- Щоб змінити їх, атакуючий повинен спочатку скомпрометувати ланцюжок завантаження або підмінити сам KTRR.
Приклад
Експлойт для підвищення привілеїв переходить в EL1 і записує trampoline у функцію ядра (наприклад у syscall handler). Але оскільки сторінки заблоковані як read-only через KTRR, запис не вдається (або спричиняє fault), тому патчі не застосовуються.8. Pointer Authentication Codes (PAC)
Введено з ARMv8.3 (апаратно), Apple починаючи з A12 / iOS ~12+
- PAC — це апаратна функція, представлена в ARMv8.3-A, щоб виявляти підміну значень указівників (адреси повернення, вказівники функцій, певні вказівники на дані) шляхом впровадження невеликого криптографічного підпису ( «MAC») у невикористовувані старші біти указівника.
- Підпис (“PAC”) обчислюється над значенням указівника плюс modifier (контекстне значення, наприклад stack pointer або інші відмінні дані). Таким чином те саме значення указівника в різних контекстах має різний PAC.
- Під час використання, перед дереференсом або переходом через такий указівник, інструкція authenticate перевіряє PAC. Якщо дійсний, PAC знімається і отримується чистий указівник; якщо недійсний, указівник стає «poisoned» (або згенеровано fault).
- Ключі, які використовуються для створення/перевірки PAC, зберігаються в привілейованих регістрах (EL1, kernel) і недоступні з user mode.
- Оскільки не всі 64 біти указівника використовуються в багатьох системах (наприклад 48-бітна адресна область), верхні біти є «зайвими» і можуть містити PAC без зміни ефективної адреси.
Архітектурна основа та типи ключів
-
ARMv8.3 вводить п’ять 128-бітних ключів (кожен реалізований через два 64-бітні системні регістри) для pointer authentication.
-
APIAKey — для instruction pointers (домен “I”, ключ A)
-
APIBKey — другий ключ для instruction pointers (домен “I”, ключ B)
-
APDAKey — для data pointers (домен “D”, ключ A)
-
APDBKey — для data pointers (домен “D”, ключ B)
-
APGAKey — “generic” ключ, для підпису не-указчикових даних або інших загальних використань
-
Ці ключі зберігаються в привілейованих системних регістрах (доступних лише на EL1/EL2 тощо), недоступних із user mode.
-
PAC обчислюється криптографічною функцією (ARM пропонує QARMA як алгоритм) з використанням:
- Значення указівника (канонічна частина)
- modifier (контекстне значення, як соль)
- Секретного ключа
- Деякої внутрішньої логіки змішування Якщо отриманий PAC збігається з тим, що збережено у старших бітах указівника, автентифікація проходить.
Сімейства інструкцій
Найменування: PAC / AUT / XPAC, далі літери домену.
PACxxінструкції підписують указівник і вставляють PACAUTxxінструкції автентифікують + знімають (перевіряють і видаляють PAC)XPACxxінструкції знімають PAC без перевірки
Домени / суфікси:
| Mnemonic | Значення / Домен | Ключ / Домен | Приклад використання в Assembly |
|---|---|---|---|
| PACIA | Sign instruction pointer with APIAKey | “I, A” | PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1 |
| PACIB | Sign instruction pointer with APIBKey | “I, B” | PACIB X2, X3 |
| PACDA | Sign data pointer with APDAKey | “D, A” | PACDA X4, X5 |
| PACDB | Sign data pointer with APDBKey | “D, B” | PACDB X6, X7 |
| PACG / PACGA | Generic (non-pointer) signing with APGAKey | “G” | PACGA X8, X9, X10 (sign X9 with modifier X10 into X8) |
| AUTIA | Authenticate APIA-signed instruction pointer & strip PAC | “I, A” | AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip |
| AUTIB | Authenticate APIB domain | “I, B” | AUTIB X2, X3 |
| AUTDA | Authenticate APDA-signed data pointer | “D, A” | AUTDA X4, X5 |
| AUTDB | Authenticate APDB-signed data pointer | “D, B” | AUTDB X6, X7 |
| AUTGA | Authenticate generic / blob (APGA) | “G” | AUTGA X8, X9, X10 (validate generic) |
| XPACI | Strip PAC (instruction pointer, no validation) | “I” | XPACI X0 — remove PAC from X0 (instruction domain) |
| XPACD | Strip PAC (data pointer, no validation) | “D” | XPACD X4 — remove PAC from data pointer in X4 |
Є спеціалізовані / скорочені форми:
PACIASP— скорочення дляPACIA X30, SP(підписати link register, використовуючи SP як modifier)AUTIASP—AUTIA X30, SP(автентифікувати link register з SP)- Комбіновані форми, як
RETAA,RETAB(authenticate-and-return) абоBLRAA(authenticate & branch) існують в розширеннях ARM / підтримці компілятора. - Також варіанти із нульовим модифікатором:
PACIZA/PACIZB, де modifier неявно нульовий тощо.
Модифікатори
Головна мета modifier — зв’язати PAC із конкретним контекстом, тож те саме адресне значення, підписане в різних контекстах, дасть різні PAC-и. Це унеможливлює просте повторне використання указівника між фреймами або об’єктами. Це як додавання солі до хешу.
Отже:
- modifier — контекстне значення (інший регістр), яке змішується в обчисленні PAC. Типові вибори: stack pointer (
SP), frame pointer або якийсь ID об’єкта. - Використання SP як modifier поширене для підпису адрес повернення: PAC прив’язується до конкретного стекового фрейму. Якщо намагатися повторно використати LR в іншому фреймі, modifier зміниться і перевірка PAC не пройде.
- Те саме значення указівника, підписане з різними modifier-ами, дає різні PAC-и.
- modifier не обов’язково має бути секретним, але бажано, щоб він не контролювався атакуючим.
- Для інструкцій, які підписують або перевіряють указівники, коли немає осмисленого modifier-а, деякі форми використовують нуль або імпліцитну константу.
Apple / iOS / XNU кастомізації та спостереження
- Впровадження PAC у Apple включає per-boot diversifiers, тож ключі або модифікації змінюються при кожному завантаженні, що унеможливлює повторне використання між перезавантаженнями.
- Також є cross-domain mitigations, щоб PAC-і, підписані в user mode, не могли легко використовуватись у kernel mode тощо.
- На Apple M1 / Apple Silicon реверс-інженіринг показав, що є дев’ять типів модифікаторів і Apple-специфічні системні регістри для контролю ключів.
- Apple використовує PAC у багатьох підсистемах ядра: підпис адрес повернення, цілісність вказівників у даних ядра, підписані контексти потоків тощо.
- Google Project Zero показав, що при потужних примітивах читання/запису пам’яті в ядрі можна було сфабрикувати kernel PACs (для A keys) на пристроях A12-ера, але Apple виправили багато таких шляхів.
- У системі Apple деякі ключі є глобальними для всього ядра, тоді як процеси користувачів можуть отримувати per-process випадковість ключів.
Обходи PAC
- Kernel-mode PAC: теоретичні vs реальні обходи
- Оскільки ключі kernel PAC і логіка жорстко контролюються (привілейовані регістри, diversifiers, ізоляція доменів), фальсифікувати довільні підписані kernel-указівники дуже складно.
- Azad у 2020 “iOS Kernel PAC, One Year Later” повідомив, що в iOS 12-13 він знайшов кілька часткових обхідних шляхів (signing gadgets, reuse of signed states, unprotected indirect branches), але не універсальний загальний обхід. bazad.github.io
- Apple’s “Dark Magic” кастомізації додатково звужують поверхні для експлуатації (domain switching, per-key enabling bits). i.blackhat.com
- Є відомий kernel PAC bypass CVE-2023-32424 на Apple silicon (M1/M2), повідомлений Zecao Cai та ін. i.blackhat.com
- Але ці обходи часто залежать від дуже специфічних гаджетів або імплементаційних багів; вони не є загальним універсальним обходом.
Отже kernel PAC вважається дуже міцним, хоча й не ідеальним.
- User-mode / runtime PAC обходи
Ці обходи зустрічаються частіше і експлуатують недосконалості в тому, як PAC застосовується або використовується в dynamic linking / runtime фреймворках. Нижче класи з прикладами.
2.1 Shared Cache / A key issues
- dyld shared cache — великий попередньо зв’язаний блоб системних фреймворків і бібліотек. Оскільки він широко використовується, вказівники функцій всередині shared cache вже «pre-signed» і використовуються багатьма процесами. Атакуючі цілеспрямовують ці вже-підписані вказівники як «PAC oracles».
- Деякі техніки обходу намагаються витягти або повторно використати A-key підписані вказівники, присутні в shared cache, і застосувати їх в гаджетах.
- Доповідь “No Clicks Required” описує створення oracle на основі shared cache, щоб вивести відносні адреси і комбінувати це з підписаними вказівниками для обходу PAC. saelo.github.io
- Також імпорти вказівників функцій із shared libraries в userspace були знайдені як недостатньо захищені PAC-ом, що дозволяло атакуючому отримувати вказівники функцій без зміни їх підпису. (Project Zero bug entry) bugs.chromium.org
2.2 dlsym(3) / dynamic symbol resolution
- Один відомий обхід — виклик
dlsym()для отримання вже підписаного вказівника функції (signed with A-key, diversifier zero), а потім його використання. Оскількиdlsymповертає легітимно підписаний вказівник, його використання обходить потребу підробляти PAC. - Блог Epsilon описує, як деякі обхідні шляхи експлуатують це: виклик
dlsym("someSym")повертає підписаний вказівник і може бути використаний для непрямих викликів. blog.epsilon-sec.com - Synacktiv у “iOS 18.4 — dlsym considered harmful” описує баг: деякі символи, розв’язані через
dlsymна iOS 18.4, повертають вказівники, які неправильно підписані (або з багнутими diversifiers), що дає можливість непередбачуваного обходу PAC. Synacktiv - Логіка в dyld для dlsym включає: коли
result->isCode, вони підписують повернутий вказівник через__builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), тобто з контекстом нуль. blog.epsilon-sec.com
Отже dlsym часто є вектором в user-mode PAC обходах.
2.3 Інші DYLD / runtime релокації
- DYLD loader і логіка динамічних релокацій є складними і іноді тимчасово маплять сторінки як read/write, щоб виконати релокації, а потім повертають їх назад у read-only. Атакуючі експлуатують ці вікна. Доповідь Synacktiv описує “Operation Triangulation”, таймінг-базований обхід PAC через динамічні релокації. Synacktiv
- Тепер сторінки DYLD захищено SPRR / VM_FLAGS_TPRO (деякі прапори захисту для dyld). Але в ранніших версіях охорона була слабшою. Synacktiv
- У exploit-ланцюгах для WebKit, DYLD loader часто стає мішенню для обходу PAC. Слайди згадують, що багато обходів PAC були націлені саме на DYLD loader (через релокацію, interposer hooks). Synacktiv
2.4 NSPredicate / NSExpression / ObjC / SLOP
- В userland-ланцюгах експлойтів Objective-C runtime методи, такі як
NSPredicate,NSExpressionабоNSInvocation, використовуються для непрямого перенаправлення викликів без очевидного підробляння указівників. - На старіших iOS (до PAC) експлойт використовував fake NSInvocation об’єкти для виклику довільних селекторів на керованій пам’яті. З PAC потрібні модифікації. Але техніка SLOP (SeLector Oriented Programming) також була розширена під PAC. Project Zero
- Початкова техніка SLOP дозволяла ланцюжити ObjC виклики шляхом створення фальшивих invocations; обхід спирався на те, що ISA або selector pointers іноді не повністю захищені PAC. Project Zero
- У середовищах, де pointer authentication застосовано частково, методи / селектори / target pointers можуть не завжди мати PAC-захист, що дає можливість обійти захист.
Приклад потоку
Example Signing & Authenticating
``` ; Example: function prologue / return address protection my_func: stp x29, x30, [sp, #-0x20]! ; push frame pointer + LR mov x29, sp PACIASP ; sign LR (x30) using SP as modifier ; … body … mov sp, x29 ldp x29, x30, [sp], #0x20 ; restore AUTIASP ; authenticate & strip PAC ret; Example: indirect function pointer stored in a struct ; suppose X1 contains a function pointer PACDA X1, X2 ; sign data pointer X1 with context X2 STR X1, [X0] ; store signed pointer
; later retrieval: LDR X1, [X0] AUTDA X1, X2 ; authenticate & strip BLR X1 ; branch to valid target
; Example: stripping for comparison (unsafe) LDR X1, [X0] XPACI X1 ; strip PAC (instruction domain) CMP X1, #some_label_address BEQ matched_label
</details>
<details>
<summary>Приклад</summary>
A buffer overflow overwrites a return address on the stack. The attacker writes the target gadget address but cannot compute the correct PAC. When the function returns, the CPU’s `AUTIA` instruction faults because the PAC mismatch. The chain fails.
Project Zero’s analysis on A12 (iPhone XS) showed how Apple’s PAC is used and methods of forging PACs if an attacker has a memory read/write primitive.
</details>
### 9. **Branch Target Identification (BTI)**
**Introduced with ARMv8.5 (later hardware)**
BTI — апаратна функція, яка перевіряє **indirect branch targets**: при виконанні `blr` або непрямих викликів/переходів, ціль має починатися з **BTI landing pad** (`BTI j` або `BTI c`). Стрибок у gadget-адреси, що не містять такого landing pad, викликає виняток.
LLVM’s implementation notes three variants of BTI instructions and how they map to branch types.
| Варіант BTI | Що дозволяє (які типи переходів) | Типове розміщення / випадок використання |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Цілі для викликів у стилі *call* (наприклад `BLR`, або `BR`, що використовує X16/X17) | Розміщують на вході функцій, які можуть викликатися непрямо |
| **BTI J** | Цілі для переходів у стилі *jump* (наприклад `BR`, що використовується для tail calls) | Ставлять на початку блоків, доступних через jump tables або tail-calls |
| **BTI JC** | Працює як C і як J одночасно | Може бути ціллю як для call, так і для jump переходів |
- У коді, скомпільованому з примусовою перевіркою branch target, компілятори вставляють інструкцію BTI (C, J або JC) в кожну валідну ціль непрямого переходу (початки функцій або блоки, досяжні через переходи), щоб непрямі переходи проходили тільки туди.
- **Direct branches / calls** (тобто фіксовані адреси `B`, `BL`) **не обмежуються** BTI. Припущення: сторінки коду вважаються довіреними і атакуючий не може їх змінити (тому direct branches вважаються безпечними).
- Також **RET / return** інструкції загалом не обмежуються BTI, оскільки адреси повернення захищені через PAC або механізми підписування повернення.
#### Mechanism and enforcement
- Коли CPU декодує **indirect branch (BLR / BR)** на сторінці, позначеній як “guarded / BTI-enabled,” воно перевіряє, чи перша інструкція за цільовою адресою є валідним BTI (C, J або JC, як дозволено). Якщо ні — виникає **Branch Target Exception**.
- Кодування інструкції BTI спроєктовано так, щоб перевикористати опкоди, раніше зарезервовані для NOPs (в ранніших версіях ARM). Тому бінарники з BTI залишаються зворотно сумісними: на апаратурі без підтримки BTI ці інструкції поводяться як NOPs.
- Компіляторні проходи, що додають BTI, вставляють їх лише там, де потрібно: у функціях, які можуть викликатися непрямо, або в базових блоках, куди спрямовують переходи.
- Деякі патчі та код LLVM показують, що BTI не вставляється для *усіх* базових блоків — лише для тих, які є потенційними цілями переходів (наприклад, з switch / jump tables).
#### BTI + PAC synergy
PAC захищає значення покажчика (джерело) — гарантує, що ланцюжок непрямих викликів / повернень не був підроблений.
BTI гарантує, що навіть валідний покажчик має спрямовуватися лише в коректно позначені точки входу.
У поєднанні, атакуючому потрібні й валідний покажчик з правильною PAC, і те, щоб ціль мала вставлений на ній BTI. Це ускладнює побудову експлойтних гаджетів.
#### Example
<details>
<summary>Приклад</summary>
An exploit tries to pivot into gadget at `0xABCDEF` that doesn’t start with `BTI c`. The CPU, upon executing `blr x0`, checks the target and faults because the instruction alignment doesn’t include a valid landing pad. Thus many gadgets become unusable unless they include BTI prefix.
</details>
### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Introduced in more recent ARMv8 extensions / iOS support (for hardened kernel)**
#### PAN (Privileged Access Never)
- **PAN** — функція, введена в **ARMv8.1-A**, яка забороняє **привілейованому коду** (EL1 або EL2) **читати або писати** пам’ять, позначену як **user-accessible (EL0)**, якщо PAN явно не відключено.
- Ідея: навіть якщо kernel буде скомпрометовано або обдурено, воно не може довільно розіменувати user-space покажчики без попереднього *очищення* PAN, зменшуючи ризики експлойтів типу **ret2usr** або зловживання буферами, контрольованими користувачем.
- Коли PAN увімкнено (PSTATE.PAN = 1), будь-яка привілейована інструкція load/store, яка звертається до віртуальної адреси, що «доступна на EL0», викликає **permission fault**.
- Kernel, коли йому потрібно легітимно отримати доступ до пам’яті користувача (наприклад, копіювання даних з/в користувацькі буфери), повинен **тимчасово відключити PAN** (або використовувати інструкції для «непривілейованих» load/store) щоб дозволити цей доступ.
- У Linux на ARM64 підтримка PAN з’явилась близько 2015 року: патчі ядра додали детекцію цієї можливості і замінили `get_user` / `put_user` тощо на варіанти, що очищають PAN навколо доступів до пам’яті користувача.
**Key nuance / limitation / bug**
- Як відзначали Siguza та інші, специфікація (або неоднозначність у ній) означає, що **execute-only user mappings** (`--x`) можуть **не викликати PAN**. Іншими словами, якщо сторінка користувача позначена як виконувана, але без права на читання, спроба ядра прочитати цю сторінку може обійти PAN, оскільки архітектура вважає, що «доступна на EL0» вимагає права на читання, а не просто виконання. Це призводить до можливого PAN bypass у певних конфігураціях.
- Через це, якщо iOS / XNU дозволяє execute-only user pages (як у деяких JIT або code-cache налаштуваннях), kernel може випадково прочитати їх навіть при увімкненому PAN. Це відома тонка вразливість в деяких ARMv8+ системах.
#### PXN (Privileged eXecute Never)
- **PXN** — біт у таблиці сторінок (у записах leaf або block), який вказує, що сторінка **неприпустима для виконання у привілейованому режимі** (тобто коли виконується код на EL1).
- PXN забороняє kernel (або будь-якому привілейованому коду) переходити і виконувати інструкції зі сторінок користувача навіть якщо контроль буде перенаправлено. Фактично, це заваджає виконанню коду в user-space з привілейованого контексту.
- У поєднанні з PAN це забезпечує:
1. Kernel не може (за замовчуванням) читати або писати дані користувача (PAN)
2. Kernel не може виконувати код користувача (PXN)
- У форматі таблиці сторінок ARMv8, leaf-записи мають біт `PXN` (а також `UXN` для unprivileged execute-never) у полях атрибутів.
Отже, навіть якщо kernel має пошкоджений function pointer, що вказує в пам’ять користувача, і він спробує перейти туди, біт PXN спричинить fault.
#### Memory-permission model & how PAN and PXN map to page table bits
Щоб зрозуміти, як працюють PAN / PXN, потрібно подивитися, як працює переклад і модель дозволів ARM (спрощено):
- Кожен запис сторінки або блоку має поля атрибутів, включаючи **AP[2:1]** для прав доступу (читання/запис, привілейований vs непри привілейований) і біти **UXN / PXN** для обмежень виконання.
- Коли PSTATE.PAN = 1 (увімкнено), апарат накладає змінені семантики: привілейовані доступи до сторінок, позначених як «доступні EL0» (тобто доступні користувачу), забороняються (fault).
- Через зазначений баг, сторінки, позначені лише як виконувані (без права читання), можуть не вважатися «доступними EL0» в деяких реалізаціях, що дозволяє обходити PAN.
- Коли у сторінки встановлено біт PXN, навіть якщо вибірка інструкцій відбувається з вищого рівня привілеїв, виконання заборонене.
#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)
У дизайні hardened kernel (наприклад, те, що може використовувати Apple):
- Kernel включає PAN за замовчуванням (щоб обмежити привілейований код).
- У шляхах, де потрібно легітимно читати або писати буфери користувача (наприклад, syscall buffer copy, I/O, read/write user pointer), kernel тимчасово **відключає PAN** або використовує спеціальні інструкції, щоб обійти його.
- Після завершення доступу до даних користувача, потрібно знову увімкнути PAN.
- PXN реалізується через таблиці сторінок: сторінки користувача мають PXN = 1 (щоб kernel не міг їх виконувати), сторінки kernel не мають PXN (щоб kernel-код міг виконуватись).
- Kernel повинен гарантувати, що жодні шляхи виконання не призведуть до виконання коду у областях пам’яті користувача (це б обійшло PXN) — тому експлойтні ланцюги, що спираються на «jump into user-controlled shellcode», блокуються.
Через виявлений PAN bypass через execute-only сторінки, у реальній системі Apple може відключити або заборонити execute-only user pages, або запатчити цю специфікаційну слабкість.
#### Attack surfaces, bypasses, and mitigations
- **PAN bypass via execute-only pages**: як обговорювалося, специфікація дозволяє прогалину: сторінки користувача з execute-only (без права читання) можуть не враховуватись як «доступні на EL0», тому PAN не блокує читання ядром на деяких реалізаціях. Це дає атакуючому нетиповий шлях для передачі даних через execute-only секції.
- **Temporal window exploit**: якщо kernel вимикає PAN на період довший за необхідний, гонка або зловмисний шлях може використати це вікно для небажаного доступу до пам’яті користувача.
- **Forgotten re-enable**: якщо код не включає PAN назад, наступні операції ядра можуть неправильно звертатися до пам’яті користувача.
- **Misconfiguration of PXN**: якщо таблиці сторінок не встановлюють PXN на сторінках користувача або неправильно відображають сторінки коду користувача, kernel може бути обманений і виконувати user-space код.
- **Speculation / side-channels**: аналогічно до speculative bypasses, можуть існувати мікроархітектурні побічні ефекти, що призводять до транзиторного порушення перевірок PAN / PXN (хоча такі атаки сильно залежать від дизайну CPU).
- **Complex interactions**: у більш складних сценаріях (наприклад JIT, shared memory, just-in-time code regions) kernel може потребувати тонкого контролю, щоб дозволити певні доступи або виконання в user-mapped областях; проектування цього безпечно під обмеженнями PAN/PXN нетривіальне.
#### Example
<details>
<summary>Приклад коду</summary>
Here are illustrative pseudo-assembly sequences showing enabling/disabling PAN around user memory access, and how a fault might occur.
</details>
// Suppose kernel entry point, PAN is enabled (privileged code cannot access user memory by default)
; Kernel receives a syscall with user pointer in X0 ; wants to read an integer from user space mov X1, X0 ; X1 = user pointer
; disable PAN to allow privileged access to user memory MSR PSTATE.PAN, #0 ; clear PAN bit, disabling the restriction
ldr W2, [X1] ; now allowed load from user address
; re-enable PAN before doing other kernel logic MSR PSTATE.PAN, #1 ; set PAN
; … further kernel work …
; Later, suppose an exploit corrupts a pointer to a user-space code page and jumps there BR X3 ; branch to X3 (which points into user memory)
; Because the target page is marked PXN = 1 for privileged execution, ; the CPU throws an exception (fault) and rejects execution
Якщо kernel had **not** set PXN на тій user page, то перехід може успішно виконатися — що було б insecure.
Якщо kernel забуде повторно увімкнути PAN після доступу до user memory, це відкриває вікно, в якому подальша kernel логіка може випадково read/write довільну user memory.
Якщо user pointer вказує на execute-only page (user page з тільки execute дозволом, без read/write), за PAN spec bug, `ldr W2, [X1]` може **not** fault навіть із PAN enabled, що дозволяє bypass exploit, залежно від implementation.
</details>
<details>
<summary>Example</summary>
A kernel vulnerability намагається взяти user-provided function pointer і викликати його в kernel context (тобто `call user_buffer`). Під PAN/PXN така операція заборонена або призводить до fault.
</details>
---
### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI означає, що top byte (найбільш значущий байт) 64-bit pointer ігнорується під час address translation. Це дозволяє OS або hardware вбудовувати **tag bits** у верхній байт pointer без зміни фактичної адреси.
- TBI означає **Top Byte Ignore** (іноді називають *Address Tagging*). Це hardware feature (доступний у багатьох ARMv8+ implementaціях), який **ігнорує top 8 bits** (bits 63:56) 64-bit pointer під час виконання **address translation / load/store / instruction fetch**.
- Фактично CPU трактує pointer `0xTTxxxx_xxxx_xxxx` (де `TT` = top byte) як `0x00xxxx_xxxx_xxxx` для цілей address translation, ігноруючи (маскуючи) top byte. Top byte може використовуватися software для зберігання **metadata / tag bits**.
- Це дає software «безкоштовний» in-band простір для вбудовування одного байта tag у кожен pointer без зміни того, на яку memory location він посилається.
- Архітектура гарантує, що loads, stores і instruction fetch трактують pointer із замаскованим top byte (тобто tag видалено) перед фактичним доступом до memory.
Отже, TBI розділяє **logical pointer** (pointer + tag) від **physical address**, яка використовується для memory operations.
#### Why TBI: Use cases and motivation
- **Pointer tagging / metadata**: Можна зберігати додаткову metadata (наприклад object type, version, bounds, integrity tags) у верхньому байті. Коли пізніше використовують pointer, tag ігнорується на рівні hardware, тож не потрібно вручну видаляти його для memory access.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI є базовим hardware механізмом, на якому будується MTE. В ARMv8.5, **Memory Tagging Extension** використовує bits 59:56 pointer як **logical tag** і перевіряє його проти **allocation tag**, збереженого в memory.
- **Enhanced security & integrity**: Поєднуючи TBI з pointer authentication (PAC) або runtime checks, можна вимагати коректності не лише значення pointer, але й tag. Атакуючий, який перепише pointer без правильного tag, отримає некоректний tag.
- **Compatibility**: Оскільки TBI опціональний і tag bits ігноруються hardware, існуючий нетегований код працює як раніше. Tag bits фактично стають «неважливими» для legacy коду.
#### Example
<details>
<summary>Example</summary>
A function pointer містив tag у верхньому байті (наприклад `0xAA`). Exploit переписує нижні біти pointer, але пропускає tag, тому коли kernel верифікує або санітує, pointer не проходить перевірку або відкидається.
</details>
---
### 12. **Page Protection Layer (PPL)**
**Introduced in late iOS / modern hardware (iOS ~17 / Apple silicon / high-end models)** (some reports show PPL circa macOS / Apple silicon, but Apple is bringing analogous protections to iOS)
- PPL спроектовано як **intra-kernel protection boundary**: навіть якщо kernel (EL1) скомпрометовано і має read/write можливості, **він не повинен мати вільного доступу** для зміни певних **sensitive pages** (особливо page tables, code-signing metadata, kernel code pages, entitlements, trust caches тощо).
- По суті це створює **“kernel within the kernel”** — менший trusted компонент (PPL) з **elevated privileges**, який тільки він може змінювати захищені сторінки. Інший kernel code має викликати PPL routines для внесення змін.
- Це зменшує attack surface для kernel exploits: навіть при повному arbitrary R/W/execute в kernel mode, exploit має ще якось потрапити в PPL domain (або обійти PPL), щоб змінити критичні структури.
- На нових Apple silicon (A15+ / M2+), Apple переходить до **SPTM (Secure Page Table Monitor)**, який у багатьох випадках замінює PPL для page-table protection на цих платформах.
Ось як, на думку публічного аналізу, PPL ймовірно працює:
#### Use of APRR / permission routing (APRR = Access Permission ReRouting)
- Apple hardware використовує механізм **APRR (Access Permission ReRouting)**, який дозволяє page table entries (PTEs) містити малі індекси замість повних permission bits. Ці індекси відображаються через APRR registers у фактичні permissions. Це дозволяє динамічно ремапити permissions по domain.
- PPL використовує APRR для сегрегації привілеїв у kernel context: тільки PPL domain дозволено оновлювати відображення між індексами і ефективними permissions. Тобто, коли non-PPL kernel code записує PTE або намагається змінити permission bits, APRR logic забороняє це (або накладає read-only mapping).
- PPL code сам запускається в обмеженій області (наприклад `__PPLTEXT`), яка зазвичай non-executable або non-writable до тих пір, поки entry gates тимчасово не дозволять доступ. Kernel викликає PPL entry points (“PPL routines”) для виконання чутливих операцій.
#### Gate / Entry & Exit
- Коли kernel потрібно модифікувати захищену сторінку (наприклад змінити permissions kernel code page або змінити page tables), він викликає **PPL wrapper** routine, яка робить валідацію і потім переходить в PPL domain. Поза цим domain захищені сторінки фактично read-only або non-modifiable для основного kernel.
- Під час PPL entry, APRR mappings налаштовуються так, щоб memory pages у PPL region були **executable & writable** в межах PPL. Після виходу вони повертаються до read-only / non-writable. Це гарантує, що тільки перевірені PPL routines можуть записувати в захищені сторінки.
- Поза PPL спроби kernel code писати в ці захищені сторінки призведуть до fault (permission denied), оскільки APRR mapping для того domain не дозволяє запис.
#### Protected page categories
Сторінки, які PPL зазвичай захищає, включають:
- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, особливо ті, що містять критичну логіку
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Інші high-value kernel structures, де патч дозволив би обійти signature checks або маніпулювати credentials
Ідея в тому, що навіть якщо kernel memory повністю контролюється, атакуючий не зможе просто патчити або переписувати ці сторінки, якщо тільки він не скомпрометує PPL routines або не обійде PPL.
#### Known Bypasses & Vulnerabilities
1. **Project Zero’s PPL bypass (stale TLB trick)**
- Public writeup від Project Zero описує bypass, що використовує **stale TLB entries**.
- Ідея:
1. Allocate two physical pages A and B, mark them as PPL pages (тому вони захищені).
2. Map two virtual addresses P and Q чиї L3 translation table pages походять з A та B.
3. Запустити thread, що постійно звертається до Q, зберігаючи його TLB entry живим.
4. Викликати `pmap_remove_options()` щоб видалити mappings починаючи з P; через баг код помилково видаляє TTEs для обох P і Q, але інвалідовує TLB entry лише для P, залишаючи Q’s stale entry живим.
5. Повторно використати B (page Q’s table) щоб змапити arbitrary memory (наприклад PPL-protected pages). Оскільки stale TLB entry все ще відображає старе мапування Q, це мапування залишається дійсним для того контексту.
6. Через це атакуючий може встановити writable mapping PPL-protected pages без проходження через PPL interface.
- Цей exploit вимагав точного контролю фізичних мапувань і поведінки TLB. Він демонструє, що boundary безпеки, який покладається на коректність TLB / mapping, має бути надзвичайно обережним з інвалідизацією TLB і консистентністю мапувань.
- Project Zero зазначив, що bypass-и такого роду тонкі і рідкісні, але можливі в складних системах. Тим не менше, вони вважають PPL міцною мірою пом’якшення.
2. **Other potential hazards & constraints**
- Якщо kernel exploit може прямо викликати PPL routines (через calling the PPL wrappers), він може обійти обмеження. Тому валідація аргументів критична.
- Bugs у самому PPL code (наприклад arithmetic overflow, boundary checks) можуть дозволити out-of-bounds модифікації всередині PPL. Project Zero помітив, що такий баг у `pmap_remove_options_internal()` був використаний у їхньому bypass.
- PPL boundary невідривно пов’язаний із hardware enforcement (APRR, memory controller), тож його сила залежить від реалізації hardware.
#### Example
<details>
<summary>Code Example</summary>
Here’s a simplified pseudocode / logic showing how a kernel might call into PPL to modify protected pages:
```c
// In kernel (outside PPL domain)
function kernel_modify_pptable(pt_addr, new_entry) {
// validate arguments, etc.
return ppl_call_modify(pt_addr, new_entry) // call PPL wrapper
}
// In PPL (trusted domain)
function ppl_call_modify(pt_addr, new_entry) {
// temporarily enable write access to protected pages (via APRR adjustments)
aprr_set_index_for_write(PPL_INDEX)
// perform the modification
*pt_addr = new_entry
// restore permissions (make pages read-only again)
aprr_restore_default()
return success
}
// If kernel code outside PPL does:
*pt_addr = new_entry // a direct write
// It will fault because APRR mapping for non-PPL domain disallows write to that page
The kernel can do many normal operations, but only through ppl_call_* routines can it change protected mappings or patch code.
Приклад
A kernel exploit tries to overwrite the entitlement table, or disable code-sign enforcement by modifying a kernel signature blob. Because that page is PPL-protected, the write is blocked unless going through the PPL interface. So even with kernel code execution, you cannot bypass code-sign constraints or modify credential data arbitrarily. On iOS 17+ certain devices use SPTM to further isolate PPL-managed pages.PPL → SPTM / Заміни / Майбутнє
- On Apple’s modern SoCs (A15 or later, M2 or later), Apple supports SPTM (Secure Page Table Monitor), which replaces PPL for page table protections.
- Apple calls out in documentation: “Page Protection Layer (PPL) and Secure Page Table Monitor (SPTM) enforce execution of signed and trusted code … PPL manages the page table permission overrides … Secure Page Table Monitor replaces PPL on supported platforms.”
- The SPTM architecture likely shifts more policy enforcement into a higher-privileged monitor outside kernel control, further reducing the trust boundary.
MTE | EMTE | MIE
Here’s a higher-level description of how EMTE operates under Apple’s MIE setup:
- Tag assignment
- When memory is allocated (e.g. in kernel or user space via secure allocators), a secret tag is assigned to that block.
- The pointer returned to the user or kernel includes that tag in its high bits (using TBI / top byte ignore mechanisms).
- Tag checking on access
- Whenever a load or store is executed using a pointer, the hardware checks that the pointer’s tag matches the memory block’s tag (allocation tag). If mismatch, it faults immediately (since synchronous).
- Because it’s synchronous, there is no “delayed detection” window.
- Retagging on free / reuse
- When memory is freed, the allocator changes the block’s tag (so older pointers with old tags no longer match).
- A use-after-free pointer would therefore have a stale tag and mismatch when accessed.
- Neighbor-tag differentiation to catch overflows
- Adjacent allocations are given distinct tags. If a buffer overflow spills into neighbor’s memory, tag mismatch causes a fault.
- This is especially powerful in catching small overflows that cross boundary.
- Tag confidentiality enforcement
- Apple must prevent tag values being leaked (because if attacker learns the tag, they could craft pointers with correct tags).
- They include protections (microarchitectural / speculative controls) to avoid side-channel leakage of tag bits.
- Kernel and user-space integration
- Apple uses EMTE not just in user-space but also in kernel / OS-critical components (to guard kernel against memory corruption).
- The hardware/OS ensures tag rules apply even when kernel is executing on behalf of user space.
Приклад
``` Allocate A = 0x1000, assign tag T1 Allocate B = 0x2000, assign tag T2// pointer P points into A with tag T1 P = (T1 << 56) | 0x1000
// Valid store *(P + offset) = value // tag T1 matches allocation → allowed
// Overflow attempt: P’ = P + size_of_A (into B region) *(P’ + delta) = value → pointer includes tag T1 but memory block has tag T2 → mismatch → fault
// Free A, allocator retags it to T3 free(A)
// Use-after-free: *(P) = value → pointer still has old tag T1, memory region is now T3 → mismatch → fault
</details>
#### Обмеження та виклики
- **Intrablock overflows**: Якщо overflow залишається в межах тієї самої алокації (не переходить межу) і тег залишається тим самим, tag mismatch цього не виявляє.
- **Tag width limitation**: Для тега доступні лише кілька бітів (наприклад, 4 біти, або невеликий домен) — обмежений простір імен.
- **Side-channel leaks**: Якщо біти тега можуть бути втрачені/виведені (через cache / speculative execution), атакуючий може дізнатися валідні теги і обійти захист. Apple’s tag confidentiality enforcement призначений для пом’якшення цього.
- **Performance overhead**: Перевірки тегів при кожному load/store додають витрати; Apple має оптимізувати апаратне забезпечення, щоб знизити overhead.
- **Compatibility & fallback**: На старішому апаратному забезпеченні або в частинах, що не підтримують EMTE, повинен бути fallback. Apple стверджує, що MIE увімкнено лише на пристроях з підтримкою.
- **Complex allocator logic**: Аллокатор має керувати тегами, retagging, вирівнюванням меж і уникати колізій mis-tag. Баги в логіці аллокатора можуть ввести вразливості.
- **Mixed memory / hybrid areas**: Частина пам’яті може залишитися untagged (legacy), що ускладнює взаємодію.
- **Speculative / transient attacks**: Як і з багатьма мікроархітектурними захистами, speculative execution або micro-op fusions можуть тимчасово обійти перевірки або вивести біти тега.
- **Limited to supported regions**: Apple може застосовувати EMTE тільки в вибраних, високоризикових областях (kernel, security-critical subsystems), а не повсюдно.
---
## Ключові покращення / відмінності порівняно зі стандартним MTE
Ось покращення та зміни, на які акцентує Apple:
| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Supports synchronous and asynchronous modes. In async, tag mismatches are reported later (delayed)| Apple insists on **synchronous mode** by default—tag mismatches are caught immediately, no delay/race windows allowed.|
| **Coverage of non-tagged memory** | Accesses to non-tagged memory (e.g. globals) may bypass checks in some implementations | EMTE requires that accesses from a tagged region to non-tagged memory also validate tag knowledge, making it harder to bypass by mixing allocations.|
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Apple adds **Tag Confidentiality Enforcement**, which attempts to prevent leakage of tag values (via speculative side-channels etc.).|
| **Allocator integration & retagging** | MTE leaves much of allocator logic to software | Apple’s secure typed allocators (kalloc_type, xzone malloc, etc.) integrate with EMTE: when memory is allocated or freed, tags are managed at fine granularity.|
| **Always-on by default** | In many platforms, MTE is optional or off by default | Apple enables EMTE / MIE by default on supported hardware (e.g. iPhone 17 / A19) for kernel and many user processes.|
Оскільки Apple контролює як апаратну, так і програмну частини стеку, вона може суворо впроваджувати EMTE, уникати проблем з продуктивністю і закривати бокові канали.
---
## Як EMTE працює на практиці (Apple / MIE)
Нижче — опис високого рівня того, як EMTE працює в налаштуванні MIE від Apple:
1. **Tag assignment**
- Коли пам’ять виділяється (наприклад у kernel або user space через secure allocators), для цього блоку присвоюється **secret tag**.
- Указівник, повернутий користувачу або kernel, включає цей тег у свої високі біти (використовуючи TBI / top byte ignore mechanisms).
2. **Tag checking on access**
- Коли виконується load або store за вказівником, апарат перевіряє, чи збігається тег у вказівнику з тегом блоку пам’яті (allocation tag). Якщо mismatch, відбувається негайний fault (оскільки synchronous).
- Оскільки режим synchronous, немає вікна “delayed detection”.
3. **Retagging on free / reuse**
- Коли пам’ять звільняється, аллокатор змінює тег блоку (щоб старі вказівники зі старими тегами більше не підходили).
- Use-after-free вказівник матиме застарілий тег і при доступі викличе mismatch.
4. **Neighbor-tag differentiation to catch overflows**
- Суміжним алокаціям присвоюють різні теги. Якщо buffer overflow переходить у пам’ять сусіда, tag mismatch викликає fault.
- Це особливо ефективно для виявлення невеликих переповнень, що перетинають межу.
5. **Tag confidentiality enforcement**
- Apple має запобігти витоку значень тегів (бо якщо атакуючий дізнається тег, він зможе сфабрикувати вказівники з коректними тегами).
- Вони включають захисти (мікроархітектурні / speculative controls) щоб уникнути side-channel виведення бітів тега.
6. **Kernel and user-space integration**
- Apple використовує EMTE не лише в user-space, але й у kernel / критичних компонентах OS (щоб захистити kernel від memory corruption).
- Апарат/OS гарантують застосування правил тегування навіть коли kernel виконує операції від імені user space.
Оскільки EMTE вбудовано в MIE, Apple застосовує EMTE в synchronous режимі по ключових поверхнях атаки, а не як опцію або режим налагодження.
---
## Exception handling in XNU
Коли відбувається **exception** (наприклад, `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, тощо), шар **Mach** ядра XNU відповідає за перехоплення її до того, як вона перетвориться на UNIX-стильний **signal** (наприклад, `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).
Цей процес включає кілька шарів propagation та обробки exception перед тим, як дійти до user space або перетворитися на BSD signal.
### Exception Flow (High-Level)
1. **CPU triggers a synchronous exception** (e.g., invalid pointer dereference, PAC failure, illegal instruction, etc.).
2. **Low-level trap handler** runs (`trap.c`, `exception.c` in XNU source).
3. The trap handler calls **`exception_triage()`**, the core of the Mach exception handling.
4. `exception_triage()` decides how to route the exception:
- First to the **thread's exception port**.
- Then to the **task's exception port**.
- Then to the **host's exception port** (often `launchd` or `ReportCrash`).
If none of these ports handle the exception, the kernel may:
- **Convert it into a BSD signal** (for user-space processes).
- **Panic** (for kernel-space exceptions).
### Core Function: `exception_triage()`
The function `exception_triage()` routes Mach exceptions up the chain of possible handlers until one handles it or until it's finally fatal. It's defined in `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);
Типовий потік викликів:
exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()
Якщо всі вони зазнають невдачі → обробляється bsd_exception() → перетворюється на сигнал, наприклад SIGSEGV.
Порти винятків
Кожен об’єкт Mach (thread, task, host) може зареєструвати порти винятків, куди надсилаються повідомлення про винятки.
Вони визначені в API:
task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()
Кожен exception port має:
- A mask (які виключення він хоче отримувати)
- A port name (Mach порт для отримання повідомлень)
- A behavior (як kernel відправляє повідомлення)
- A flavor (який thread state включити)
Debuggers and Exception Handling
A debugger (наприклад, LLDB) встановлює exception port на цільовому task або thread, зазвичай використовуючи task_set_exception_ports().
Коли виникає виключення:
- Mach message відправляється до процесу debugger.
- Debugger може вирішити handle (resume, змінити регістри, пропустити інструкцію) або не handle виключення.
- Якщо debugger не обробляє його, виключення поширюється на наступний рівень (task → host).
Flow of EXC_BAD_ACCESS
-
Потік дереференсує недійсний вказівник → CPU піднімає Data Abort.
-
Kernel trap handler викликає
exception_triage(EXC_BAD_ACCESS, ...). -
Повідомлення надсилається до:
-
Thread port → (debugger може перехопити breakpoint).
-
Якщо debugger ігнорує → Task port → (process-level handler).
-
Якщо ігнорується → Host port (зазвичай ReportCrash).
- Якщо ніхто не обробляє →
bsd_exception()транслює вSIGSEGV.
PAC Exceptions
Коли Pointer Authentication (PAC) не проходить перевірку (невідповідність підпису), піднімається спеціальне Mach виключення:
EXC_ARM_PAC(type)- Codes можуть містити деталі (наприклад, тип ключа, тип вказівника).
Якщо бінар має прапор TFRO_PAC_EXC_FATAL, kernel трактує помилки PAC як фатальні, обходячи перехоплення debugger. Це зроблено, щоб запобігти використанню дебагера зловмисниками для обходу PAC перевірок, і воно увімкнене для platform binaries.
Software Breakpoints
Software breakpoint (int3 на x86, brk на ARM64) реалізований шляхом навмисного викликання fault.
Debugger ловить це через exception port:
- Модифікує instruction pointer або пам’ять.
- Відновлює оригінальну інструкцію.
- Відновлює виконання.
Цей же механізм дозволяє «піймати» PAC exception — якщо тільки TFRO_PAC_EXC_FATAL не встановлений, в такому випадку воно ніколи не досягає debugger.
Conversion to BSD Signals
Якщо жоден обробник не приймає виключення:
-
Kernel викликає
task_exception_notify() → bsd_exception(). -
Це мапує Mach exceptions на сигнали:
| Mach Exception | Signal |
|---|---|
| EXC_BAD_ACCESS | SIGSEGV or SIGBUS |
| EXC_BAD_INSTRUCTION | SIGILL |
| EXC_ARITHMETIC | SIGFPE |
| EXC_SOFTWARE | SIGTRAP |
| EXC_BREAKPOINT | SIGTRAP |
| EXC_CRASH | SIGKILL |
| EXC_ARM_PAC | SIGILL (on non-fatal) |
### Key Files in XNU Source
-
osfmk/kern/exception.c→ Core ofexception_triage(),exception_deliver_*(). -
bsd/kern/kern_sig.c→ Signal delivery logic. -
osfmk/arm64/trap.c→ Low-level trap handlers. -
osfmk/mach/exc.h→ Exception codes and structures. -
osfmk/kern/task.c→ Task exception port setup.
Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)
Kernel використовував zone allocator (kalloc), розбитий на фіксовані за розміром “zones”.
Кожна zone зберігала тільки алокації одного size class.
Зі скріншоту:
| Zone Name | Element Size | Example Use |
|---|---|---|
default.kalloc.16 | 16 bytes | Дуже малі kernel structs, pointers. |
default.kalloc.32 | 32 bytes | Малі структури, заголовки об’єктів. |
default.kalloc.64 | 64 bytes | IPC messages, крихітні kernel buffers. |
default.kalloc.128 | 128 bytes | Середні об’єкти, наприклад частини OSObject. |
| … | … | … |
default.kalloc.1280 | 1280 bytes | Великі структури, IOSurface/graphics metadata. |
Як це працювало:
- Кожен запит на алокацію округлювався вгору до найближчого розміру zone.
(Напр., запит на 50 байт потрапляв у зону
kalloc.64). - Пам’ять у кожній зоні зберігалася у freelist — chunks, звільнені kernel-ом, поверталися в ту зону.
- Якщо ви переповнювали буфер на 64 байти, ви перезаписували наступний об’єкт у тій же зоні.
Саме тому heap spraying / feng shui були такими ефективними: можна було спрогнозувати сусідні об’єкти, засипавши алокаціями одного size class.
The freelist
Всередині кожної kalloc зони звільнені об’єкти не поверталися безпосередньо системі — вони йшли у freelist, зв’язаний список доступних chunks.
-
Коли chunk звільнявся, kernel записував в початок цього chunk вказівник → адресу наступного вільного chunk у тій же зоні.
-
Зона зберігала HEAD вказівник на перший вільний chunk.
-
Алокація завжди використовувала поточний HEAD:
-
Pop HEAD (повернути цю пам’ять викликачеві).
-
Update HEAD = HEAD->next (збережено у заголовку звільненого chunk).
-
Звільнення пушило chunks назад:
-
freed_chunk->next = HEAD -
HEAD = freed_chunk
Отже, freelist був просто зв’язаним списком, побудованим всередині самої звільненої пам’яті.
Normal state:
Zone page (64-byte chunks for example):
[ A ] [ F ] [ F ] [ A ] [ F ] [ A ] [ F ]
Freelist view:
HEAD ──► [ F ] ──► [ F ] ──► [ F ] ──► [ F ] ──► NULL
(next ptrs stored at start of freed chunks)
Експлуатація freelist
Оскільки перші 8 байт free chunk = freelist pointer, зловмисник може його пошкодити:
-
Heap overflow into an adjacent freed chunk → перезаписати його “next” pointer.
-
Use-after-free write into a freed object → перезаписати його “next” pointer.
Потім, при наступному виділенні пам’яті того ж розміру:
-
The allocator pops the corrupted chunk.
-
Follows the attacker-supplied “next” pointer.
-
Повертає вказівник на довільну пам’ять, дозволяючи fake object primitives або targeted overwrite.
Visual example of freelist poisoning:
Before corruption:
HEAD ──► [ F1 ] ──► [ F2 ] ──► [ F3 ] ──► NULL
After attacker overwrite of F1->next:
HEAD ──► [ F1 ]
(next) ──► 0xDEAD_BEEF_CAFE_BABE (attacker-chosen)
Next alloc of this zone → kernel hands out memory at attacker-controlled address.
This freelist design made exploitation highly effective pre-hardening: predictable neighbors from heap sprays, raw pointer freelist links, and no type separation allowed attackers to escalate UAF/overflow bugs into arbitrary kernel memory control.
Heap Grooming / Feng Shui
The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data.
Steps:
- Spray allocations (fill the holes)
- Over time, the kernel heap gets fragmented: some zones have holes where old objects were freed.
- The attacker first makes lots of dummy allocations to fill these gaps, so the heap becomes “packed” and predictable.
- Force new pages
- Once the holes are filled, the next allocations must come from new pages added to the zone.
- Fresh pages mean objects will be clustered together, not scattered across old fragmented memory.
- This gives the attacker much better control of neighbors.
- Place attacker objects
- The attacker now sprays again, creating lots of attacker-controlled objects in those new pages.
- These objects are predictable in size and placement (since they all belong to the same zone).
- Free a controlled object (make a gap)
- The attacker deliberately frees one of their own objects.
- This creates a “hole” in the heap, which the allocator will later reuse for the next allocation of that size.
- Victim object lands in the hole
- The attacker triggers the kernel to allocate the victim object (the one they want to corrupt).
- Since the hole is the first available slot in the freelist, the victim is placed exactly where the attacker freed their object.
- Overflow / UAF into victim
- Now the attacker has attacker-controlled objects around the victim.
- By overflowing from one of their own objects (or reusing a freed one), they can reliably overwrite the victim’s memory fields with chosen values.
Why it works:
- Zone allocator predictability: allocations of the same size always come from the same zone.
- Freelist behavior: new allocations reuse the most recently freed chunk first.
- Heap sprays: attacker fills memory with predictable content and controls layout.
- End result: attacker controls where the victim object lands and what data sits next to it.
Modern Kernel Heap (iOS 15+/A12+ SoCs)
Apple hardened the allocator and made heap grooming much harder:
1. From Classic kalloc to kalloc_type
- Before: a single
kalloc.<size>zone existed for each size class (16, 32, 64, … 1280, etc.). Any object of that size was placed there → attacker objects could sit next to privileged kernel objects. - Now:
- Kernel objects are allocated from typed zones (
kalloc_type). - Each type of object (e.g.,
ipc_port_t,task_t,OSString,OSData) has its own dedicated zone, even if they’re the same size. - The mapping between object type ↔ zone is generated from the kalloc_type system at compile time.
An attacker can no longer guarantee that controlled data (OSData) ends up adjacent to sensitive kernel objects (task_t) of the same size.
2. Slabs and Per-CPU Caches
- The heap is divided into slabs (pages of memory carved into fixed-size chunks for that zone).
- Each zone has a per-CPU cache to reduce contention.
- Allocation path:
- Try per-CPU cache.
- If empty, pull from the global freelist.
- If freelist is empty, allocate a new slab (one or more pages).
- Benefit: This decentralization makes heap sprays less deterministic, since allocations may be satisfied from different CPUs’ caches.
3. Randomization inside zones
- Within a zone, freed elements are not handed back in simple FIFO/LIFO order.
- Modern XNU uses encoded freelist pointers (safe-linking like Linux, introduced ~iOS 14).
- Each freelist pointer is XOR-encoded with a per-zone secret cookie.
- This prevents attackers from forging a fake freelist pointer if they gain a write primitive.
- Some allocations are randomized in their placement within a slab, so spraying doesn’t guarantee adjacency.
4. Guarded Allocations
- Certain critical kernel objects (e.g., credentials, task structures) are allocated in guarded zones.
- These zones insert guard pages (unmapped memory) between slabs or use redzones around objects.
- Any overflow into the guard page triggers a fault → immediate panic instead of silent corruption.
5. Page Protection Layer (PPL) and SPTM
- Even if you control a freed object, you can’t modify all of kernel memory:
- PPL (Page Protection Layer) enforces that certain regions (e.g., code signing data, entitlements) are read-only even to the kernel itself.
- On A15/M2+ devices, this role is replaced/enhanced by SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
- These hardware-enforced layers mean attackers can’t escalate from a single heap corruption to arbitrary patching of critical security structures.
- (Added / Enhanced): also, PAC (Pointer Authentication Codes) is used in the kernel to protect pointers (especially function pointers, vtables) so that forging or corrupting them becomes harder.
- (Added / Enhanced): zones may enforce zone_require / zone enforcement, i.e. that an object freed can only be returned through its correct typed zone; invalid cross-zone frees may panic or be rejected. (Apple alludes to this in their memory safety posts)
6. Large Allocations
- Not all allocations go through
kalloc_type. - Very large requests (above ~16 KB) bypass typed zones and are served directly from kernel VM (kmem) via page allocations.
- These are less predictable, but also less exploitable, since they don’t share slabs with other objects.
7. Allocation Patterns Attackers Target
Even with these protections, attackers still look for:
- Reference count objects: if you can tamper with retain/release counters, you may cause use-after-free.
- Objects with function pointers (vtables): corrupting one still yields control flow.
- Shared memory objects (IOSurface, Mach ports): these are still attack targets because they bridge user ↔ kernel.
But — unlike before — you can’t just spray OSData and expect it to neighbor a task_t. You need type-specific bugs or info leaks to succeed.
Example: Allocation Flow in Modern Heap
Suppose userspace calls into IOKit to allocate an OSData object:
- Type lookup →
OSDatamaps tokalloc_type_osdatazone (size 64 bytes). - Check per-CPU cache for free elements.
- If found → return one.
- If empty → go to global freelist.
- If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes).
- Return chunk to caller.
Freelist pointer protection:
- Each freed chunk stores the address of the next free chunk, but encoded with a secret key.
- Overwriting that field with attacker data won’t work unless you know the key.
Comparison Table
| Feature | Old Heap (Pre-iOS 15) | Modern Heap (iOS 15+ / A12+) |
|---|---|---|
| Allocation granularity | Fixed size buckets (kalloc.16, kalloc.32, etc.) | Size + type-based buckets (kalloc_type) |
| Placement predictability | High (same-size objects side by side) | Low (same-type grouping + randomness) |
| Freelist management | Raw pointers in freed chunks (easy to corrupt) | Encoded pointers (safe-linking style) |
| Adjacent object control | Easy via sprays/frees (feng shui predictable) | Hard — typed zones separate attacker objects |
| Kernel data/code protections | Few hardware protections | PPL / SPTM protect page tables & code pages, and PAC protects pointers |
| Allocation reuse validation | None (freelist pointers raw) | zone_require / zone enforcement |
| Exploit reliability | High with heap sprays | Much lower, requires logic bugs or info leaks |
| Large allocations handling | All small allocations managed equally | Large ones bypass zones → handled via VM |
Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)
In recent Apple OS versions (especially iOS 17+), Apple introduced a more secure userland allocator, xzone malloc (XZM). This is the user-space analog to the kernel’s kalloc_type, applying type awareness, metadata isolation, and memory tagging safeguards.
Goals & Design Principles
- Type segregation / type awareness: group allocations by type or usage (pointer vs data) to prevent type confusion and cross-type reuse.
- Metadata isolation: separate heap metadata (e.g. free lists, size/state bits) from object payloads so that out-of-bounds writes are less likely to corrupt metadata.
- Guard pages / redzones: insert unmapped pages or padding around allocations to catch overflows.
- Memory tagging (EMTE / MIE): work in conjunction with hardware tagging to detect use-after-free, out-of-bounds, and invalid accesses.
- Scalable performance: maintain low overhead, avoid excessive fragmentation, and support many allocations per second with low latency.
Architecture & Components
Below are the main elements in the xzone allocator:
Segment Groups & Zones
- Segment groups partition the address space by usage categories: e.g.
data,pointer_xzones,data_large,pointer_large. - Each segment group contains segments (VM ranges) that host allocations for that category.
- Associated with each segment is a metadata slab (separate VM area) that stores metadata (e.g. free/used bits, size/classes) for that segment. This out-of-line (OOL) metadata ensures that metadata is not intermingled with object payloads, mitigating corruption from overflows.
- Segments are carved into chunks (slices) which in turn are subdivided into blocks (allocation units). A chunk is tied to a specific size class and segment group (i.e. all blocks in a chunk share the same size & category).
- For small / medium allocations, it will use fixed-size chunks; for large/huges, it may map separately.
Chunks & Blocks
- A chunk is a region (often several pages) dedicated to allocations of one size class within a group.
- Inside a chunk, blocks are slots available for allocations. Freed blocks are tracked via the metadata slab — e.g. via bitmaps or free lists stored out-of-line.
- Between chunks (or within), guard slices / guard pages may be inserted (e.g. unmapped slices) to catch out-of-bounds writes.
Type / Type ID
- Every allocation site (or call to malloc, calloc, etc.) is associated with a type identifier (a
malloc_type_id_t) which encodes what kind of object is being allocated. That type ID is passed to the allocator, which uses it to select which zone / segment to serve the allocation. - Because of this, even if two allocations have the same size, they may go into entirely different zones if their types differ.
- In early iOS 17 versions, not all APIs (e.g. CFAllocator) were fully type-aware; Apple addressed some of those weaknesses in iOS 18.
Allocation & Freeing Workflow
Here is a high-level flow of how allocation and deallocation operate in xzone:
- malloc / calloc / realloc / typed alloc is invoked with a size and type ID.
- The allocator uses the type ID to pick the correct segment group / zone.
- Within that zone/segment, it seeks a chunk that has free blocks of the requested size.
- It may consult local caches / per-thread pools or free block lists from metadata.
- If no free block is available, it may allocate a new chunk in that zone.
- The metadata slab is updated (free bit cleared, bookkeeping).
- If memory tagging (EMTE) is in play, the returned block gets a tag assigned, and metadata is updated to reflect its “live” state.
- When
free()is called:
- The block is marked as freed in metadata (via OOL slab).
- The block may be placed into a free list or pooled for reuse.
- Optionally, block contents may be cleared or poisoned to reduce data leaks or use-after-free exploitation.
- The hardware tag associated with the block may be invalidated or re-tagged.
- If an entire chunk becomes free (all blocks freed), the allocator may reclaim that chunk (unmap it or return to OS) under memory pressure.
Security Features & Hardening
These are the defenses built into modern userland xzone:
| Feature | Purpose | Notes |
|---|---|---|
| Metadata decoupling | Prevent overflow from corrupting metadata | Metadata lives in separate VM region (metadata slab) |
| Guard pages / unmapped slices | Catch out-of-bounds writes | Helps detect buffer overflows rather than silently corrupting adjacent blocks |
| Type-based segregation | Prevent cross-type reuse & type confusion | Even same-size allocations from different types go to different zones |
| Memory Tagging (EMTE / MIE) | Detect invalid access, stale references, OOB, UAF | xzone works in concert with hardware EMTE in synchronous mode (“Memory Integrity Enforcement”) |
| Delayed reuse / poisoning / zap | Reduce chance of use-after-free exploitation | Freed blocks may be poisoned, zeroed, or quarantined before reuse |
| Chunk reclamation / dynamic unmapping | Reduce memory waste and fragmentation | Entire chunks may be unmapped when unused |
| Randomization / placement variation | Prevent deterministic adjacency | Blocks in a chunk and chunk selection may have randomized aspects |
| Segregation of “data-only” allocations | Separate allocations that don’t store pointers | Reduces attacker control over metadata or control fields |
Interaction with Memory Integrity Enforcement (MIE / EMTE)
- Apple’s MIE (Memory Integrity Enforcement) is the hardware + OS framework that brings Enhanced Memory Tagging Extension (EMTE) into always-on, synchronous mode across major attack surfaces.
- xzone allocator is a fundamental foundation of MIE in user space: allocations done via xzone get tags, and accesses are checked by hardware.
- In MIE, the allocator, tag assignment, metadata management, and tag confidentiality enforcement are integrated to ensure that memory errors (e.g. stale reads, OOB, UAF) are caught immediately, not exploited later.
- If you like, I can also generate a cheat-sheet or diagram of xzone internals for your book. Do you want me to do that next?
- :contentReference[oai:20]{index=20}
(Old) Physical Use-After-Free via IOSurface
Ghidra Install BinDiff
Download BinDiff DMG from https://www.zynamics.com/bindiff/manual and install it.
Open Ghidra with ghidraRun and go to File –> Install Extensions, press the add button and select the path /Applications/BinDiff/Extra/Ghidra/BinExport and click OK and isntall it even if there is a version mismatch.
Using BinDiff with Kernel versions
- Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be
.ipswfiles. - Decompress until you get the bin format of the kernelcache of both
.ipswfiles. You have information on how to do this on:
macOS Kernel Extensions & Kernelcache
- Open Ghidra with
ghidraRun, create a new project and load the kernelcaches. - Open each kernelcache so they are automatically analyzed by Ghidra.
- Then, on the project Window of Ghidra, right click each kernelcache, select
Export, select formatBinary BinExport (v2) for BinDiffand export them. - Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache.
Finding the right XNU version
If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).
For example, the versions 15.1 RC, 15.1 and 15.1.1 use the version Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006.
JSKit-Based Safari Chains and PREYHUNTER Stagers
Renderer RCE abstraction with JSKit
- Reusable entry: Recent in-the-wild chains abused a WebKit JIT bug (patched as CVE-2023-41993) purely to gain JavaScript-level arbitrary read/write. The exploit immediately pivots into a purchased framework called JSKit, so any future Safari bug only needs to deliver the same primitive.
- Version abstraction & PAC bypasses: JSKit bundles support for a wide range of iOS releases together with multiple, selectable Pointer Authentication Code bypass modules. The framework fingerprints the target build, selects the appropriate PAC bypass logic, and verifies every step (primitive validation, shellcode launch) before progressing.
- Manual Mach-O mapping: JSKit parses Mach-O headers directly from memory, resolves the symbols it needs inside dyld-cached images, and can manually map additional Mach-O payloads without writing them to disk. This keeps the renderer process in-memory only and evades code-signature checks tied to filesystem artifacts.
- Portfolio model: Debug strings such as “exploit number 7” show that the suppliers maintain multiple interchangeable WebKit exploits. Once the JS primitive matches JSKit’s interface, the rest of the chain is unchanged across campaigns.
Kernel bridge: IPC UAF -> code-sign bypass pattern
- Kernel IPC UAF (CVE-2023-41992): The second stage, still running inside the Safari context, triggers a kernel use-after-free in IPC code, re-allocates the freed object from userland, and abuses the dangling pointers to pivot into arbitrary kernel read/write. The stage also reuses PAC bypass material previously computed by JSKit instead of re-deriving it.
- Code-signing bypass (CVE-2023-41991): With kernel R/W available, the exploit patches the trust cache / code-signing structures so unsigned payloads execute as
system. The stage then exposes a lightweight kernel R/W service to later payloads. - Composed pattern: This chain demonstrates a reusable recipe that defenders should expect going forward:
WebKit renderer RCE -> kernel IPC UAF -> kernel arbitrary R/W -> code-sign bypass -> unsigned system stager
PREYHUNTER helper & watcher модулі
-
Watcher anti-analysis: Окремий виконуваний файл watcher постійно профілює пристрій і припиняє kill-chain, коли виявляється дослідницьке середовище. Він перевіряє
security.mac.amfi.developer_mode_status, наявність консольногоdiagnosticd, локаліUSабоIL, сліди jailbreak, такі як Cydia, процеси на кшталтbash,tcpdump,frida,sshdабоcheckrain, мобільні AV-додатки (McAfee, AvastMobileSecurity, NortonMobileSecurity), користувацькі HTTP proxy налаштування та кастомні root CAs. Невдала будь-яка перевірка блокує подальшу доставку payload. -
Helper surveillance hooks: Компонент helper спілкується з іншими стадіями через
/tmp/helper.sock, після чого завантажує набори хуків під іменами DMHooker та UMHooker. Ці хуки прослуховують VOIP аудіошляхи (записи зберігаються під/private/var/tmp/l/voip_%lu_%u_PART.m4a), реалізують системний keylogger, роблять фото без UI та хукають SpringBoard, щоб приглушити сповіщення, які зазвичай би зʼявилися від цих дій. Тож helper діє як прихований шар валідації та легкого спостереження перед розгортанням важчих імплантів, таких як Predator. -
HiddenDot indicator suppression in SpringBoard: За допомогою інʼєкції коду на рівні ядра, Predator хукує
SBSensorActivityDataProvider._handleNewDomainData:(точка агрегації активності сенсорів). Хук обнуляє Objective-Cselfвказівник (x0), тож виклик стає[nil _handleNewDomainData:newData], викидаючи оновлення камери/мікрофона та пригнічуючи як зелений, так і помаранчевий індикатори. -
Mach exception-based hooking flow (DMHooker): Хуки реалізовані через
EXC_BREAKPOINT+ exception ports, після чогоthread_set_stateмутує регістри і виконання відновлюється. Код повернення2означає «продовжити з модифікованим станом потоку». -
PAC-aware redirection for camera access checks: У
mediaserverdpattern-scan (наприклад,memmem) знаходить приватну рутину поблизуFigVideoCaptureSourceCreateWithSourceInfoвсерединіCMCapture.framework. Хук повертає3, щоб перенаправити з використанням попередньо підписаної PAC кешованої адреси повернення, задовольняючи PAC та обходячи перевірку. -
VoIP capture pipeline in
mediaserverd: ХукуєAudioConverterNewтаAudioConverterConvertComplexBuffer+52, щоб захопити буфери, вивести sample rate з розмірів буферів, конвертувати float32 PCM → int16 з NEON, звести 4-канальний до стерео та зберегти черезExtAudioFileWrite(). Сам модуль VoIP не пригнічує індикатори, тому оператори повинні окремо вмикати HiddenDot.
WebKit DFG Store-Barrier UAF + ANGLE PBO OOB (iOS 26.1)
Webkit Dfg Store Barrier Uaf Angle Oob
iMessage/Media Parser Zero-Click Chains
Imessage Media Parser Zero Click Coreaudio Pac Bypass
References
- https://www.jamf.com/blog/predator-spyware-ios-recording-indicator-bypass-analysis/
- Google Threat Intelligence – Intellexa zero-day exploits continue
Tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримайте HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на github.


