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
- ĂberprĂŒfen Sie die AbonnementplĂ€ne!
- Treten Sie der đŹ Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter đŠ @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.
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.
mprotectmit 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_mapetc.) 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:
- Dem Pointer-Wert (kanonischer Teil)
- Einem modifier (ein Kontextwert, wie ein Salt)
- Dem geheimen Key
- 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.
PACxxInstruktionen signieren einen Pointer und fĂŒgen eine PAC einAUTxxInstruktionen authentifizieren + strippen (validieren und die PAC entfernen)XPACxxInstruktionen strippen ohne Validierung
Domains / Suffixe:
| Mnemonic | Meaning / Domain | Key / Domain | Example Usage in 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 |
Es gibt spezialisierte / Alias-Formen:
PACIASPist Kurzform fĂŒrPACIA X30, SP(sign the link register using SP as modifier)AUTIASPistAUTIA X30, SP(authenticate link register with SP)- Kombinierte Formen wie
RETAA,RETAB(authenticate-and-return) oderBLRAA(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
- 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.
- 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. Weildlsymlegitim 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
dlsymaufgelö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,NSExpressionoderNSInvocationverwendet, 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:
- 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).
- 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â.
- 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.
- 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.
- 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.
- 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
-
Thread dereferenziert einen ungĂŒltigen Pointer â CPU löst Data Abort aus.
-
Kernel trap handler ruft
exception_triage(EXC_BAD_ACCESS, ...)auf. -
Nachricht wird gesendet an:
-
Thread port â (debugger kann breakpoint abfangen).
-
Wenn debugger ignoriert â Task port â (process-level handler).
-
Wenn ignoriert â Host port (meist ReportCrash).
- Wenn niemand handlet â
bsd_exception()ĂŒbersetzt das inSIGSEGV.
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 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â Kern vonexception_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 Name | Element Size | Beispielverwendung |
|---|---|---|
default.kalloc.16 | 16 bytes | Sehr kleine Kernel-Strukturen, Pointer. |
default.kalloc.32 | 32 bytes | Kleine Strukturen, Objekt-Header. |
default.kalloc.64 | 64 bytes | IPC messages, winzige Kernel-Buffer. |
default.kalloc.128 | 128 bytes | MittelgroĂe Objekte wie Teile von OSObject. |
| ⊠| ⊠| ⊠|
default.kalloc.1280 | 1280 bytes | GroĂ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:
-
Pop HEAD (gibt diesen Speicher an den Caller zurĂŒck).
-
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:
-
Heap overflow in einen angrenzenden freed chunk â ĂŒberschreibe dessen ânextâ pointer.
-
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:
- 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.
- 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.
- 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).
- 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.
- 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.
- 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:
- Versuche per-CPU cache.
- Wenn leer, ziehe aus der global freelist.
- 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:
- Type lookup â
OSDatamappt auf diekalloc_type_osdatazone (GröĂe 64 bytes). - 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).
- 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
| 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 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:
- malloc / calloc / realloc / typed alloc wird mit einer GröĂe und Type ID aufgerufen.
- Der Allocator benutzt die Type ID, um die korrekte Segment-Gruppe / Zone auszuwÀhlen.
- 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.
- Die metadata slab wird aktualisiert (free bit wird gelöscht, BuchfĂŒhrung).
- Falls Memory-Tagging (EMTE) aktiv ist, bekommt der zurĂŒckgegebene Block ein Tag zugewiesen und die Metadaten werden auf âliveâ gesetzt.
- 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:
| Feature | Zweck | Hinweise |
|---|---|---|
| Metadata decoupling | Verhindert, dass Overflows Metadaten korruptieren | Metadaten leben in separatem VM-Bereich (metadata slab) |
| Guard pages / unmapped slices | Erkennen von Out-of-Bounds-Writes | Hilft, Buffer Overflows zu erkennen, statt angrenzende Blocks still zu korruptieren |
| Type-based segregation | Verhindert Cross-Type-Reuse & type confusion | Auch gleichgroĂe Allocations aus unterschiedlichen Typen gehen in verschiedene Zonen |
| Memory Tagging (EMTE / MIE) | Erkennen ungĂŒltiger Zugriffe, stale references, OOB, UAF | xzone arbeitet mit Hardware-EMTE im synchronen Modus (âMemory Integrity Enforcementâ) zusammen |
| Delayed reuse / poisoning / zap | Reduziert Chance auf Use-After-Free-Exploitation | Freed Blocks können ge-poisoned, geloescht oder in QuarantÀne gehalten werden vor Reuse |
| Chunk reclamation / dynamic unmapping | Reduziert Memory-Waste und Fragmentierung | Ganze Chunks können ungemappt werden, wenn sie ungenutzt sind |
| Randomization / placement variation | Verhindert deterministische Nachbarschaften | Blocks in einem Chunk und die Chunk-Auswahl können randomisierte Aspekte haben |
| Segregation of âdata-onlyâ allocations | Trennung von Allocations, die keine Pointer speichern | Verringert 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
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 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 einerdiagnosticd-Konsole, die LocaleUSoderIL, Jailbreak-Spuren wie Cydia, Prozesse wiebash,tcpdump,frida,sshdodercheckrain, 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.sockund 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
- ĂberprĂŒfen Sie die AbonnementplĂ€ne!
- Treten Sie der đŹ Discord-Gruppe oder der Telegram-Gruppe bei oder folgen Sie uns auf Twitter đŠ @hacktricks_live.
- Teilen Sie Hacking-Tricks, indem Sie PRs an die HackTricks und HackTricks Cloud GitHub-Repos senden.


