iOS Exploiting

Tip

Lernen & ĂŒben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & ĂŒben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & ĂŒben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

UnterstĂŒtzen Sie HackTricks

iOS Exploit Mitigations

1. Code Signing / Runtime Signature Verification

Introduced early (iPhone OS → iOS) Dies ist eines der grundlegenden Schutzmechanismen: jeglicher ausfĂŒhrbarer Code (Apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) muss kryptographisch signiert sein durch eine Zertifikatskette, die in Apples Trust verwurzelt ist. Zur Laufzeit prĂŒft das System vor dem Laden eines Binaries in den Speicher (oder bevor SprĂŒnge ĂŒber bestimmte Grenzen erfolgen) dessen Signatur. Wenn der Code verĂ€ndert wurde (Bit-Flips, Patches) oder unsigniert ist, schlĂ€gt das Laden fehl.

  • Verhindert: die “klassische payload drop + execute”-Phase in Exploit-Ketten; arbitrary code injection; das Modifizieren eines existierenden Binaries, um bösartige Logik einzufĂŒgen.
  • Mechanismus-Detail:
  • Der Mach-O loader (und dynamic linker) prĂŒft Code-Pages, Segmente, Entitlements, Team IDs und dass die Signatur den Inhalt der Datei abdeckt.
  • FĂŒr Speicherregionen wie JIT Caches oder dynamisch generierten Code erzwingt Apple, dass Pages signiert sind oder ĂŒber spezielle APIs validiert werden (z. B. mprotect mit Code-Sign-Checks).
  • Die Signatur enthĂ€lt Entitlements und Identifier; das OS stellt sicher, dass bestimmte APIs oder privilegierte FĂ€higkeiten spezifische Entitlements erfordern, die nicht gefĂ€lscht werden können.
Example Angenommen, ein Exploit erzielt Code-AusfĂŒhrung in einem Prozess und versucht, Shellcode in einen Heap zu schreiben und dorthin zu springen. Auf iOS mĂŒsste diese Page sowohl als executable markiert sein **und** die Code-Signatur-Anforderungen erfĂŒllen. Da der Shellcode nicht mit Apples Zertifikat signiert ist, schlĂ€gt der Sprung fehl oder das System verweigert, diese Speicherregion ausfĂŒhrbar zu machen.

2. CoreTrust

Introduced around iOS 14+ era (or gradually in newer devices / later iOS) CoreTrust ist das Subsystem, das die runtime signature validation von Binaries (inklusive System- und User-Binaries) gegen Apples root certificate durchfĂŒhrt anstatt sich auf gecachte userland trust stores zu verlassen.

  • Verhindert: post-install Tampering an Binaries, Jailbreaking-Techniken, die versuchen, System-Bibliotheken oder User-Apps zu tauschen oder zu patchen; das TĂ€uschen des Systems durch Ersetzen vertrauenswĂŒrdiger Binaries mit bösartigen GegenstĂŒcken.
  • Mechanismus-Detail:
  • Anstatt einer lokalen Trust-Datenbank oder eines Zertifikat-Caches zu vertrauen, bezieht sich CoreTrust direkt auf Apples Root oder verifiziert Intermediate-Zertifikate in einer sicheren Kette.
  • Es stellt sicher, dass Modifikationen (z. B. im Filesystem) an existierenden Binaries entdeckt und abgewiesen werden.
  • Es verbindet Entitlements, Team IDs, Code-Signing-Flags und andere Metadaten mit dem Binary zur Ladezeit.
Example Ein Jailbreak könnte versuchen, `SpringBoard` oder `libsystem` durch eine gepatchte Version zu ersetzen, um Persistenz zu erlangen. Wenn aber der OS-Loader oder CoreTrust prĂŒft, bemerkt er die Signaturabweichung (oder verĂ€nderte Entitlements) und verweigert die AusfĂŒhrung.

3. Data Execution Prevention (DEP / NX / W^X)

Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time DEP erzwingt, dass als schreibbar markierte Pages (fĂŒr Daten) nicht-executable sind und als ausfĂŒhrbar markierte Pages nicht-schreibbar sind. Man kann nicht einfach Shellcode in Heap- oder Stack-Regionen schreiben und ausfĂŒhren.

  • Verhindert: direkte Shellcode-AusfĂŒhrung; klassische buffer-overflow → Sprung zu injiziertem Shellcode.
  • Mechanismus-Detail:
  • Die MMU / Memory-Protection-Flags (via Page Tables) erzwingen die Trennung.
  • Jeglicher Versuch, eine schreibbare Page ausfĂŒhrbar zu machen, löst eine SystemprĂŒfung aus (und ist entweder verboten oder erfordert Code-Sign-Freigaben).
  • In vielen FĂ€llen erfordert das AusfĂŒhrbar-Machen von Pages das Durchlaufen von OS-APIs, die zusĂ€tzliche EinschrĂ€nkungen oder PrĂŒfungen durchsetzen.
Example Ein Overflow schreibt Shellcode auf den Heap. Der Angreifer versucht `mprotect(heap_addr, size, PROT_EXEC)`, um ihn ausfĂŒhrbar zu machen. Das System verweigert dies jedoch oder validiert, dass die neue Page Code-Sign-Bedingungen erfĂŒllen muss (was der Shellcode nicht kann).

4. Address Space Layout Randomization (ASLR)

Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) ASLR randomisiert die Basisadressen wichtiger Speicherregionen: libraries, heap, stack, etc., bei jedem Prozessstart. Gadget-Adressen verschieben sich zwischen Runs.

  • Verhindert: das harte Kodieren von Gadget-Adressen fĂŒr ROP/JOP; statische Exploit-Ketten; blindes Springen zu bekannten Offsets.
  • Mechanismus-Detail:
  • Jede geladene Library / jedes dynamische Modul wird bei einem randomisierten Offset rebased.
  • Stack- und Heap-Basiszeiger werden (innerhalb gewisser Entropiegrenzen) randomisiert.
  • Manchmal werden auch andere Regionen (z. B. mmap-Allocations) randomisiert.
  • In Kombination mit information-leak Mitigations zwingt es den Angreifer, zuerst eine Adresse oder einen Pointer zu leak-en, um Basisadressen zur Laufzeit zu bestimmen.
Example Eine ROP-Kette erwartet ein Gadget bei `0x
.lib + offset`. Da `lib` aber bei jedem Lauf anders relociert wird, schlÀgt die hartkodierte Kette fehl. Ein Exploit muss zuerst die Basisadresse des Moduls leak-en, bevor er Gadget-Adressen berechnen kann.

5. Kernel Address Space Layout Randomization (KASLR)

Introduced in iOS ~ (iOS 5 / iOS 6 timeframe) Analog zu User-ASLR randomisiert KASLR die Basis der kernel text und anderer Kernel-Strukturen beim Booten.

  • Verhindert: Kernel-Level Exploits, die sich auf fixe Orte von Kernel-Code oder -Daten verlassen; statische Kernel-Exploits.
  • Mechanismus-Detail:
  • Bei jedem Boot wird die Kernel-Basisadresse (innerhalb eines Bereichs) randomisiert.
  • Kernel-Datenstrukturen (wie task_structs, vm_map etc.) können ebenfalls verschoben oder offsettet werden.
  • Angreifer mĂŒssen zuerst Kernel-Pointer leak-en oder information-disclosure Vulnerabilities nutzen, um Offsets zu berechnen, bevor sie Kernel-Strukturen oder -Code kapern.
Example Eine lokale Vulnerability zielt darauf ab, einen Kernel-Funktionspointer zu korrupten (z. B. in einer `vtable`) bei `KERN_BASE + offset`. Da `KERN_BASE` unbekannt ist, muss der Angreifer ihn zuerst leak-en (z. B. via eine read-Primitive), bevor er die korrekte Adresse fĂŒr die Korruption berechnen kann.

6. Kernel Patch Protection (KPP / AMCC)

Introduced in newer iOS / A-series hardware (post around iOS 15–16 era or newer chips) KPP (auch AMCC genannt) ĂŒberwacht kontinuierlich die IntegritĂ€t der Kernel-Text-Pages (via Hash oder Checksum). Erkennt es Manipulationen (Patches, Inline-Hooks, Code-Modifikationen) außerhalb erlaubter Fenster, löst es einen Kernel-Panic oder Reboot aus.

  • Verhindert: persistentes Kernel-Patching (Modifizieren von Kernel-Instruktionen), Inline-Hooks, statische FunktionsĂŒberschreibungen.
  • Mechanismus-Detail:
  • Ein Hardware- oder Firmware-Modul ĂŒberwacht den Kernel-Textbereich.
  • Es re-hasht die Pages periodisch oder bei Bedarf und vergleicht die Werte mit erwarteten Daten.
  • Wenn Abweichungen außerhalb harmloser Update-Fenster auftreten, panict es das GerĂ€t (um persistente bösartige Patches zu vermeiden).
  • Angreifer mĂŒssen entweder Detektionsfenster umgehen oder legitime Patch-Pfade verwenden.
Example Ein Exploit versucht, den Prolog einer Kernel-Funktion (z. B. `memcmp`) zu patchen, um Aufrufe abzufangen. KPP bemerkt jedoch, dass der Hash der Code-Page nicht mehr mit dem erwarteten Wert ĂŒbereinstimmt und löst einen Kernel-Panic aus, wodurch das GerĂ€t abstĂŒrzt, bevor der Patch stabil wird.

7. Kernel Text Read‐Only Region (KTRR)

Introduced in modern SoCs (post ~A12 / newer hardware) KTRR ist ein hardware-erzwungener Mechanismus: sobald der Kernel-Text frĂŒh wĂ€hrend des Boots gesperrt wird, ist er von EL1 (dem Kernel) her schreibgeschĂŒtzt und verhindert weitere Schreibzugriffe auf Code-Pages.

  • Verhindert: alle Modifikationen am Kernel-Code nach dem Boot (z. B. Patching, In-Place-Code-Injection) auf EL1-Privilege-Level.
  • Mechanismus-Detail:
  • WĂ€hrend des Boots (im secure/bootloader-Stadium) markiert der Memory Controller (oder eine secure Hardware-Einheit) die physikalischen Pages, die Kernel-Text enthalten, als read-only.
  • Selbst wenn ein Exploit volle Kernel-Privilegien erlangt, kann er diese Pages nicht schreiben, um Instruktionen zu patchen.
  • Um sie zu verĂ€ndern, mĂŒsste der Angreifer zuerst die Boot-Chain kompromittieren oder KTRR selbst unterlaufen.
Example Ein Privilege-Escalation-Exploit springt in EL1 und schreibt ein Trampolin in eine Kernel-Funktion (z. B. im `syscall`-Handler). Weil die Pages jedoch durch KTRR als read-only gesperrt sind, schlÀgt der Schreibversuch fehl (oder löst einen Fault aus), sodass Patches nicht angewendet werden können.

8. Pointer Authentication Codes (PAC)

Introduced with ARMv8.3 (hardware), Apple beginning with A12 / iOS ~12+

  • PAC ist ein Hardware-Feature, eingefĂŒhrt in ARMv8.3-A, um Manipulationen an Pointer-Werten (Return-Adressen, Funktionspointer, bestimmte Datenpointers) zu erkennen, indem eine kleine kryptographische Signatur (eine “MAC”) in ungenutzte hohe Bits des Pointers eingebettet wird.
  • Die Signatur (“PAC”) wird ĂŒber den Pointer-Wert plus einen modifier (einen Kontextwert, z. B. Stack Pointer oder anderes Unterscheidungsmerkmal) berechnet. Dadurch erhĂ€lt derselbe Pointer-Wert in unterschiedlichen Kontexten eine andere PAC.
  • Beim Gebrauch prĂŒft eine authenticate-Instruktion die PAC. Ist sie gĂŒltig, wird die PAC entfernt und der reine Pointer verwendet; ist sie ungĂŒltig, wird der Pointer “poisoned” (oder ein Fault ausgelöst).
  • Die Keys, die zur Erzeugung/Validierung von PACs verwendet werden, liegen in privilegierten Registern (EL1, Kernel) und sind aus dem User-Mode nicht direkt lesbar.
  • Weil nicht alle 64 Bits eines Pointers in vielen Systemen genutzt werden (z. B. 48-bit Adressraum), können die oberen Bits “frei” sein und die PAC aufnehmen, ohne die effektive Adresse zu verĂ€ndern.

Architectural Basis & Key Types

  • ARMv8.3 fĂŒhrt fĂŒnf 128-bit Keys ein (jeweils implementiert ĂŒber zwei 64-bit System-Register) fĂŒr Pointer Authentication.

  • APIAKey — fĂŒr instruction pointers (Domain “I”, Key A)

  • APIBKey — zweiter instruction pointer Key (Domain “I”, Key B)

  • APDAKey — fĂŒr data pointers (Domain “D”, Key A)

  • APDBKey — fĂŒr data pointers (Domain “D”, Key B)

  • APGAKey — “generic” Key, zum Signieren von non-pointer Daten oder anderen generischen Zwecken

  • Diese Keys werden in privilegierten System-Register gespeichert (nur auf EL1/EL2 etc. zugreifbar), nicht vom User-Mode aus zugĂ€nglich.

  • Die PAC wird via einer kryptographischen Funktion berechnet (ARM schlĂ€gt QARMA als Algorithmus vor) unter Verwendung von:

  1. Dem Pointer-Wert (kanonischer Teil)
  2. Einem modifier (ein Kontextwert, wie ein Salt)
  3. Dem geheimen Key
  4. Einiger interner Tweak-Logik Wenn die resultierende PAC mit den im oberen Bits des Pointers gespeicherten Werten ĂŒbereinstimmt, gelingt die Authentifizierung.

Instruction Families

Die Namenskonvention ist: PAC / AUT / XPAC, gefolgt von Domain-Buchstaben.

  • PACxx Instruktionen signieren einen Pointer und fĂŒgen eine PAC ein
  • AUTxx Instruktionen authentifizieren + strippen (validieren und die PAC entfernen)
  • XPACxx Instruktionen strippen ohne Validierung

Domains / Suffixe:

MnemonicMeaning / DomainKey / DomainExample Usage in Assembly
PACIASign instruction pointer with APIAKey“I, A”PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1
PACIBSign instruction pointer with APIBKey“I, B”PACIB X2, X3
PACDASign data pointer with APDAKey“D, A”PACDA X4, X5
PACDBSign data pointer with APDBKey“D, B”PACDB X6, X7
PACG / PACGAGeneric (non-pointer) signing with APGAKey“G”PACGA X8, X9, X10 (sign X9 with modifier X10 into X8)
AUTIAAuthenticate APIA-signed instruction pointer & strip PAC“I, A”AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip
AUTIBAuthenticate APIB domain“I, B”AUTIB X2, X3
AUTDAAuthenticate APDA-signed data pointer“D, A”AUTDA X4, X5
AUTDBAuthenticate APDB-signed data pointer“D, B”AUTDB X6, X7
AUTGAAuthenticate generic / blob (APGA)“G”AUTGA X8, X9, X10 (validate generic)
XPACIStrip PAC (instruction pointer, no validation)“I”XPACI X0 — remove PAC from X0 (instruction domain)
XPACDStrip PAC (data pointer, no validation)“D”XPACD X4 — remove PAC from data pointer in X4

Es gibt spezialisierte / Alias-Formen:

  • PACIASP ist Kurzform fĂŒr PACIA X30, SP (sign the link register using SP as modifier)
  • AUTIASP ist AUTIA X30, SP (authenticate link register with SP)
  • Kombinierte Formen wie RETAA, RETAB (authenticate-and-return) oder BLRAA (authenticate & branch) existieren in ARM-Erweiterungen / Compiler-UnterstĂŒtzung.
  • Auch Zero-Modifier-Varianten: PACIZA / PACIZB, bei denen der modifier implizit null ist, etc.

Modifiers

Das Hauptziel des modifiers ist, die PAC an einen spezifischen Kontext zu binden, sodass derselbe adressierte Wert in verschiedenen Kontexten unterschiedliche PACs erhĂ€lt. Das ist wie das HinzufĂŒgen eines Salts zu einem Hash.

Daher:

  • Der modifier ist ein Kontextwert (ein anderes Register), der in die PAC-Berechnung mit einfließt. Typische Auswahl: der Stack Pointer (SP), ein Frame Pointer oder eine Objekt-ID.
  • Die Verwendung von SP als modifier ist ĂŒblich fĂŒr Return-Address-Signing: die PAC ist an den spezifischen Stack-Frame gebunden. Versucht man, LR in einem anderen Frame wiederzuverwenden, Ă€ndert sich der modifier, sodass die PAC-Validierung fehlschlĂ€gt.
  • Derselbe Pointer-Wert, der unter verschiedenen modifiers signiert wurde, ergibt unterschiedliche PACs.
  • Der modifier muss nicht geheim sein, idealerweise ist er aber nicht vom Angreifer kontrollierbar.
  • FĂŒr Instruktionen, die Pointer signieren oder verifizieren, wo kein sinnvoller modifier existiert, verwenden manche Formen null oder eine implizite Konstante.

Apple / iOS / XNU Customizations & Observations

  • Apples PAC-Implementierung enthĂ€lt per-boot diversifiers, sodass Keys oder Tweaks bei jedem Boot wechseln und eine Wiederverwendung ĂŒber Boots hinweg verhindern.
  • Sie enthalten auch cross-domain Mitigations, sodass PACs, die im User-Mode signiert wurden, nicht einfach im Kernel-Mode wiederverwendet werden können, etc.
  • Auf Apple M1 / Apple Silicon zeigte Reverse Engineering, dass es neun modifier-Typen und Apple-spezifische System-Register fĂŒr Key-Control gibt.
  • Apple nutzt PAC in vielen Kernel-Subsystemen: Return-Address-Signing, Pointer-IntegritĂ€t in Kernel-Daten, signierte Thread-Contexts, etc.
  • Google Project Zero demonstrierte, dass man unter einer mĂ€chtigen Memory read/write-Primitive im Kernel Kernel-PACs (fĂŒr A-Keys) auf A12-era GerĂ€ten fĂ€lschen konnte, aber Apple schloss viele dieser Wege.
  • In Apples System sind manche Keys global fĂŒr den Kernel, wĂ€hrend User-Prozesse per-Process Key-Randomness bekommen können.

PAC Bypasses

  1. Kernel-mode PAC: theoretical vs real bypasses
  • Da Kernel-PAC-Keys und -Logik streng kontrolliert sind (privilegierte Register, Diversifiers, Domain-Isolation), ist das FĂ€lschen beliebiger signierter Kernel-Pointer sehr schwierig.
  • Azads 2020er “iOS Kernel PAC, One Year Later” berichtet, dass er in iOS 12–13 einige partielle BypĂ€sse fand (signing gadgets, reuse of signed states, unprotected indirect branches), aber keinen vollstĂ€ndigen generischen Bypass. bazad.github.io
  • Apples “Dark Magic”-Customizations verengen die ausnutzbaren FlĂ€chen weiter (domain switching, per-key enabling bits). i.blackhat.com
  • Es gibt einen bekannten kernel PAC bypass CVE-2023-32424 auf Apple Silicon (M1/M2) gemeldet von Zecao Cai et al. i.blackhat.com
  • Diese BypĂ€sse beruhen jedoch oft auf sehr spezifischen Gadgets oder Implementierungsfehlern; sie sind nicht allgemeingĂŒltige Workarounds.

Deshalb gilt Kernel-PAC als hochgradig robust, wenn auch nicht perfekt.

  1. User-mode / runtime PAC bypass techniques

Diese sind hÀufiger und nutzen Imperfektionen in der Anwendung von PAC im dynamic linking / runtime-Framework. Nachfolgend Klassen mit Beispielen.

2.1 Shared Cache / A key issues

  • Die dyld shared cache ist ein großes vor-verlinktes Blob von System-Frameworks und Libraries. Weil es so weit verbreitet geteilt wird, sind Funktionspointer innerhalb des shared cache bereits signiert und werden von vielen Prozessen benutzt. Angreifer zielen auf diese bereits-signed Pointer als “PAC oracles”.

  • Einige Bypass-Techniken versuchen, A-key signierte Pointer aus dem shared cache zu extrahieren oder wiederzuverwenden und sie in Gadgets zu nutzen.

  • Der Vortrag “No Clicks Required” beschreibt, wie man ein Oracle ĂŒber die shared cache baut, um relative Adressen zu inferieren und das mit signierten Pointern zu kombinieren, um PAC zu umgehen. saelo.github.io

  • Außerdem wurden Importe von Funktionspointer aus shared libraries in Userspace als unzureichend durch PAC geschĂŒtzt gefunden, was einem Angreifer erlaubt, Funktionspointer zu erhalten, ohne deren Signatur zu verĂ€ndern. (Project Zero Bug Entry) bugs.chromium.org

2.2 dlsym(3) / dynamic symbol resolution

  • Ein bekannter Bypass ist, dlsym() aufzurufen, um einen bereits signierten Funktionspointer zu bekommen (mit A-Key, diversifier zero) und diesen dann zu verwenden. Weil dlsym legitim signierte Pointer zurĂŒckgibt, umgeht ihre Verwendung das Fordern, PAC zu fĂ€lschen.

  • Epsilons Blog beschreibt, wie einige BypĂ€sse dies ausnutzen: dlsym("someSym") liefert einen signierten Pointer und kann fĂŒr indirekte Aufrufe verwendet werden. blog.epsilon-sec.com

  • Synacktivs “iOS 18.4 — dlsym considered harmful” beschreibt einen Bug: einige mittels dlsym aufgelöste Symbole in iOS 18.4 geben Pointer zurĂŒck, die falsch signiert sind (oder fehlerhafte diversifiers haben), was unbeabsichtigte PAC-BypĂ€sse ermöglicht. Synacktiv

  • Die Logik in dyld fĂŒr dlsym beinhaltet: wenn result->isCode, signieren sie den zurĂŒckgegebenen Pointer mit __builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), also mit Kontext null. blog.epsilon-sec.com

Damit ist dlsym ein hÀufiger Vektor in User-Mode PAC-BypÀssen.

2.3 Other DYLD / runtime relocations

  • Der DYLD-Loader und die dynamische Relocation-Logik sind komplex und mappten manchmal temporĂ€r Pages als read/write, um Relocations vorzunehmen, und schalteten sie danach wieder auf read-only. Angreifer nutzen diese Zeitfenster aus. Synacktivs Vortrag beschreibt “Operation Triangulation”, einen timing-basierten PAC-Bypass ĂŒber dynamische Relocations. Synacktiv

  • DYLD-Pages sind jetzt mit SPRR / VM_FLAGS_TPRO geschĂŒtzt (einige Protections-Flags fĂŒr dyld). FrĂŒhere Versionen hatten schwĂ€chere Schutzmaßnahmen. Synacktiv

  • In WebKit-Exploit-Ketten ist der DYLD-Loader oft Ziel fĂŒr PAC-BypĂ€sse. Die Slides erwĂ€hnen, dass viele PAC-BypĂ€sse den DYLD-Loader ins Visier genommen haben (via Relocation, Interposer-Hooks). Synacktiv

2.4 NSPredicate / NSExpression / ObjC / SLOP

  • In Userland-Exploit-Ketten werden Objective-C Runtime-Methoden wie NSPredicate, NSExpression oder NSInvocation verwendet, um Control-Calls zu schmuggeln, ohne offensichtliches Pointer-FĂ€lschen.

  • Auf Ă€lteren iOS-Versionen (vor PAC) nutzte ein Exploit fake NSInvocation-Objekte, um arbitrary selectors auf kontrolliertem Speicher aufzurufen. Mit PAC sind Modifikationen nötig. Aber die Technik SLOP (SeLector Oriented Programming) wurde auch unter PAC erweitert. Project Zero

  • Die ursprĂŒngliche SLOP-Technik erlaubte das Verketten von ObjC-Aufrufen durch das Erzeugen gefĂ€lschter invocations; der Bypass beruht darauf, dass ISA- oder Selector-Pointer manchmal nicht vollstĂ€ndig PAC-geschĂŒtzt sind. Project Zero

  • In Umgebungen, in denen Pointer Authentication nur partiell angewendet wird, sind Methoden / Selector- / Target-Pointer nicht immer PAC-geschĂŒtzt, was Raum fĂŒr BypĂ€sse lĂ€sst.

Example Flow

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>Example</summary>
Ein buffer overflow ĂŒberschreibt eine return address auf dem stack. Der attacker schreibt die target gadget address, kann aber den korrekten PAC nicht berechnen. Wenn die Funktion zurĂŒckkehrt, löst die CPU‑Anweisung `AUTIA` einen Fault aus, weil der PAC nicht ĂŒbereinstimmt. Die Kette schlĂ€gt fehl.
Project Zero’s Analyse des A12 (iPhone XS) zeigte, wie Apple PAC verwendet wird und welche Methoden es zum FĂ€lschen von PACs gibt, wenn ein attacker ein memory read/write primitive besitzt.
</details>


### 9. **Branch Target Identification (BTI)**
**EingefĂŒhrt mit ARMv8.5 (neuere Hardware)**
BTI ist ein Hardware‑Feature, das **indirect branch targets** ĂŒberprĂŒft: Beim AusfĂŒhren von `blr` oder indirekten calls/jumps muss das Ziel mit einem **BTI landing pad** (`BTI j` oder `BTI c`) beginnen. Das Springen in gadget addresses, die kein Landing Pad haben, löst eine Exception aus.

LLVM’s Implementation beschreibt drei Varianten von BTI‑Instruktionen und wie sie auf Branch‑Typen abgebildet werden.

| BTI Variant | What it permits (which branch types) | Typical placement / use case |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Targets of *call*-style indirect branches (e.g. `BLR`, or `BR` using X16/X17) | Put at entry of functions that may be called indirectly |
| **BTI J** | Targets of *jump*-style branches (e.g. `BR` used for tail calls) | Placed at the beginning of blocks reachable by jump tables or tail-calls |
| **BTI JC** | Acts as both C and J | Can be targeted by either call or jump branches |

- In Code, die mit branch target enforcement kompiliert wurde, fĂŒgen Compiler eine BTI‑Instruktion (C, J oder JC) an jedem gĂŒltigen indirect‑branch target ein (FunktionsanfĂ€nge oder Blocks, die per Jump erreichbar sind), sodass indirekte Branches nur zu diesen Stellen erfolgreich sind.
- **Direct branches / calls** (d.h. fixe Adressen `B`, `BL`) werden **nicht** von BTI eingeschrĂ€nkt. Die Annahme ist, dass Code‑Pages vertrauenswĂŒrdig sind und ein Angreifer sie nicht verĂ€ndern kann (daher sind direkte Branches sicher).
- Außerdem sind **RET / return**‑Instruktionen im Allgemeinen nicht durch BTI eingeschrĂ€nkt, da return‑Adressen via PAC oder return signing Mechanismen geschĂŒtzt sind.

#### Mechanism and enforcement

- Wenn die CPU einen **indirect branch (BLR / BR)** dekodiert, der sich in einer als „guarded / BTI‑enabled“ markierten Page befindet, prĂŒft sie, ob die erste Instruktion am Ziel ein gĂŒltiges BTI (C, J oder JC, je nach erlaubt) ist. Falls nicht, tritt eine **Branch Target Exception** auf.
- Die BTI‑Instruktionscodierung wurde so entworfen, dass sie Opcodes wiederverwendet, die zuvor fĂŒr NOPs reserviert waren (in Ă€lteren ARM‑Versionen). Daher bleiben BTI‑aktivierte Binaries abwĂ€rtskompatibel: Auf Hardware ohne BTI‑Support wirken diese Instruktionen wie NOPs.
- Die Compiler‑Passes, die BTIs einfĂŒgen, tun dies nur dort, wo es nötig ist: Funktionen, die indirekt aufgerufen werden können, oder Basic Blocks, die per Jump erreichbar sind.
- Einige Patches und LLVM‑Code zeigen, dass BTI nicht fĂŒr *alle* Basic Blocks eingefĂŒgt wird — nur fĂŒr jene, die potenzielle branch targets sind (z. B. aus switch / jump tables).

#### BTI + PAC synergy

PAC schĂŒtzt den Pointer‑Wert (die Quelle) — stellt sicher, dass die Kette von indirekten Aufrufen / Returns nicht manipuliert wurde.

BTI stellt sicher, dass selbst ein gĂŒltiger Pointer nur auf korrekt markierte Entry‑Points zeigen darf.

Kombiniert benötigt ein Angreifer sowohl einen gĂŒltigen Pointer mit korrektem PAC als auch ein Ziel, das dort ein BTI hat. Dadurch wird es deutlich schwieriger, brauchbare Exploit‑Gadgets zusammenzustellen.

#### Example


<details>
<summary>Example</summary>
Ein Exploit versucht, auf ein gadget bei `0xABCDEF` zu pivotieren, das nicht mit `BTI c` beginnt. Die CPU prĂŒft beim AusfĂŒhren von `blr x0` das Ziel und löst einen Fault aus, weil die Instruktionsausrichtung kein gĂŒltiges Landing Pad enthĂ€lt. Viele Gadgets werden dadurch unbrauchbar, sofern sie nicht mit einem BTI‑Prefix versehen sind.
</details>


### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**EingefĂŒhrt in neueren ARMv8‑Extensions / iOS‑Support (fĂŒr gehĂ€rteten kernel)**

#### PAN (Privileged Access Never)

- **PAN** ist ein Feature, eingefĂŒhrt in **ARMv8.1‑A**, das verhindert, dass **privileged code** (EL1 oder EL2) **lesen oder schreiben** auf Speicher zugreift, der als **user‑accessible (EL0)** markiert ist, sofern PAN nicht explizit deaktiviert wird.
- Die Idee: Selbst wenn der kernel manipuliert oder kompromittiert wird, kann er nicht beliebig user‑space Pointer dereferenzieren, ohne zuvor PAN zu *clearen*, wodurch das Risiko von **ret2usr**‑artigen Exploits oder Missbrauch von user‑kontrollierten Buffern reduziert wird.
- Wenn PAN aktiviert ist (PSTATE.PAN = 1), fĂŒhrt jede privilegierte load/store‑Instruktion, die auf eine virtuelle Adresse zugreift, die „accessible at EL0“ ist, zu einem **permission fault**.
- Der kernel, wenn er legitimerweise auf user‑space memory zugreifen muss (z. B. Daten von/zu user Buffern kopieren), muss PAN **vorĂŒbergehend deaktivieren** (oder „unprivileged load/store“ Instruktionen verwenden), um diesen Zugriff zu erlauben.
- In Linux auf ARM64 wurde PAN etwa 2015 eingefĂŒhrt: Kernel‑Patches fĂŒgten Feature‑Erkennung hinzu und ersetzten `get_user` / `put_user` etc. durch Varianten, die PAN rund um user memory accesses clearen.

**Wichtige Nuance / Limitierung / Bug**
- Wie von Siguza und anderen bemerkt, fĂŒhrt ein Spezifikationsfehler (oder eine mehrdeutige Verhalten) in ARMs Design dazu, dass **execute‑only user mappings** (`--x`) PAN **nicht auslösen** können. Anders gesagt: Wenn eine user‑Seite ausfĂŒhrbar, aber ohne Lese‑Permission ist, kann der kernel‑Leseversuch PAN möglicherweise umgehen, weil die Architektur „accessible at EL0“ als lesbar definiert, nicht nur ausfĂŒhrbar. Das fĂŒhrt in manchen Implementierungen zu einem PAN‑Bypass.
- Deshalb, falls iOS / XNU execute‑only user pages erlauben (wie in einigen JIT oder code‑cache Setups), könnte der kernel trotz aktiviertem PAN versehentlich von diesen Seiten lesen. Das ist ein bekanntes, subtiles, in bestimmten ARMv8+ Systemen ausnutzbares Problem.

#### PXN (Privileged eXecute Never)

- **PXN** ist ein Page‑Table‑Flag (in den PTEs, leaf oder block entries), das angibt, dass die Page **nicht ausfĂŒhrbar im privileged mode** ist (d.h. wenn EL1 sie ausfĂŒhrt).
- PXN verhindert, dass der kernel (oder jeglicher privileged code) in user‑space Pages springt oder Instruktionen daraus ausfĂŒhrt, selbst wenn die Kontrolle umgeleitet wird. Effektiv verhindert es kernel‑level Control‑Flow‑Redirections in user memory.
- Kombiniert mit PAN stellt das sicher:
1. Kernel kann standardmĂ€ĂŸig nicht user‑space Daten lesen oder schreiben (PAN)
2. Kernel kann nicht user‑space Code ausfĂŒhren (PXN)
- Im ARMv8 Page‑Table‑Format haben leaf entries ein `PXN` Bit (sowie `UXN` fĂŒr unprivileged execute‑never) in ihren Attributbits.

Auch wenn der kernel einen korrupten Funktionszeiger hat, der auf user memory zeigt und dorthin zu springen versucht, wĂŒrde das PXN‑Bit einen Fault verursachen.

#### Memory‑permission model & wie PAN und PXN zu Page‑Table Bits abgebildet werden

Um zu verstehen, wie PAN / PXN funktionieren, muss man sehen, wie ARMs Translation‑ und Permission‑Modell arbeitet (vereinfacht):

- Jede Page‑ oder Block‑Entry hat Attributfelder, einschließlich **AP[2:1]** fĂŒr Access‑Permissions (read/write, privileged vs unprivileged) und **UXN / PXN** Bits fĂŒr execute‑never EinschrĂ€nkungen.
- Wenn PSTATE.PAN = 1 (aktiviert), erzwingt die Hardware geĂ€nderte Semantiken: privilegierte Zugriffe auf Seiten, die als „accessible by EL0“ markiert sind (d.h. user‑accessible), werden disallowed (Fault).
- Wegen des erwĂ€hnten Bugs könnten Seiten, die nur ausfĂŒhrbar markiert sind (keine Leseberechtigung), unter bestimmten Implementierungen nicht als „accessible by EL0“ zĂ€hlen und damit PAN umgehen.
- Wenn eine Page das PXN‑Bit gesetzt hat, ist selbst bei Instruktionsfetches aus höherer Privilegstufe die AusfĂŒhrung verboten.

#### Kernel‑Nutzung von PAN / PXN in einem gehĂ€rteten OS (z.B. iOS / XNU)

In einem gehĂ€rteten kernel‑Design (wie Apple es verwenden könnte):

- Der kernel aktiviert PAN standardmĂ€ĂŸig (so dass privileged code eingeschrĂ€nkt ist).
- In Pfaden, die legitimerweise user‑Buffer lesen oder schreiben mĂŒssen (z. B. syscall Buffer‑Copy, I/O, read/write user pointer), deaktiviert der kernel PAN vorĂŒbergehend oder verwendet spezielle Instruktionen, um den Zugriff zu erlauben.
- Nach Abschluss des user‑Datenzugriffs muss PAN wieder aktiviert werden.
- PXN wird ĂŒber Page‑Tables durchgesetzt: user‑Pages haben PXN = 1 (sodass kernel sie nicht ausfĂŒhren kann), kernel‑Pages haben PXN nicht gesetzt (sodass kernel Code ausfĂŒhren kann).
- Der kernel muss sicherstellen, dass keine Code‑Pfade zur Execution in user memory Regionen fĂŒhren (das wĂŒrde PXN umgehen) — Exploit‑Ketten, die auf „jump into user‑controlled shellcode“ setzen, werden dadurch blockiert.

Aufgrund des beschriebenen PAN‑Bypass durch execute‑only Pages könnte ein reales System Apple dazu bringen, execute‑only user Pages zu deaktivieren oder die SpezifikationsschwĂ€che anderweitig zu patchen.

#### Attack surfaces, bypasses, and mitigations

- **PAN bypass via execute‑only pages**: wie besprochen erlaubt die Spezifikation eine LĂŒcke: user pages mit execute‑only (ohne read perm) könnten unter bestimmten Implementierungen nicht als „accessible at EL0“ zĂ€hlen, sodass PAN kernel Reads von solchen Pages nicht blockiert. Das gibt einem Angreifer einen ungewöhnlichen Pfad, Daten ĂŒber „execute‑only“ Abschnitte einzuschleusen.
- **Temporal window exploit**: wenn der kernel PAN fĂŒr ein grĂ¶ĂŸeres Zeitfenster deaktiviert als nötig, könnte ein Race oder ein bösartiger Pfad dieses Fenster ausnutzen, um ungewollte user memory Zugriffe durchzufĂŒhren.
- **Forgotten re‑enable**: werden Pfade vergessen, PAN wieder zu aktivieren, können nachfolgende kernel‑Operationen fĂ€lschlicherweise user memory zugreifen.
- **Misconfiguration of PXN**: wenn Page‑Tables PXN auf user Pages nicht setzen oder user code Pages falsch mappen, kann der kernel dazu gebracht werden, user‑space Code auszufĂŒhren.
- **Speculation / side‑channels**: Ă€hnlich zu spekulativen Bypasses können mikroarchitektonische Seiteneffekte transient PAN / PXN Checks verletzen (obwohl solche Angriffe stark von CPU‑Design abhĂ€ngig sind).
- **Complex interactions**: In fortgeschrittenen Features (z. B. JIT, shared memory, just‑in‑time code regions) benötigt der kernel feingranulare Kontrolle, um bestimmte memory accesses oder AusfĂŒhrung in user‑gemappten Regionen zu erlauben; diese sicher unter PAN/PXN zu designen ist nicht trivial.

#### Example

<details>
<summary>Code Example</summary>
Here are illustrative pseudo-assembly sequences showing enabling/disabling PAN around user memory access, and how a fault might occur.

// 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

Wenn der Kernel PXN auf dieser user page **nicht** gesetzt hĂ€tte, könnte der branch erfolgreich sein — was unsicher wĂ€re.

Wenn der Kernel vergisst, PAN nach dem Zugriff auf user memory wieder zu aktivieren, entsteht ein Zeitfenster, in dem weitere Kernel-Logik versehentlich beliebiges user memory lesen oder schreiben könnte.

Wenn der user pointer in eine execute-only page zeigt (user page mit nur execute-Berechtigung, kein read/write), könnte unter dem PAN-Spec-Bug `ldr W2, [X1]` **nicht** faulten, selbst bei aktiviertem PAN, was je nach Implementierung einen Bypass-Exploit ermöglichen wĂŒrde.

</details>

<details>
<summary>Example</summary>
A kernel vulnerability tries to take a user-provided function pointer and call it in kernel context (i.e. `call user_buffer`). Under PAN/PXN, that operation is disallowed or faults.
</details>

---

### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI means the top byte (most-significant byte) of a 64-bit pointer is ignored by address translation. This lets OS or hardware embed **tag bits** in the pointer’s top byte without affecting the actual address.

- TBI stands for **Top Byte Ignore** (sometimes called *Address Tagging*). It is a hardware feature (available in many ARMv8+ implementations) that **ignores the top 8 bits** (bits 63:56) of a 64-bit pointer when performing **address translation / load/store / instruction fetch**.
- In effect, the CPU treats a pointer `0xTTxxxx_xxxx_xxxx` (where `TT` = top byte) as `0x00xxxx_xxxx_xxxx` for the purposes of address translation, ignoring (masking off) the top byte. The top byte can be used by software to store **metadata / tag bits**.
- This gives software “free” in-band space to embed a byte of tag in each pointer without altering which memory location it refers to.
- The architecture ensures that loads, stores, and instruction fetch treat the pointer with its top byte masked (i.e. tag stripped off) before performing the actual memory access.

Thus TBI decouples the **logical pointer** (pointer + tag) from the **physical address** used for memory operations.

#### Why TBI: Use cases and motivation

- **Pointer tagging / metadata**: You can store extra metadata (e.g. object type, version, bounds, integrity tags) in that top byte. When you later use the pointer, the tag is ignored at hardware level, so you don’t need to strip manually for the memory access.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI is the base hardware mechanism that MTE builds on. In ARMv8.5, the **Memory Tagging Extension** uses bits 59:56 of the pointer as a **logical tag** and checks it against an **allocation tag** stored in memory.
- **Enhanced security & integrity**: By combining TBI with pointer authentication (PAC) or runtime checks, you can force not just the pointer value but also the tag to be correct. An attacker overwriting a pointer without the correct tag will produce a mismatched tag.
- **Compatibility**: Because TBI is optional and tag bits are ignored by hardware, existing untagged code continues to operate normally. The tag bits effectively become “don’t care” bits for legacy code.

#### Example
<details>
<summary>Example</summary>
A function pointer included a tag in its top byte (say `0xAA`). An exploit overwrites the pointer low bits but neglects the tag, so when the kernel verifies or sanitizes, the pointer fails or is rejected.
</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 is designed as an **intra-kernel protection boundary**: even if the kernel (EL1) is compromised and has read/write capabilities, **it should not be able to freely modify** certain **sensitive pages** (especially page tables, code-signing metadata, kernel code pages, entitlements, trust caches, etc.).
- It effectively creates a **“kernel within the kernel”** — a smaller trusted component (PPL) with **elevated privileges** that alone can modify protected pages. Other kernel code must call into PPL routines to effect changes.
- This reduces the attack surface for kernel exploits: even with full arbitrary R/W/execute in kernel mode, exploit code must also somehow get into the PPL domain (or bypass PPL) to modify critical structures.
- On newer Apple silicon (A15+ / M2+), Apple is transitioning to **SPTM (Secure Page Table Monitor)**, which in many cases replaces PPL for page-table protection on those platforms.

Here’s how PPL is believed to operate, based on public analysis:

#### Use of APRR / permission routing (APRR = Access Permission ReRouting)

- Apple hardware uses a mechanism called **APRR (Access Permission ReRouting)**, which allows page table entries (PTEs) to contain small indices, rather than full permission bits. Those indices are mapped via APRR registers to actual permissions. This allows dynamic remapping of permissions per domain.
- PPL leverages APRR to segregate privilege within kernel context: only the PPL domain is permitted to update the mapping between indices and effective permissions. That is, when non-PPL kernel code writes a PTE or tries to flip permission bits, the APRR logic disallows it (or enforces read-only mapping).
- PPL code itself runs in a restricted region (e.g. `__PPLTEXT`) which is normally non-executable or non-writable until entry gates temporarily allow it. The kernel calls PPL entry points (“PPL routines”) to perform sensitive operations.

#### Gate / Entry & Exit

- When the kernel needs to modify a protected page (e.g. change permissions of a kernel code page, or modify page tables), it calls into a **PPL wrapper** routine, which does validation and then transitions into the PPL domain. Outside that domain, the protected pages are effectively read-only or non-modifiable by the main kernel.
- During PPL entry, the APRR mappings are adjusted so that memory pages in the PPL region are set to **executable & writable** within PPL. Upon exit, they are returned to read-only / non-writable. This ensures that only well-audited PPL routines can write to protected pages.
- Outside PPL, attempts by kernel code to write to those protected pages will fault (permission denied) because the APRR mapping for that code domain doesn’t permit writing.

#### Protected page categories

The pages that PPL typically protects include:

- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, especially those containing critical logic
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Other high-value kernel structures where a patch would allow bypassing signature checks or credentials manipulation

The idea is that even if the kernel memory is fully controlled, the attacker cannot simply patch or rewrite these pages, unless they also compromise PPL routines or bypass PPL.


#### Known Bypasses & Vulnerabilities

1. **Project Zero’s PPL bypass (stale TLB trick)**

- A public writeup by Project Zero describes a bypass involving **stale TLB entries**.
- The idea:

1. Allocate two physical pages A and B, mark them as PPL pages (so they are protected).
2. Map two virtual addresses P and Q whose L3 translation table pages come from A and B.
3. Spin a thread to continuously access Q, keeping its TLB entry alive.
4. Call `pmap_remove_options()` to remove mappings starting at P; due to a bug, the code mistakenly removes the TTEs for both P and Q, but only invalidates the TLB entry for P, leaving Q’s stale entry live.
5. Reuse B (page Q’s table) to map arbitrary memory (e.g. PPL-protected pages). Because the stale TLB entry still maps Q’s old mapping, that mapping remains valid for that context.
6. Through this, the attacker can put writable mapping of PPL-protected pages in place without going through PPL interface.

- This exploit required fine control of physical mapping and TLB behavior. It demonstrates that a security boundary relying on TLB / mapping correctness must be extremely careful about TLB invalidations and mapping consistency.

- Project Zero commented that bypasses like this are subtle and rare, but possible in complex systems. Still, they regard PPL as a solid mitigation.

2. **Other potential hazards & constraints**

- If a kernel exploit can directly enter PPL routines (via calling the PPL wrappers), it might bypass restrictions. Thus argument validation is critical.
- Bugs in the PPL code itself (e.g. arithmetic overflow, boundary checks) can allow out-of-bounds modifications inside PPL. Project Zero observed that such a bug in `pmap_remove_options_internal()` was exploited in their bypass.
- The PPL boundary is irrevocably tied to hardware enforcement (APRR, memory controller), so it's only as strong as the hardware implementation.



#### 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

Der Kernel kann viele normale Operationen ausfĂŒhren, aber nur ĂŒber die ppl_call_*-Routinen kann er geschĂŒtzte Mappings Ă€ndern oder Code patchen.

Beispiel Ein kernel-Exploit versucht, die entitlement table zu ĂŒberschreiben oder die code-sign-Enforcement zu deaktivieren, indem er ein kernel signature blob modifiziert. Da diese Seite PPL-protected ist, wird der Schreibzugriff blockiert, es sei denn, er erfolgt ĂŒber das PPL-Interface. Selbst bei kernel code execution kann man also code-sign-BeschrĂ€nkungen nicht umgehen oder credential-Daten beliebig Ă€ndern. Auf iOS 17+ verwenden bestimmte GerĂ€te SPTM, um PPL-managed pages weiter zu isolieren.

PPL → SPTM / Ersatz / Zukunft

  • Auf Apples modernen SoCs (A15 oder neuer, M2 oder neuer) unterstĂŒtzt Apple SPTM (Secure Page Table Monitor), das PPL fĂŒr den Schutz von Seitentabellen ersetzt.
  • Apple stellt in der Dokumentation fest: “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.”
  • Die SPTM-Architektur verlagert wahrscheinlich mehr Policy-Durchsetzung in einen höher privilegierten Monitor außerhalb der Kernel-Kontrolle und reduziert so die Vertrauensgrenze weiter.

MTE | EMTE | MIE

Hier eine ĂŒbergeordnete Beschreibung, wie EMTE im Rahmen von Apples MIE-Setup funktioniert:

  1. Tag assignment
  • Wenn Speicher zugewiesen wird (z. B. im Kernel oder im user space ĂŒber secure allocators), wird dem Block ein secret tag zugewiesen.
  • Der an den User oder Kernel zurĂŒckgegebene Pointer enthĂ€lt dieses Tag in seinen oberen Bits (unter Verwendung von TBI / top byte ignore Mechanismen).
  1. Tag checking on access
  • Wann immer ein Load oder Store mit einem Pointer ausgefĂŒhrt wird, prĂŒft die Hardware, ob das Tag des Pointers mit dem Tag des Speicherblocks (allocation tag) ĂŒbereinstimmt. Bei NichtĂŒbereinstimmung tritt sofort ein Fault auf (da synchron).
  • Weil es synchron ist, gibt es kein Fenster fĂŒr „verzögerte Erkennung“.
  1. Retagging on free / reuse
  • Wenn Speicher freigegeben wird, Ă€ndert der Allocator das Tag des Blocks (so dass Ă€ltere Pointer mit alten Tags nicht mehr ĂŒbereinstimmen).
  • Ein use-after-free Pointer hĂ€tte daher ein veraltetes Tag und wĂŒrde bei Zugriff nicht ĂŒbereinstimmen.
  1. Neighbor-tag differentiation to catch overflows
  • Benachbarte Allokationen erhalten unterschiedliche Tags. Wenn ein buffer overflow in den Speicher des Nachbarn ĂŒberlĂ€uft, verursacht der Tag-Mismatch einen Fault.
  • Das ist besonders effektiv, um kleine Overflows zu erkennen, die eine Grenze ĂŒberschreiten.
  1. Tag confidentiality enforcement
  • Apple muss verhindern, dass Tag-Werte leaked werden (denn wenn ein Angreifer das Tag kennt, könnte er Pointer mit korrekten Tags erstellen).
  • Sie fĂŒgen Protektionen hinzu (microarchitectural / speculative controls), um side-channel leakage von Tag-Bits zu vermeiden.
  1. Kernel and user-space integration
  • Apple verwendet EMTE nicht nur im user-space, sondern auch in Kernel / OS-kritischen Komponenten (um den Kernel gegen memory corruption zu schĂŒtzen).
  • Hardware/OS stellen sicher, dass die Tag-Regeln gelten, selbst wenn der Kernel im Auftrag des user space ausgefĂŒhrt wird.
Beispiel ``` 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>

#### EinschrÀnkungen & Herausforderungen

- **Intrablock overflows**: Wenn ein Overflow innerhalb derselben Allocation verbleibt (ĂŒberschreitet die Grenze nicht) und das tag gleich bleibt, fĂ€ngt ein tag mismatch das nicht ab.
- **Tag width limitation**: Es stehen nur wenige Bits (z. B. 4 bits, oder eine kleine Domain) fĂŒr Tags zur VerfĂŒgung — begrenzter Namespace.
- **Side-channel leaks**: Falls Tag-Bits geleakt werden können (via cache / speculative execution), kann ein Angreifer gĂŒltige Tags lernen und umgehen. Apples Tag Confidentiality Enforcement soll dem entgegenwirken.
- **Performance overhead**: Tag-Checks bei jedem Load/Store verursachen Kosten; Apple muss die Hardware optimieren, um den Overhead gering zu halten.
- **Compatibility & fallback**: Auf Ă€lterer Hardware oder Teilen, die EMTE nicht unterstĂŒtzen, muss ein Fallback existieren. Apple behauptet, MIE sei nur auf GerĂ€ten mit Support aktiviert.
- **Complex allocator logic**: Der Allocator muss Tags verwalten, retaggen, Grenzen ausrichten und Kollisionen vermeiden. Bugs in der Allocator-Logik könnten neue Verwundbarkeiten einfĂŒhren.
- **Mixed memory / hybrid areas**: Einige Speicherbereiche können untagged bleiben (legacy), was die InteroperabilitÀt erschwert.
- **Speculative / transient attacks**: Wie bei vielen mikroarchitektonischen Protektionen könnten speculative AusfĂŒhrungen oder micro-op Fusions Checks transient umgehen oder Tag-Bits leaken.
- **Limited to supported regions**: Apple könnte EMTE nur in selektiven, hochriskanten Bereichen (Kernel, security-kritische Subsysteme) durchsetzen, nicht flÀchendeckend.



---

## Wichtige Verbesserungen / Unterschiede im Vergleich zu standard MTE

Hier sind die Verbesserungen und Änderungen, die Apple hervorhebt:

| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Supports synchronous and asynchronous modes. In async, tag mismatches are reported later (delayed) | Apple besteht standardmĂ€ĂŸig auf **synchronous mode** — tag mismatches werden sofort erkannt, keine Verzögerungs-/Race-Fenster erlaubt. |
| **Coverage of non-tagged memory** | Accesses to non-tagged memory (e.g. globals) may bypass checks in some implementations | EMTE verlangt, dass Zugriffe von einem tagged Bereich auf non-tagged memory ebenfalls Kenntnisse ĂŒber das tag validieren, was das Umgehen durch Mixing von Allocations erschwert. |
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Apple fĂŒgt **Tag Confidentiality Enforcement** hinzu, das versucht, das Leaken von Tag-Werten zu verhindern (via speculative side-channels etc.). |
| **Allocator integration & retagging** | MTE leaves much of allocator logic to software | Apples secure typed allocators (kalloc_type, xzone malloc, etc.) integrieren sich mit EMTE: Beim Alloc/Free werden Tags fein granular verwaltet. |
| **Always-on by default** | In many platforms, MTE is optional or off by default | Apple aktiviert EMTE / MIE standardmĂ€ĂŸig auf unterstĂŒtzter Hardware (z. B. iPhone 17 / A19) fĂŒr Kernel und viele User-Prozesse. |

Da Apple sowohl Hardware als auch Software-Stack kontrolliert, kann es EMTE streng durchsetzen, Performance-Probleme vermeiden und Side-Channel-LĂŒcken schließen.

---

## Wie EMTE in der Praxis arbeitet (Apple / MIE)

Hier eine höherstufige Beschreibung, wie EMTE unter Apples MIE-Setup arbeitet:

1. **Tag assignment**
- Wenn Speicher alloziert wird (z. B. im Kernel oder User-Space via secure allocators), wird diesem Block ein **secret tag** zugewiesen.
- Der zurĂŒckgegebene Pointer an User oder Kernel enthĂ€lt dieses Tag in seinen High-Bits (unter Verwendung von TBI / top byte ignore mechanisms).

2. **Tag checking on access**
- Wann immer ein Load oder Store mit einem Pointer ausgefĂŒhrt wird, prĂŒft die Hardware, dass das Pointer-Tag mit dem Tag des Speicherblocks (allocation tag) ĂŒbereinstimmt. Bei Mismatch wird sofort ein Fault ausgelöst (da synchronous).
- Weil es synchronous ist, gibt es kein "delayed detection" Window.

3. **Retagging on free / reuse**
- Wenn Speicher freigegeben wird, Ă€ndert der Allocator den Tag des Blocks (Ă€ltere Pointer mit alten Tags stimmen dann nicht mehr ĂŒberein).
- Ein use-after-free Pointer hĂ€tte daher ein veraltetes Tag und wĂŒrde bei Zugriffen mismatchen.

4. **Neighbor-tag differentiation to catch overflows**
- Benachbarte Allocations erhalten unterschiedliche Tags. Wenn ein Buffer Overflow in den Nachbar-Speicher schreibt, fĂŒhrt ein tag mismatch zu einem Fault.
- Das ist besonders effektiv beim Erkennen kleiner Overflows, die Grenzen ĂŒberschreiten.

5. **Tag confidentiality enforcement**
- Apple muss verhindern, dass Tag-Werte geleakt werden (denn wenn ein Angreifer das Tag kennt, könnte er Pointer mit korrekten Tags erzeugen).
- Sie implementieren Schutzmaßnahmen (mikroarchitektonisch / speculative Controls), um Side-Channel-Leaks von Tag-Bits zu vermeiden.

6. **Kernel and user-space integration**
- Apple nutzt EMTE nicht nur im User-Space, sondern auch im Kernel / in OS-kritischen Komponenten (um den Kernel gegen memory corruption zu schĂŒtzen).
- Die Hardware/OS stellen sicher, dass Tag-Regeln auch gelten, wenn der Kernel im Auftrag von User-Space ausgefĂŒhrt wird.

Weil EMTE in MIE eingebettet ist, verwendet Apple EMTE im synchronous mode ĂŒber wichtige AngriffsflĂ€chen hinweg, nicht als opt-in oder Debugging-Mode.



---

## Exception-Handling in XNU

Wenn eine **Exception** auftritt (z. B. `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, etc.), ist die **Mach layer** des XNU-Kernels dafĂŒr verantwortlich, sie abzufangen, bevor sie zu einem UNIX-Ă€hnlichen **Signal** (wie `SIGSEGV`, `SIGBUS`, `SIGILL`, ...) wird.

Dieser Prozess umfasst mehrere Ebenen der Exception-Propagation und -Behandlung, bevor er den User-Space erreicht oder in ein BSD-Signal umgewandelt wird.


### Exception Flow (High-Level)

1.  **CPU triggers a synchronous exception** (z. B. ungĂŒltiger Pointer-Dereferenz, PAC-Failure, illegal instruction, etc.).

2.  **Low-level trap handler** lÀuft (`trap.c`, `exception.c` im XNU-Source).

3.  Der Trap-Handler ruft **`exception_triage()`** auf, den Kern der Mach-Exception-Behandlung.

4.  `exception_triage()` entscheidet, wie die Exception geroutet wird:

-   Zuerst an das **thread's exception port**.

-   Dann an das **task's exception port**.

-   Dann an das **host's exception port** (oft `launchd` oder `ReportCrash`).

Wenn keiner dieser Ports die Exception behandelt, kann der Kernel:

-   **Sie in ein BSD-Signal umwandeln** (fĂŒr User-Space-Prozesse).

-   **Panic** auslösen (fĂŒr Kernel-Space-Exceptions).


### Kernfunktion: `exception_triage()`

Die Funktion `exception_triage()` leitet Mach-Exceptions die Kette möglicher Handler hinauf, bis einer sie behandelt oder bis sie schließlich fatal ist. Sie ist definiert in `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);

Typischer Ablauf:

exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()

Wenn alle fehlschlagen → wird von bsd_exception() behandelt → in ein Signal wie SIGSEGV ĂŒbersetzt.

Exception-Ports

Jedes Mach-Objekt (thread, task, host) kann Exception-Ports registrieren, an die Exception-Nachrichten gesendet werden.

Sie werden durch die API definiert:

task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()

Jeder exception port hat:

  • Eine Maske (welche Exceptions er empfangen möchte)
  • Einen port name (Mach-Port, der Nachrichten empfĂ€ngt)
  • Ein behavior (wie der Kernel die Nachricht sendet)
  • Einen flavor (welchen thread state er einschließt)

Debuggers and Exception Handling

Ein debugger (z. B. LLDB) setzt einen exception port auf die Ziel-task oder den thread, ĂŒblicherweise mittels task_set_exception_ports().

Wenn eine Exception auftritt:

  • Die Mach-Nachricht wird an den debugger-Prozess gesendet.
  • Der debugger kann entscheiden, die Exception zu handlen (resume, Register Ă€ndern, Instruction ĂŒberspringen) oder nicht zu handlen.
  • Wenn der debugger sie nicht handlet, propagiert die Exception zur nĂ€chsten Ebene (task → host).

Flow of EXC_BAD_ACCESS

  1. Thread dereferenziert einen ungĂŒltigen Pointer → CPU löst Data Abort aus.

  2. Kernel trap handler ruft exception_triage(EXC_BAD_ACCESS, ...) auf.

  3. Nachricht wird gesendet an:

  • Thread port → (debugger kann breakpoint abfangen).

  • Wenn debugger ignoriert → Task port → (process-level handler).

  • Wenn ignoriert → Host port (meist ReportCrash).

  1. Wenn niemand handlet → bsd_exception() ĂŒbersetzt das in SIGSEGV.

PAC Exceptions

Wenn Pointer Authentication (PAC) fehlschlÀgt (Signature mismatch), wird eine spezielle Mach exception ausgelöst:

  • EXC_ARM_PAC (Typ)
  • Codes können Details enthalten (z. B. key type, pointer type).

Wenn das Binary das Flag TFRO_PAC_EXC_FATAL gesetzt hat, behandelt der Kernel PAC-Fehler als fatal und umgeht die debugger-Interzeption. Das verhindert, dass Angreifer Debugger benutzen, um PAC-Checks zu umgehen, und ist fĂŒr platform binaries aktiviert.

Software Breakpoints

Ein Software-breakpoint (int3 auf x86, brk auf ARM64) wird durch absichtliches HerbeifĂŒhren eines Faults implementiert.
Der debugger fĂ€ngt das ĂŒber den exception port ab:

  • Modifiziert instruction pointer oder Memory.
  • Stellt die originale Instruction wieder her.
  • Setzt die AusfĂŒhrung fort.

Dieser Mechanismus erlaubt es auch, eine PAC-Exception “abzufangen” — es sei denn TFRO_PAC_EXC_FATAL ist gesetzt, dann erreicht sie nie den debugger.

Conversion to BSD Signals

Wenn kein Handler die Exception akzeptiert:

  • Kernel ruft task_exception_notify() → bsd_exception() auf.

  • Das mappt Mach-Exceptions auf Signale:

Mach ExceptionSignal
EXC_BAD_ACCESSSIGSEGV or SIGBUS
EXC_BAD_INSTRUCTIONSIGILL
EXC_ARITHMETICSIGFPE
EXC_SOFTWARESIGTRAP
EXC_BREAKPOINTSIGTRAP
EXC_CRASHSIGKILL
EXC_ARM_PACSIGILL (on non-fatal)

### Key Files in XNU Source

  • osfmk/kern/exception.c → Kern von exception_triage(), exception_deliver_*().

  • bsd/kern/kern_sig.c → Signal-Delivery-Logik.

  • osfmk/arm64/trap.c → Low-level trap handlers.

  • osfmk/mach/exc.h → Exception-Codes und Strukturen.

  • osfmk/kern/task.c → Setup von Task exception ports.


Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)

Der Kernel verwendete einen zone allocator (kalloc), aufgeteilt in feste “zones” nach GrĂ¶ĂŸe. Jede zone speicherte nur Allocations einer einzelnen size class.

Aus dem Screenshot:

Zone NameElement SizeBeispielverwendung
default.kalloc.1616 bytesSehr kleine Kernel-Strukturen, Pointer.
default.kalloc.3232 bytesKleine Strukturen, Objekt-Header.
default.kalloc.6464 bytesIPC messages, winzige Kernel-Buffer.
default.kalloc.128128 bytesMittelgroße Objekte wie Teile von OSObject.




default.kalloc.12801280 bytesGroße Strukturen, IOSurface/graphics-Metadaten.

Funktionsweise:

  • Jede Allocation wird auf die nĂ€chsthöhere zone-GrĂ¶ĂŸe aufgerundet. (z. B. landet eine 50-Byte-Anfrage in der kalloc.64-zone).
  • Speicher in jeder zone wurde in einer freelist gehalten — von Kernel freigegebene Chunks gingen in diese zone zurĂŒck.
  • Wenn du einen 64-Byte-Buffer overflowst, ĂŒberschreibst du das nĂ€chste Objekt in derselben zone.

Deshalb war heap spraying / feng shui so effektiv: du konntest Nachbarn vorhersagen, indem du Allocations derselben size class gesprayed hast.

The freelist

Innerhalb jeder kalloc-zone wurden freed objects nicht direkt an das System zurĂŒckgegeben — sie kamen in eine freelist, eine verkettete Liste verfĂŒgbarer Chunks.

  • Wenn ein Chunk freigegeben wurde, schrieb der Kernel einen Pointer an den Anfang dieses Chunks → die Adresse des nĂ€chsten freien Chunks in derselben zone.

  • Die zone hielt einen HEAD-Pointer auf das erste freie Chunk.

  • Allocation nutzte immer den aktuellen HEAD:

  1. Pop HEAD (gibt diesen Speicher an den Caller zurĂŒck).

  2. Update HEAD = HEAD->next (gespeichert im Header des freed Chunks).

  • Freeing pusht Chunks zurĂŒck:

  • freed_chunk->next = HEAD

  • HEAD = freed_chunk

Die freelist war also einfach eine verkettete Liste, aufgebaut im freigegebenen Speicher selbst.

Normaler Zustand:

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)

Ausnutzen der freelist

Weil die ersten 8 Bytes eines free chunk = freelist pointer, könnte ein Angreifer diesen korrumpieren:

  1. Heap overflow in einen angrenzenden freed chunk → ĂŒberschreibe dessen “next” pointer.

  2. Use-after-free: Schreibe in ein freed object → ĂŒberschreibe dessen “next” pointer.

Dann, bei der nĂ€chsten Allocation dieser GrĂ¶ĂŸe:

  • Der allocator poppt das korrumpierte chunk.
  • Der allocator folgt dem vom Angreifer bereitgestellten “next” pointer.
  • Gibt einen Pointer auf beliebigen Speicher zurĂŒck, wodurch fake object primitives oder targeted overwrite möglich werden.

Visuelles Beispiel fĂŒr 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 machte Exploitation vor dem Hardening sehr effektiv: predictable neighbors from heap sprays, raw pointer freelist links und keine Typtrennung erlaubten es Angreifern, UAF/overflow-Bugs in die willkĂŒrliche Kontrolle des Kernel-Speichers zu eskalieren.

Heap Grooming / Feng Shui

Das Ziel von heap grooming ist, das heap layout so zu formen, dass, wenn ein Angreifer einen overflow oder use-after-free auslöst, das Zielobjekt (victim) direkt neben einem vom Angreifer kontrollierten Objekt liegt.
Auf diese Weise kann der Angreifer bei einer Speicherkorruption zuverlĂ€ssig das victim-Objekt mit kontrollierten Daten ĂŒberschreiben.

Schritte:

  1. Spray allocations (fill the holes)
  • Im Laufe der Zeit fragmentiert der kernel heap: einige Zonen haben LĂŒcken, in denen alte Objekte freed wurden.
  • Der Angreifer erzeugt zuerst viele Dummy-allocations, um diese LĂŒcken aufzufĂŒllen, sodass der Heap „gepackt“ und vorhersehbar wird.
  1. Force new pages
  • Sobald die LĂŒcken gefĂŒllt sind, mĂŒssen neue Allocations aus neuen Seiten kommen, die der Zone hinzugefĂŒgt werden.
  • Frische Pages bedeuten, dass Objekte zusammengeclustert werden, statt ĂŒber altes fragmentiertes Memory verstreut zu sein.
  • Das gibt dem Angreifer deutlich bessere Kontrolle ĂŒber Nachbarn.
  1. Place attacker objects
  • Der Angreifer sprayt jetzt erneut und erstellt viele vom Angreifer kontrollierte Objekte in diesen neuen Pages.
  • Diese Objekte sind in GrĂ¶ĂŸe und Platzierung vorhersehbar (da sie alle zur selben Zone gehören).
  1. Free a controlled object (make a gap)
  • Der Angreifer freed bewusst eines seiner eigenen Objekte.
  • Das erzeugt ein „Loch“ im Heap, das der Allocator spĂ€ter fĂŒr die nĂ€chste Allocation dieser GrĂ¶ĂŸe wiederverwenden wird.
  1. Victim object lands in the hole
  • Der Angreifer löst das Kernel aus, das victim-Objekt zu allocaten (das Objekt, das er korrumpieren möchte).
  • Da das Loch der erste verfĂŒgbare Slot in der freelist ist, wird das victim genau dort platziert, wo der Angreifer sein Objekt freed hat.
  1. Overflow / UAF into victim
  • Nun hat der Angreifer vom Angreifer-kontrollierte Objekte rund um das victim.
  • Durch ein Overflow aus einem der eigenen Objekte (oder durch Reuse eines freed Objekts) kann er zuverlĂ€ssig die Memory-Felder des victims mit gewĂ€hlten Werten ĂŒberschreiben.

Warum es funktioniert:

  • Zone allocator predictability: Allocations der gleichen GrĂ¶ĂŸe stammen immer aus derselben Zone.
  • Freelist behavior: Neue Allocations reuse den most recently freed Chunk zuerst.
  • Heap sprays: Der Angreifer fĂŒllt Memory mit vorhersehbarem Content und kontrolliert das Layout.
  • Endresultat: Der Angreifer kontrolliert, wo das victim-Objekt landet und welche Daten daneben liegen.

Modern Kernel Heap (iOS 15+/A12+ SoCs)

Apple hat den Allocator gehÀrtet und heap grooming deutlich erschwert:

1. From Classic kalloc to kalloc_type

  • Before: FĂŒr jede GrĂ¶ĂŸenklasse (16, 32, 64, 
 1280, etc.) existierte eine einzelne kalloc.<size> Zone. Jedes Objekt dieser GrĂ¶ĂŸe wurde dort platziert → Angreifer-Objekte konnten neben privilegierten Kernel-Objekten liegen.
  • Now:
  • Kernel-Objekte werden aus typed zones (kalloc_type) alloziert.
  • Jeder Objekttyp (z. B. ipc_port_t, task_t, OSString, OSData) hat seine eigene dedizierte Zone, selbst wenn sie die gleiche GrĂ¶ĂŸe haben.
  • Die Zuordnung zwischen Objekttyp ↔ Zone wird zur Compile-Zeit vom kalloc_type system generiert.

Ein Angreifer kann nicht mehr garantieren, dass kontrollierte Daten (OSData) neben sensitiven Kernel-Objekten (task_t) derselben GrĂ¶ĂŸe landen.

2. Slabs and Per-CPU Caches

  • Der Heap ist in slabs (Pages, in fixed-size Chunks fĂŒr diese Zone) unterteilt.
  • Jede Zone hat einen per-CPU cache, um Contention zu reduzieren.
  • Allocation-Pfad:
  1. Versuche per-CPU cache.
  2. Wenn leer, ziehe aus der global freelist.
  3. Wenn freelist leer, allocate eine neue slab (eine oder mehrere Pages).
  • Vorteil: Diese Dezentralisierung macht heap sprays weniger deterministisch, da Allocations von unterschiedlichen CPUs’ Caches bedient werden können.

3. Randomization inside zones

  • Innerhalb einer Zone werden freed Elemente nicht in einfacher FIFO/LIFO-Reihenfolge zurĂŒckgegeben.
  • Modernes XNU verwendet encoded freelist pointers (safe-linking-Ă€hnlich wie Linux, eingefĂŒhrt ~iOS 14).
  • Jeder freelist pointer ist XOR-encoded mit einem pro-Zone secret cookie.
  • Das verhindert, dass Angreifer einen gefĂ€lschten freelist pointer erstellen, selbst wenn sie eine Schreibprimitive haben.
  • Manche Allocations werden in ihrer Platzierung innerhalb einer slab randomisiert, sodass ein Spray keine Nachbarschaft garantiert.

4. Guarded Allocations

  • Bestimmte kritische Kernel-Objekte (z. B. credentials, task-Strukturen) werden in guarded zones alloziert.
  • Diese Zonen fĂŒgen guard pages (unmapped Memory) zwischen Slabs ein oder verwenden redzones um Objekte.
  • Jedes Overflow in die guard page löst einen Fault aus → sofortiger Panic statt stiller Korruption.

5. Page Protection Layer (PPL) and SPTM

  • Selbst wenn man ein freed Objekt kontrolliert, kann man nicht den gesamten Kernel-Speicher modifizieren:
  • PPL (Page Protection Layer) erzwingt, dass bestimmte Regionen (z. B. code signing data, entitlements) read-only sind, sogar fĂŒr den Kernel selbst.
  • Auf A15/M2+ devices wird diese Rolle durch SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor) ersetzt/ergĂ€nzt.
  • Diese hardware-erzwungenen Layer bedeuten, dass Angreifer nicht von einer einzelnen Heap-Korruption zu einem willkĂŒrlichen Patchen kritischer Sicherheitsstrukturen eskalieren können.
  • (Added / Enhanced): außerdem wird PAC (Pointer Authentication Codes) im Kernel verwendet, um Pointer (insbesondere function pointers, vtables) zu schĂŒtzen, sodass das FĂ€lschen oder Korrumpieren schwieriger wird.
  • (Added / Enhanced): Zonen können zone_require / zone enforcement durchsetzen, d. h. dass ein freed Objekt nur durch seine korrekte typed zone zurĂŒckgegeben werden kann; ungĂŒltige Cross-Zone frees können panics verursachen oder abgelehnt werden. (Apple deutet darauf in ihren Memory-Safety-Posts hin)

6. Large Allocations

  • Nicht alle Allocations laufen ĂŒber kalloc_type.
  • Sehr große Anfragen (oberhalb von ~16 KB) umgehen typed zones und werden direkt aus dem kernel VM (kmem) ĂŒber Page-Allocations bedient.
  • Diese sind weniger vorhersehbar, aber auch weniger ausnutzbar, da sie Slabs nicht mit anderen Objekten teilen.

7. Allocation Patterns Attackers Target

Auch mit diesen Schutzmaßnahmen suchen Angreifer weiterhin nach:

  • Reference count objects: Wenn man retain/release-ZĂ€hler manipulieren kann, kann das zu use-after-free fĂŒhren.
  • Objects with function pointers (vtables): Das Korrumpieren eines solchen Objekts bietet weiterhin Control Flow.
  • Shared memory objects (IOSurface, Mach ports): Diese sind weiterhin Ziele, weil sie User ↔ Kernel ĂŒberbrĂŒcken.

Aber — anders als frĂŒher — kann man nicht einfach OSData sprayen und erwarten, dass es neben einem task_t landet. Man braucht type-spezifische Bugs oder info leaks, um erfolgreich zu sein.

Example: Allocation Flow in Modern Heap

Angenommen, Userspace ruft IOKit auf, um ein OSData-Objekt zu allocaten:

  1. Type lookup → OSData mappt auf die kalloc_type_osdata zone (GrĂ¶ĂŸe 64 bytes).
  2. PrĂŒfe per-CPU cache auf freie Elemente.
  • Falls gefunden → gebe eines zurĂŒck.
  • Falls leer → gehe zur global freelist.
  • Falls freelist leer → allocate eine neue slab (Page von 4KB → 64 Chunks Ă  64 bytes).
  1. Gebe Chunk an Aufrufer zurĂŒck.

Freelist pointer protection:

  • Jeder freed Chunk speichert die Adresse des next free Chunk, aber encoded mit einem secret key.
  • Das Überschreiben dieses Feldes mit Angreifer-Daten funktioniert nicht, sofern man den Key nicht kennt.

Comparison Table

FeatureOld Heap (Pre-iOS 15)Modern Heap (iOS 15+ / A12+)
Allocation granularityFixed size buckets (kalloc.16, kalloc.32, etc.)Size + type-based buckets (kalloc_type)
Placement predictabilityHigh (same-size objects side by side)Low (same-type grouping + randomness)
Freelist managementRaw pointers in freed chunks (easy to corrupt)Encoded pointers (safe-linking style)
Adjacent object controlEasy via sprays/frees (feng shui predictable)Hard — typed zones separate attacker objects
Kernel data/code protectionsFew hardware protectionsPPL / SPTM protect page tables & code pages, and PAC protects pointers
Allocation reuse validationNone (freelist pointers raw)zone_require / zone enforcement
Exploit reliabilityHigh with heap spraysMuch lower, requires logic bugs or info leaks
Large allocations handlingAll small allocations managed equallyLarge ones bypass zones → handled via VM

Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)

In neueren Apple-OS-Versionen (insbesondere iOS 17+) fĂŒhrte Apple einen sichereren Userland-Allocator ein, xzone malloc (XZM). Dies ist das User-Space-Pendant zum Kernel-kalloc_type und wendet Type-Awareness, Metadaten-Isolation und Memory-Tagging-Sicherheiten an.

Goals & Design Principles

  • Type segregation / type awareness: Gruppiere Allocations nach Typ oder Verwendung (Pointer vs Data), um type confusion und Cross-Type-Reuse zu verhindern.
  • Metadata isolation: Trenne Heap-Metadaten (z. B. free lists, size/state bits) vom Objekt-Payload, sodass Out-of-Bounds-SchreibvorgĂ€nge weniger wahrscheinlich Metadaten korruptieren.
  • Guard pages / redzones: FĂŒge unmapped Pages oder Padding um Allocations ein, um Overflows zu erkennen.
  • Memory tagging (EMTE / MIE): Arbeitet zusammen mit Hardware-Tagging, um Use-After-Free, Out-of-Bounds und ungĂŒltige Zugriffe zu erkennen.
  • Scalable performance: Behalte niedrigen Overhead, vermeide ĂŒbermĂ€ĂŸige Fragmentierung und unterstĂŒtze viele Allocations pro Sekunde mit niedriger Latenz.

Architecture & Components

Nachfolgend die Hauptelemente im xzone-Allocator:

Segment Groups & Zones

  • Segment groups partitionieren den Adressraum nach Verwendungskategorien: z. B. data, pointer_xzones, data_large, pointer_large.
  • Jede Segment-Gruppe enthĂ€lt segments (VM-Bereiche), die Allocations fĂŒr diese Kategorie hosten.
  • Zu jedem Segment gehört ein metadata slab (separater VM-Bereich), das Metadaten speichert (z. B. free/used bits, GrĂ¶ĂŸenklassen) fĂŒr dieses Segment. Diese out-of-line (OOL) metadata stellt sicher, dass Metadaten nicht mit Objekt-Payloads vermischt sind und mindert Korruption durch Overflows.
  • Segments werden in chunks (Slices) unterteilt, die wiederum in blocks (Allocation-Einheiten) geschnitten werden. Ein Chunk ist an eine bestimmte GrĂ¶ĂŸenklasse und Segment-Gruppe gebunden (d. h. alle Blocks in einem Chunk teilen dieselbe GrĂ¶ĂŸe & Kategorie).
  • FĂŒr kleine/mittlere Allocations werden feste Chunk-GrĂ¶ĂŸen verwendet; fĂŒr große/huge kann separat gemappt werden.

Chunks & Blocks

  • Ein chunk ist ein Bereich (oft mehrere Pages), der einer GrĂ¶ĂŸenklasse innerhalb einer Gruppe gewidmet ist.
  • Innerhalb eines Chunks sind blocks Slots fĂŒr Allocations. Freed Blocks werden ĂŒber die metadata slab getrackt — z. B. per Bitmaps oder Free-Lists, die out-of-line gespeichert sind.
  • Zwischen Chunks (oder innerhalb) können guard slices / guard pages eingefĂŒgt werden (z. B. unmapped Slices), um Out-of-Bounds-SchreibvorgĂ€nge zu erkennen.

Type / Type ID

  • Jede Allocation-Site (oder Aufruf von malloc, calloc, etc.) ist mit einer type identifier (malloc_type_id_t) verbunden, die kodiert, welche Art von Objekt alloziert wird. Diese Type ID wird an den Allocator ĂŒbergeben, der sie benutzt, um die richtige Zone / Segment auszuwĂ€hlen.
  • Dadurch können zwei Allocations gleicher GrĂ¶ĂŸe in völlig unterschiedlichen Zonen landen, wenn ihre Typen unterschiedlich sind.
  • In frĂŒhen iOS-17-Versionen waren nicht alle APIs (z. B. CFAllocator) vollstĂ€ndig type-aware; Apple hat einige dieser SchwĂ€chen in iOS 18 adressiert.

Allocation & Freeing Workflow

Hier ein High-Level-Flow, wie Allocation und Deallocation in xzone funktionieren:

  1. malloc / calloc / realloc / typed alloc wird mit einer GrĂ¶ĂŸe und Type ID aufgerufen.
  2. Der Allocator benutzt die Type ID, um die korrekte Segment-Gruppe / Zone auszuwÀhlen.
  3. Innerhalb dieser Zone/Segment sucht er nach einem Chunk mit freien Blocks der gewĂŒnschten GrĂ¶ĂŸe.
  • Er kann lokale Caches / per-thread Pools oder free block lists aus den Metadaten konsultieren.
  • Wenn kein freier Block verfĂŒgbar ist, kann er einen neuen Chunk in dieser Zone allocaten.
  1. Die metadata slab wird aktualisiert (free bit wird gelöscht, BuchfĂŒhrung).
  2. Falls Memory-Tagging (EMTE) aktiv ist, bekommt der zurĂŒckgegebene Block ein Tag zugewiesen und die Metadaten werden auf „live“ gesetzt.
  3. Bei free():
  • Der Block wird in den Metadaten als freed markiert (via OOL slab).
  • Der Block kann in eine free list gelegt oder zum Reuse gepoolt werden.
  • Optional werden Block-Inhalte gecleared oder ge-poisoned, um Data-Leaks oder Use-After-Free-Exploitation zu reduzieren.
  • Der Hardware-Tag des Blocks kann invalidiert oder neu getaggt werden.
  • Wenn ein ganzer Chunk frei wird (alle Blocks freed), kann der Allocator diesen Chunk reclaimen (unmap oder an das OS zurĂŒckgeben) bei Memory-Pressure.

Security Features & Hardening

Dies sind die Abwehrmaßnahmen im modernen Userland-xzone:

FeatureZweckHinweise
Metadata decouplingVerhindert, dass Overflows Metadaten korruptierenMetadaten leben in separatem VM-Bereich (metadata slab)
Guard pages / unmapped slicesErkennen von Out-of-Bounds-WritesHilft, Buffer Overflows zu erkennen, statt angrenzende Blocks still zu korruptieren
Type-based segregationVerhindert Cross-Type-Reuse & type confusionAuch gleichgroße Allocations aus unterschiedlichen Typen gehen in verschiedene Zonen
Memory Tagging (EMTE / MIE)Erkennen ungĂŒltiger Zugriffe, stale references, OOB, UAFxzone arbeitet mit Hardware-EMTE im synchronen Modus („Memory Integrity Enforcement“) zusammen
Delayed reuse / poisoning / zapReduziert Chance auf Use-After-Free-ExploitationFreed Blocks können ge-poisoned, geloescht oder in QuarantÀne gehalten werden vor Reuse
Chunk reclamation / dynamic unmappingReduziert Memory-Waste und FragmentierungGanze Chunks können ungemappt werden, wenn sie ungenutzt sind
Randomization / placement variationVerhindert deterministische NachbarschaftenBlocks in einem Chunk und die Chunk-Auswahl können randomisierte Aspekte haben
Segregation of “data-only” allocationsTrennung von Allocations, die keine Pointer speichernVerringert Angreifer-Kontrolle ĂŒber Metadaten oder Kontrollfelder

Interaction with Memory Integrity Enforcement (MIE / EMTE)

  • Apples MIE (Memory Integrity Enforcement) ist das Hardware- + OS-Framework, das Enhanced Memory Tagging Extension (EMTE) in einen always-on, synchronen Modus ĂŒber große AngriffsflĂ€chen bringt.
  • Der xzone-Allocator ist eine fundamentale Grundlage von MIE im User-Space: Allocations, die via xzone erfolgen, bekommen Tags, und Zugriffe werden von der Hardware geprĂŒft.
  • In MIE sind Allocator, Tag-Zuweisung, Metadaten-Management und Tag-Confidentiality-Enforcement integriert, um sicherzustellen, dass Memory-Fehler (z. B. stale reads, OOB, UAF) sofort erkannt werden und nicht spĂ€ter ausgenutzt werden.

Wenn du magst, kann ich auch ein Cheat-Sheet oder Diagramm der xzone-Interna fĂŒr dein Buch erzeugen. Möchtest du, dass ich das als NĂ€chstes erstelle?
:contentReference[oai:20]{index=20}

(Old) Physical Use-After-Free via IOSurface

ios Physical UAF - 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

  1. Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be .ipsw files.
  2. Decompress until you get the bin format of the kernelcache of both .ipsw files. You have information on how to do this on:

macOS Kernel Extensions & Kernelcache

  1. Open Ghidra with ghidraRun, create a new project and load the kernelcaches.
  2. Open each kernelcache so they are automatically analyzed by Ghidra.
  3. Then, on the project Window of Ghidra, right click each kernelcache, select Export, select format Binary BinExport (v2) for BinDiff and export them.
  4. 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 modules

  • Watcher anti-analysis: Eine dedizierte Watcher-Binary profiliert das GerĂ€t kontinuierlich und bricht die Kill-Chain ab, wenn eine Analyseumgebung erkannt wird. Sie prĂŒft security.mac.amfi.developer_mode_status, das Vorhandensein einer diagnosticd-Konsole, die Locale US oder IL, Jailbreak-Spuren wie Cydia, Prozesse wie bash, tcpdump, frida, sshd oder checkrain, mobile AV-Apps (McAfee, AvastMobileSecurity, NortonMobileSecurity), benutzerdefinierte HTTP-Proxy-Einstellungen und benutzerdefinierte Root-CAs. Wird eine PrĂŒfung ausgelöst, wird die weitere Payload-Zustellung blockiert.
  • Helper surveillance hooks: Der Helper-Komponent kommuniziert mit anderen Stages ĂŒber /tmp/helper.sock und lĂ€dt dann Hook-Sets namens DMHooker und UMHooker. Diese Hooks greifen VOIP-Audio-Pfade ab (Aufnahmen landen unter /private/var/tmp/l/voip_%lu_%u_PART.m4a), implementieren einen systemweiten keylogger, nehmen Fotos ohne UI auf und hooken SpringBoard, um Benachrichtigungen zu unterdrĂŒcken, die diese Aktionen normalerweise auslösen wĂŒrden. Der Helper fungiert daher als unauffĂ€llige Validierungs- und leichte Überwachungs-Schicht, bevor schwerere Implants wie Predator installiert werden.

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

Referenzen

Tip

Lernen & ĂŒben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & ĂŒben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & ĂŒben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

UnterstĂŒtzen Sie HackTricks