iOS Exploiting

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

iOS Exploit Mitigations

1. Code Signing / Runtime Signature Verification

Introduced early (iPhone OS → iOS) Questa è una delle protezioni fondamentali: tutto il codice eseguibile (app, dynamic libraries, JIT-ed code, extensions, frameworks, caches) deve essere firmato crittograficamente da una catena di certificati radicata nella fiducia di Apple. A runtime, prima di caricare un binario in memoria (o prima di effettuare salti attraverso certe boundary), il sistema verifica la sua firma. Se il codice viene modificato (bit-flipped, patched) o non è firmato, il caricamento fallisce.

  • Thwarts: la fase “classic payload drop + execute” nelle catene di exploit; arbitrary code injection; modificare un binario esistente per inserire logica malevola.
  • Mechanism detail:
  • Il Mach-O loader (e il dynamic linker) verifica le code pages, i segmenti, le entitlements, i team IDs e che la signature copra il contenuto del file.
  • Per regioni di memoria come i JIT caches o codice generato dinamicamente, Apple impone che le pagine siano firmate o validate tramite API speciali (es. mprotect con controlli code-sign).
  • La signature include entitlements e identificatori; l’OS impone che certe API o capacità privilegiate richiedano entitlements specifici che non possono essere falsificati.
Example Supponiamo che un exploit ottenga code execution in un processo e tenti di scrivere shellcode nell’heap e saltarci. Su iOS, quella pagina dovrebbe essere marcata executable **e** soddisfare i vincoli di code-signature. Poiché lo shellcode non è firmato con il certificato di Apple, il salto fallisce o il sistema rifiuta di rendere eseguibile quella regione di memoria.

2. CoreTrust

Introduced around iOS 14+ era (or gradually in newer devices / later iOS) CoreTrust è il sottosistema che esegue la runtime signature validation dei binari (inclusi i binari di sistema e utente) contro il root certificate di Apple invece di fare affidamento su store di trust cached in userland.

  • Thwarts: post-install tampering dei binari, tecniche di jailbreaking che cercano di sostituire o patchare librerie di sistema o app utente; ingannare il sistema sostituendo binari trusted con controparti malevole.
  • Mechanism detail:
  • Invece di affidarsi a un database di trust locale o a una cache di certificati, CoreTrust recupera o fa riferimento al root di Apple direttamente o verifica certificati intermedi in una catena sicura.
  • Si assicura che modifiche (es. nel filesystem) ai binari esistenti vengano rilevate e rifiutate.
  • Le entitlements, i team IDs, i flags di code signing e altri metadata sono vincolati al binario al momento del load.
Example Un jailbreak potrebbe tentare di sostituire `SpringBoard` o `libsystem` con una versione patchata per ottenere persistenza. Ma quando il loader del sistema o CoreTrust verifica, nota la mismatch della signature (o entitlements modificate) e rifiuta l’esecuzione.

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

Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time DEP impone che le pagine marcate writable (per i dati) siano non-executable, e le pagine marcate executable siano non-writable. Non si può semplicemente scrivere shellcode in una regione heap o stack ed eseguirlo.

  • Thwarts: esecuzione diretta di shellcode; classico buffer-overflow → salto a shellcode iniettato.
  • Mechanism detail:
  • L’MMU / i flag di protezione della memoria (tramite page tables) applicano la separazione.
  • Qualsiasi tentativo di marcare una pagina writable come executable scatena un controllo di sistema (ed è o proibito o richiede approvazione di code-sign).
  • In molti casi, rendere pagine executable richiede l’uso di API dell’OS che applicano vincoli o controlli addizionali.
Example Un overflow scrive shellcode nell’heap. L’attaccante tenta `mprotect(heap_addr, size, PROT_EXEC)` per renderla eseguibile. Ma il sistema rifiuta o valida che la nuova pagina debba superare i vincoli di code-sign (cosa che lo shellcode non può fare).

4. Address Space Layout Randomization (ASLR)

Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) ASLR randomizza gli indirizzi base di regioni chiave di memoria: libraries, heap, stack, ecc., a ogni avvio del processo. Gli indirizzi dei gadget cambiano fra le esecuzioni.

  • Thwarts: hardcoding di indirizzi di gadget per ROP/JOP; catene di exploit statiche; saltare “alla cieca” a offset noti.
  • Mechanism detail:
  • Ogni libreria / modulo dinamico caricato viene rebased a un offset randomizzato.
  • Stack e heap base pointers sono randomizzati (entro certi limiti di entropia).
  • A volte anche altre regioni (es. mmap allocations) sono randomizzate.
  • Combinato con mitigazioni di information-leak, forza l’attaccante a prima leakare un indirizzo o un puntatore per scoprire le base addresses a runtime.
Example Una ROP chain si aspetta un gadget a `0x….lib + offset`. Ma poiché `lib` viene rilocata diversamente a ogni run, la catena hardcodata fallisce. Un exploit deve prima leakare l’indirizzo base del modulo prima di calcolare gli indirizzi dei gadget.

5. Kernel Address Space Layout Randomization (KASLR)

Introduced in iOS ~ (iOS 5 / iOS 6 timeframe) Analogamente a ASLR utente, KASLR randomizza la base del kernel text e altre strutture kernel al boot.

  • Thwarts: exploit a livello kernel che si basano su locazioni fisse del codice o dei dati kernel; exploit kernel statici.
  • Mechanism detail:
  • Ad ogni boot l’indirizzo base del kernel viene randomizzato (entro un range).
  • Strutture dati kernel (come task_structs, vm_map, ecc.) possono essere relocate o offsettate.
  • Gli attaccanti devono prima leakare puntatori kernel o usare vulnerabilità di information disclosure per calcolare offset prima di corrompere strutture o codice kernel.
Example Una vulnerabilità locale mira a corrompere un function pointer kernel (es. in una `vtable`) a `KERN_BASE + offset`. Ma poiché `KERN_BASE` è sconosciuto, l’attaccante deve prima leakarlo (es. tramite una primitive di read) prima di calcolare l’indirizzo corretto per la corruzione.

6. Kernel Patch Protection (KPP / AMCC)

Introduced in newer iOS / A-series hardware (post around iOS 15–16 era or newer chips) KPP (aka AMCC) monitora continuamente l’integrità delle kernel text pages (via hash o checksum). Se rileva tampering (patch, inline hooks, code modifications) al di fuori delle finestre consentite, scatena un kernel panic o un reboot.

  • Thwarts: kernel patching persistente (modifica di istruzioni kernel), inline hooks, sovrascritture statiche di funzioni.
  • Mechanism detail:
  • Un modulo hardware o firmware monitora la regione kernel text.
  • Periodicamente o su richiesta ricalcola gli hash delle pagine e li confronta con i valori attesi.
  • Se si rilevano mismatch al di fuori di finestre di update legittime, fa panicare il dispositivo (per prevenire patch malevole persistenti).
  • Gli attaccanti devono o evitare le finestre di rilevamento o usare percorsi di patch legittimi.
Example Un exploit tenta di patchare il prologo di una funzione kernel (es. `memcmp`) per intercettare le chiamate. Ma KPP nota che la hash della pagina di codice non corrisponde a quella attesa e scatena un kernel panic, crashando il dispositivo prima che la patch si stabilizzi.

7. Kernel Text Read‐Only Region (KTRR)

Introduced in modern SoCs (post ~A12 / newer hardware) KTRR è un meccanismo hardware-enforced: una volta che il kernel text viene lockato early durante il boot, diventa read-only da EL1 (il kernel), prevenendo ulteriori scritture sulle code pages.

  • Thwarts: qualsiasi modifica al codice kernel dopo il boot (es. patching, code injection in-place) a livello di privilegio EL1.
  • Mechanism detail:
  • Durante il boot (nella fase secure/bootloader), il memory controller (o un’unità hardware sicura) marca le physical pages contenenti il kernel text come read-only.
  • Anche se un exploit ottiene privilegi kernel completi, non può scrivere su quelle pagine per patchare istruzioni.
  • Per modificarle, l’attaccante deve prima compromettere la catena di boot o sovvertire KTRR stesso.
Example Un exploit di escalation privilegio salta in EL1 e scrive un trampoline in una funzione kernel (es. nell’handler di `syscall`). Ma poiché le pagine sono lockate read-only da KTRR, la scrittura fallisce (o genera fault), quindi le patch non vengono applicate.

8. Pointer Authentication Codes (PAC)

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

  • PAC è una funzionalità hardware introdotta in ARMv8.3-A per rilevare la manomissione di valori di puntatore (return addresses, function pointers, certi data pointers) incorporando una piccola signature crittografica (una “MAC”) nei bit superiori inutilizzati del puntatore.
  • La signature (“PAC”) è calcolata sul valore del puntatore più un modifier (un valore di contesto, es. stack pointer o qualche dato distintivo). In questo modo lo stesso valore di puntatore in contesti diversi ottiene PAC diversi.
  • Al momento dell’uso, prima di dereferenziare o branchare tramite quel puntatore, un’istruzione di authenticate verifica la PAC. Se valida, la PAC viene rimossa e si ottiene il puntatore “puro”; se invalida, il puntatore diventa “poisoned” (o viene generato un fault).
  • Le chiavi usate per produrre/validare le PAC risiedono in registri privilegiati (EL1, kernel) e non sono leggibili da user mode.
  • Poiché non tutti i 64 bit di un puntatore sono usati in molti sistemi (es. spazio d’indirizzi a 48 bit), i bit superiori sono “spare” e possono contenere la PAC senza alterare l’indirizzo effettivo.

Architectural Basis & Key Types

  • ARMv8.3 introduce cinque chiavi a 128-bit (ognuna implementata tramite due registri system a 64-bit) per pointer authentication.

  • APIAKey — per instruction pointers (dominio “I”, key A)

  • APIBKey — secondo key per instruction pointers (dominio “I”, key B)

  • APDAKey — per data pointers (dominio “D”, key A)

  • APDBKey — per data pointers (dominio “D”, key B)

  • APGAKey — key “generic”, per firmare dati non-pointer o altri usi generici

  • Queste chiavi sono conservate in registri di sistema privilegiati (accessibili solo a EL1/EL2, etc.), non accessibili da user mode.

  • La PAC è calcolata tramite una funzione crittografica (ARM suggerisce QARMA come algoritmo) usando:

  1. Il valore del puntatore (porzione canonica)
  2. Un modifier (un valore di contesto, come un salt)
  3. La chiave segreta
  4. Alcuna logica interna di tweak Se la PAC risultante corrisponde a quella immagazzinata nei bit superiori del puntatore, l’autenticazione ha successo.

Instruction Families

La convenzione di denominazione è: PAC / AUT / XPAC, poi le lettere di dominio.

  • PACxx istruzioni firmano un puntatore e inseriscono una PAC
  • AUTxx istruzioni autenticano + rimuovono (validano e rimuovono la PAC)
  • XPACxx istruzioni rimuovono senza validare

Domains / suffixes:

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

There are specialized / alias forms:

  • PACIASP is shorthand for PACIA X30, SP (sign the link register using SP as modifier)
  • AUTIASP is AUTIA X30, SP (authenticate link register with SP)
  • Combined forms like RETAA, RETAB (authenticate-and-return) or BLRAA (authenticate & branch) exist in ARM extensions / compiler support.
  • Also zero-modifier variants: PACIZA / PACIZB where the modifier is implicitly zero, etc.

Modifiers

Lo scopo principale del modifier è legare la PAC a uno specifico contesto così che lo stesso indirizzo firmato in frame o oggetti diversi produca PAC differenti. È come aggiungere un salt a un hash.

Perciò:

  • Il modifier è un valore di contesto (un altro registro) che viene miscelato nel calcolo della PAC. Scelte tipiche: lo stack pointer (SP), un frame pointer, o un object ID.
  • Usare SP come modifier è comune per il signing degli indirizzi di ritorno: la PAC diventa vincolata allo specifico stack frame. Se provi a riusare LR in un frame diverso, il modifier cambia, quindi la validazione PAC fallisce.
  • Lo stesso valore di puntatore firmato con modifier differenti produce PAC diverse.
  • Il modifier non deve essere segreto, ma idealmente non deve essere controllato dall’attaccante.
  • Per istruzioni che firmano o verificano puntatori dove non esiste un modifier significativo, alcune forme usano zero o una costante implicita.

Apple / iOS / XNU Customizations & Observations

  • L’implementazione PAC di Apple include diversificatori per boot in modo che le chiavi o i tweak cambino a ogni boot, prevenendo il riuso tra boot.
  • Includono anche mitigazioni cross-domain in modo che PAC firmati in user mode non siano facilmente riutilizzabili in kernel mode, ecc.
  • Su Apple M1 / Apple Silicon, il reverse engineering ha mostrato che ci sono nove tipi di modifier e registri di sistema Apple-specifici per il controllo delle chiavi.
  • Apple usa PAC in molti sottosistemi kernel: signing degli indirizzi di ritorno, integrità dei puntatori nei dati kernel, signed thread contexts, ecc.
  • Google Project Zero ha dimostrato che con una potente primitive di memory read/write in kernel si potevano forgiare PAC kernel (per A keys) su dispositivi A12-era, ma Apple ha patchato molte di quelle vie.
  • Nel sistema Apple, alcune chiavi sono globali al kernel, mentre i processi utente possono ottenere randomness per le chiavi per processo.

PAC Bypasses

  1. Kernel-mode PAC: theoretical vs real bypasses
  • Poiché le chiavi PAC kernel e la logica sono strettamente controllate (registri privilegiati, diversificatori, isolamento di dominio), forgiare puntatori kernel firmati arbitrariamente è molto difficile.
  • Azad nel 2020 “iOS Kernel PAC, One Year Later” riporta che in iOS 12-13 trovò alcuni bypass parziali (signing gadgets, riuso di signed states, indirect branches non protette) ma nessun bypass generico completo. bazad.github.io
  • Le customizzazioni “Dark Magic” di Apple restringono ulteriormente le superfici sfruttabili (domain switching, per-key enabling bits). i.blackhat.com
  • Esiste un noto kernel PAC bypass CVE-2023-32424 su Apple silicon (M1/M2) segnalato da Zecao Cai et al. i.blackhat.com
  • Ma questi bypass spesso dipendono da gadget molto specifici o bug di implementazione; non sono bypass generici.

Quindi il kernel PAC è considerato altamente robusto, anche se non perfetto.

  1. User-mode / runtime PAC bypass techniques

Queste sono più comuni e sfruttano imperfezioni nel modo in cui PAC è applicato o usato nel dynamic linking / runtime frameworks. Di seguito le classi, con esempi.

2.1 Shared Cache / A key issues

  • La dyld shared cache è un grande blob pre-linked di system frameworks e libraries. Poiché è così ampiamente condivisa, function pointers all’interno della shared cache sono “pre-signed” e poi usati da molti processi. Gli attaccanti mirano a questi puntatori già firmati come “PAC oracles”.

  • Alcune tecniche di bypass cercano di estrarre o riutilizzare puntatori firmati con A-key presenti nella shared cache e riusarle nei gadget.

  • Il talk “No Clicks Required” descrive la costruzione di un oracle sulla shared cache per inferire indirizzi relativi e combinarli con puntatori firmati per bypassare PAC. saelo.github.io

  • Inoltre, gli import di function pointers da shared libraries in userspace sono stati trovati insufficientemente protetti da PAC, permettendo a un attaccante di ottenere function pointers senza cambiare la loro signature. (Project Zero bug entry) bugs.chromium.org

2.2 dlsym(3) / dynamic symbol resolution

  • Un bypass noto è chiamare dlsym() per ottenere un function pointer già firmato (signed con A-key, diversifier zero) e poi usarlo. Poiché dlsym restituisce un puntatore legittimamente firmato, usarlo aggira la necessità di forgiare PAC.

  • Il blog di Epsilon spiega come alcuni bypass sfruttino questo: chiamare dlsym("someSym") ritorna un puntatore firmato e può essere usato per indirect calls. blog.epsilon-sec.com

  • Synacktiv in “iOS 18.4 — dlsym considered harmful” descrive un bug: alcuni simboli risolti via dlsym su iOS 18.4 restituiscono puntatori firmati in modo errato (o con diversifiers buggy), abilitando bypass involontari di PAC. Synacktiv

  • La logica in dyld per dlsym include: quando result->isCode, firmano il puntatore restituito con __builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), cioè contesto zero. blog.epsilon-sec.com

Quindi, dlsym è un vettore frequente nei bypass PAC in user-mode.

2.3 Other DYLD / runtime relocations

  • Il loader DYLD e la logica di dynamic relocation sono complesse e a volte mappano temporaneamente pagine come read/write per eseguire le relocation, poi le riportano in read-only. Gli attaccanti sfruttano queste finestre. Il talk di Synacktiv descrive “Operation Triangulation”, un bypass basato sul timing delle relocation dinamiche. Synacktiv

  • Le pagine DYLD ora sono protette con SPRR / VM_FLAGS_TPRO (alcuni flag di protezione per dyld). Ma versioni precedenti avevano guardie più deboli. Synacktiv

  • Nelle catene di exploit WebKit, il loader DYLD è spesso un target per il bypass PAC. Le slides menzionano che molti bypass PAC hanno preso di mira il DYLD loader (via relocation, interposer hooks). Synacktiv

2.4 NSPredicate / NSExpression / ObjC / SLOP

  • Nelle catene di exploit userland, metodi del runtime Objective-C come NSPredicate, NSExpression o NSInvocation sono usati per contrabbandare chiamate di controllo senza apparente forging di puntatori.

  • Su iOS più vecchi (prima di PAC), un exploit usava fake NSInvocation objects per chiamare selector arbitrari su memoria controllata. Con PAC servono modifiche. Ma la tecnica SLOP (SeLector Oriented Programming) è stata estesa anche sotto PAC. Project Zero

  • La tecnica SLOP originale permetteva il chaining di chiamate ObjC creando invocations false; il bypass si basa sul fatto che ISA o selector pointers a volte non sono completamente protetti da PAC. Project Zero

  • In ambienti dove pointer authentication è applicata parzialmente, metodi / selector / target pointers possono non avere sempre protezione PAC, lasciando spazio a bypass.

Example Flow

Esempio: firma e autenticazione ``` ; 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>
Un buffer overflow sovrascrive un return address nello stack. L'attaccante scrive l'indirizzo del gadget di destinazione ma non può calcolare il PAC corretto. Quando la funzione ritorna, l'istruzione CPU `AUTIA` genera un fault a causa del mismatch del PAC. La catena fallisce.
L'analisi di Project Zero su A12 (iPhone XS) ha mostrato come viene usato il PAC di Apple e i metodi per forgiare PAC se un attaccante dispone di un primitive di lettura/scrittura di memoria.
</details>


### 9. **Branch Target Identification (BTI)**
**Introduced with ARMv8.5 (later hardware)**
BTI è una feature hardware che verifica i **target di branch indiretti**: quando si eseguono `blr` o chiamate/salti indiretti, il target deve iniziare con un **BTI landing pad** (`BTI j` o `BTI c`). Saltare verso indirizzi di gadget che non hanno il landing pad provoca un'eccezione.

Le note di implementazione di LLVM descrivono tre varianti dell'istruzione BTI e come esse si mappano ai tipi di branch.

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

- Nel codice compilato con branch target enforcement, i compiler inseriscono un'istruzione BTI (C, J o JC) ad ogni valido target di branch indiretto (inizio di funzioni o blocchi raggiungibili da salti) in modo che i branch indiretti abbiano successo solo verso quei punti.
- **Direct branches / calls** (cioè indirizzi fissi `B`, `BL`) **non sono limitati** da BTI. L'assunzione è che le pagine di codice siano trusted e che un attaccante non possa modificarle (quindi i direct branches sono considerati sicuri).
- Inoltre, le istruzioni **RET / return** generalmente non sono soggette a restrizioni BTI perché gli indirizzi di return sono protetti tramite PAC o meccanismi di return signing.

#### Mechanism and enforcement

- Quando la CPU decodifica un **indirect branch (BLR / BR)** in una pagina marcata come “guarded / BTI-enabled,” controlla se la prima istruzione all'indirizzo target è una BTI valida (C, J o JC come permesso). Se non lo è, si verifica una **Branch Target Exception**.
- L'encoding dell'istruzione BTI è progettato per riusare opcode precedentemente riservati a NOP (nelle versioni ARM precedenti). Quindi i binari BTI-enabled rimangono backward-compatible: su hardware senza supporto BTI, quelle istruzioni si comportano come NOP.
- I pass del compiler che aggiungono BTI li inseriscono solo dove necessario: funzioni che possono essere chiamate indirettamente, o basic block target di salti.
- Alcune patch e codice LLVM mostrano che BTI non viene inserito per *tutti* i basic block — solo per quelli che sono potenziali target di branch (es. da switch / jump table).

#### BTI + PAC synergy

PAC protegge il valore del puntatore (la sorgente) — garantendo che la catena di chiamate indirette / return non sia stata manomessa.

BTI assicura che anche un puntatore valido deve puntare solo a entry point opportunamente marcati.

Combinati, un attaccante necessita sia di un puntatore valido con PAC corretto sia che il target abbia un BTI posizionato lì. Questo aumenta la difficoltà di costruire gadget di exploit.

#### Example


<details>
<summary>Example</summary>
Un exploit tenta di pivotare verso un gadget a `0xABCDEF` che non inizia con `BTI c`. La CPU, eseguendo `blr x0`, verifica il target e fa fault perché l'allineamento dell'istruzione non include un valid landing pad. Di conseguenza molti gadget diventano inutilizzabili a meno che non includano il prefisso BTI.
</details>


### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Introduced in more recent ARMv8 extensions / iOS support (for hardened kernel)**

#### PAN (Privileged Access Never)

- **PAN** è una feature introdotta in **ARMv8.1-A** che impedisce al codice **privilegiato** (EL1 o EL2) di **leggere o scrivere** memoria marcata come **user-accessible (EL0)**, a meno che PAN non sia esplicitamente disabilitato.
- L'idea: anche se il kernel viene ingannato o compromesso, non può dereferenziare arbitrariamente puntatori user-space senza prima *clearare* PAN, riducendo così il rischio di exploit in stile **`ret2usr`** o l'abuso di buffer controllati dall'utente.
- Quando PAN è abilitato (PSTATE.PAN = 1), qualsiasi istruzione privilegiata di load/store che accede a un indirizzo virtuale “accessible at EL0” provoca un **permission fault**.
- Il kernel, quando deve legittimamente accedere a memoria user-space (es. copiare dati da/verso buffer utente), deve **disabilitare temporaneamente PAN** (o usare istruzioni “unprivileged load/store”) per permettere l'accesso.
- In Linux su ARM64, il supporto PAN è stato introdotto intorno al 2015: patch del kernel hanno aggiunto il rilevamento della feature e hanno sostituito `get_user` / `put_user` ecc. con varianti che clearano PAN attorno agli accessi alla memoria utente.

**Key nuance / limitation / bug**
- Come notato da Siguza e altri, un bug di specifica (o comportamenti ambigui) nel design ARM fa sì che le mappature utente execute-only (`--x`) possano **non attivare PAN**. In altre parole, se una pagina utente è marcata eseguibile ma senza permesso di lettura, il tentativo del kernel di leggere potrebbe bypassare PAN perché l'architettura considera “accessible at EL0” come requisito di lettura, non solo di esecuzione. Questo porta a un bypass di PAN in certe configurazioni.
- Per questo motivo, se iOS / XNU permettono pagine utente execute-only (come alcune configurazioni JIT o code-cache), il kernel potrebbe accidentalmente leggere da esse anche con PAN abilitato. Questa è una nota area sottile e talvolta sfruttabile in alcuni sistemi ARMv8+.

#### PXN (Privileged eXecute Never)

- **PXN** è un flag nella page table (nelle entry leaf o block) che indica che la pagina è **non eseguibile quando si esegue in privileged mode** (cioè quando EL1 esegue).
- PXN impedisce al kernel (o a qualsiasi codice privilegiato) di saltare o eseguire istruzioni provenienti da pagine user-space anche se il controllo viene deviato. Di fatto, blocca una redirezione del control-flow a livello kernel verso memoria utente.
- Combinato con PAN, questo assicura che:
1. Il kernel non può (di default) leggere o scrivere dati user-space (PAN)
2. Il kernel non può eseguire codice user-space (PXN)
- Nel formato di page table ARMv8, le entry leaf hanno un bit `PXN` (e anche `UXN` per unprivileged execute-never) nei loro attributi.

Quindi anche se il kernel ha un puntatore di funzione corrotto che punta a memoria utente e prova a brancharci, il bit PXN causerebbe un fault.

#### Memory-permission model & how PAN and PXN map to page table bits

Per comprendere come funzionano PAN / PXN, è necessario vedere come funziona il modello di traduzione e permessi di ARM (semplificato):

- Ogni page o block entry ha campi attributo inclusi **AP[2:1]** per i permessi di accesso (read/write, privileged vs unprivileged) e bit **UXN / PXN** per le restrizioni execute-never.
- Quando PSTATE.PAN è 1 (enabled), l'hardware applica semantiche modificate: gli accessi privilegiati a pagine marcate come “accessible by EL0” (cioè accessibili dall'utente) sono disabilitati (fault).
- A causa del bug menzionato, pagine marcate solo come eseguibili (senza permesso di lettura) potrebbero non essere conteggiate come “accessible by EL0” in alcune implementazioni, permettendo così il bypass di PAN.
- Se il bit PXN di una pagina è impostato, anche se l'istruction fetch proviene da un livello di privilegio più alto, l'esecuzione è proibita.

#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)

In un design di kernel hardening (come quello che Apple potrebbe usare):

- Il kernel abilita PAN per default (quindi il codice privilegiato è vincolato).
- Nei percorsi che devono legittimamente leggere o scrivere buffer utente (es. copia buffer syscall, I/O, read/write su user pointer), il kernel **disabilita temporaneamente PAN** o usa istruzioni speciali per sovrascrivere il comportamento.
- Dopo aver finito l'accesso ai dati utente, deve riabilitare PAN.
- PXN è applicato via page table: le pagine utente hanno PXN = 1 (quindi il kernel non può eseguirle), le pagine kernel non hanno PXN (quindi il codice kernel può eseguirle).
- Il kernel deve garantire che nessun percorso di codice causi l'esecuzione di flussi in regioni di memoria utente (che aggirerebbero PXN) — quindi le catene di exploit che si basano su “jump into user-controlled shellcode” vengono bloccate.

A causa del bypass PAN tramite pagine execute-only, in un sistema reale Apple potrebbe disabilitare o non consentire pagine utente execute-only, o applicare patch per aggirare la debolezza della specifica.


#### Attack surfaces, bypasses, and mitigations

- **PAN bypass via execute-only pages**: come discusso, la spec lascia una lacuna: pagine utente con execute-only (nessun permesso di read) potrebbero non essere considerate “accessible at EL0,” quindi PAN non bloccherebbe le letture del kernel su tali pagine in alcune implementazioni. Questo offre all'attaccante un percorso insolito per fornire dati tramite sezioni “execute-only”.
- **Temporal window exploit**: se il kernel disabilita PAN per una finestra temporale più lunga del necessario, una race o un percorso malevolo potrebbe sfruttare quella finestra per eseguire accessi non intenzionati alla memoria utente.
- **Forgotten re-enable**: se i percorsi di codice non riabilitano PAN, operazioni kernel successive potrebbero accedere in modo errato alla memoria utente.
- **Misconfiguration of PXN**: se le page table non impostano PXN sulle pagine utente o mappano in modo errato le pagine di codice utente, il kernel potrebbe essere ingannato nell'eseguire codice user-space.
- **Speculation / side-channels**: analogamente ai bypass speculativi, potrebbero esistere side-effect microarchitetturali che causano violazioni transitorie dei controlli PAN / PXN (anche se tali attacchi dipendono fortemente dal design della CPU).
- **Complex interactions**: in funzionalità più avanzate (es. JIT, shared memory, regioni di codice just-in-time), il kernel può aver bisogno di controllo granulare per permettere certi accessi a memoria o esecuzioni in regioni mappate per l'utente; progettare ciò in sicurezza sotto i vincoli PAN/PXN non è banale.


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

Se il kernel non avesse **impostato** PXN su quella pagina utente, allora il branch potrebbe riuscire — il che sarebbe insicuro.

Se il kernel si dimentica di riabilitare PAN dopo l'accesso alla memoria utente, si apre una finestra in cui altra logica del kernel potrebbe accidentalmente leggere/scrivere memoria utente arbitraria.

Se il puntatore utente punta a una pagina execute-only (pagina utente con solo permesso execute, nessun read/write), sotto il bug della specifica PAN, `ldr W2, [X1]` potrebbe **non** generare fault anche con PAN abilitato, permettendo un exploit di bypass, a seconda dell'implementazione.

</details>

<details>
<summary>Example</summary>
Una vulnerabilità nel kernel cerca di prendere un puntatore a funzione fornito dall'utente e invocarlo in contesto kernel (i.e. `call user_buffer`). Sotto PAN/PXN, quell'operazione è proibita o causa un fault.
</details>

---

### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI significa Top Byte Ignore (a volte chiamato Address Tagging). Significa che il top byte (byte più significativo) di un puntatore a 64 bit viene ignorato dalla address translation. Questo permette a OS o hardware di incorporare bit di **tag** nel top byte del puntatore senza influire sull'indirizzo effettivo.

- TBI stands for **Top Byte Ignore** (sometimes called *Address Tagging*). È una funzionalità hardware (disponibile in molte implementazioni ARMv8+) che **ignora i top 8 bits** (bits 63:56) di un puntatore a 64 bit quando si esegue la **address translation / load/store / instruction fetch**.
- In pratica, la CPU tratta un puntatore `0xTTxxxx_xxxx_xxxx` (dove `TT` = top byte) come `0x00xxxx_xxxx_xxxx` ai fini della address translation, ignorando (mascherando) il top byte. Il top byte può essere usato dal software per memorizzare **metadata / tag bits**.
- Questo dà al software spazio "in-band" gratuito per incorporare un byte di tag in ogni puntatore senza alterare quale locazione di memoria esso indica.
- L'architettura garantisce che load, store e instruction fetch trattino il puntatore con il top byte mascherato (cioè tag rimosso) prima di eseguire l'accesso reale alla memoria.

Quindi TBI disaccoppia il **logical pointer** (pointer + tag) dall'**indirizzo fisico** usato per le operazioni di memoria.

#### Why TBI: Use cases and motivation

- **Pointer tagging / metadata**: È possibile memorizzare metadata aggiuntivi (es. tipo di oggetto, versione, bounds, integrity tags) in quel top byte. Quando poi si usa il puntatore, il tag viene ignorato a livello hardware, quindi non è necessario rimuoverlo manualmente per l'accesso alla memoria.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI è il meccanismo hardware di base su cui MTE si costruisce. In ARMv8.5, la **Memory Tagging Extension** usa i bit 59:56 del puntatore come **logical tag** e li confronta con un **allocation tag** memorizzato nella memoria.
- **Enhanced security & integrity**: Combinando TBI con pointer authentication (PAC) o controlli runtime, puoi forzare non solo il valore del puntatore ma anche che il tag sia corretto. Un attaccante che sovrascrive un puntatore senza il tag corretto produrrà un mismatch sul tag.
- **Compatibility**: Poiché TBI è opzionale e i tag bits sono ignorati dall'hardware, il codice legacy non taggato continua a funzionare normalmente. I tag diventano effettivamente bit "don't care" per codice legacy.

#### Example
<details>
<summary>Example</summary>
Un puntatore a funzione includeva un tag nel top byte (per esempio `0xAA`). Un exploit sovrascrive i bit bassi del puntatore ma trascura il tag, quindi quando il kernel verifica o sanitizza, il puntatore fallisce o viene rifiutato.
</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 è progettato come un **boundary di protezione intra-kernel**: anche se il kernel (EL1) è compromesso e possiede capacità di read/write, **non dovrebbe poter modificare liberamente** certe **pagine sensibili** (in particolare page tables, metadata di code-signing, pagine di codice del kernel, entitlements, trust caches, ecc.).
- Crea effettivamente un **"kernel dentro il kernel"** — un componente più piccolo e trusted (PPL) con **privilegi elevati** che solo esso può usare per modificare pagine protette. Il resto del codice kernel deve chiamare routine PPL per effettuare cambiamenti.
- Questo riduce la superficie d'attacco per exploit kernel: anche con arbitrary R/W/execute in kernel mode, il codice exploit deve anche in qualche modo entrare nel dominio PPL (o bypassare PPL) per modificare strutture critiche.
- Su Apple silicon più recenti (A15+ / M2+), Apple sta transitando verso **SPTM (Secure Page Table Monitor)**, che in molti casi sostituisce PPL per la protezione delle page-table su quelle piattaforme.

Ecco come si ritiene che PPL operi, basandosi su analisi pubbliche:

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

- Apple hardware usa un meccanismo chiamato **APRR (Access Permission ReRouting)**, che permette alle page table entries (PTEs) di contenere piccoli indici, invece dei full permission bits. Quegli indici sono mappati tramite registri APRR alle effettive permission. Questo permette il remapping dinamico delle permission per dominio.
- PPL sfrutta APRR per segregare i privilegi all'interno del contesto kernel: solo il dominio PPL è autorizzato ad aggiornare la mappatura tra indici e permission effettive. Cioè, quando codice non-PPL del kernel scrive una PTE o cerca di cambiare bit di permission, la logica APRR lo disabilita (o impone una mappatura read-only).
- Il codice PPL stesso gira in una regione ristretta (es. `__PPLTEXT`) che normalmente è non-eseguibile o non-scrivibile fino a quando i gate di ingresso la rendono temporaneamente accessibile. Il kernel chiama entry points PPL ("PPL routines") per eseguire operazioni sensibili.

#### Gate / Entry & Exit

- Quando il kernel deve modificare una pagina protetta (es. cambiare permessi di una pagina di codice del kernel o modificare page tables), chiama una routine wrapper PPL, che fa validazione e poi esegue la transizione nel dominio PPL. Fuori da quel dominio, le pagine protette sono effettivamente read-only o non modificabili dal kernel principale.
- Durante l'entry in PPL, le mappature APRR sono aggiustate in modo che le pagine di memoria nella regione PPL siano impostate come **executable & writable** all'interno di PPL. All'uscita, vengono ristabilite come read-only / non-writable. Questo assicura che solo routine PPL ben revisionate possano scrivere su pagine protette.
- Fuori da PPL, i tentativi del codice kernel di scrivere su quelle pagine protette genereranno fault (permission denied) perché la mappatura APRR per quel dominio di codice non permette la scrittura.

#### Protected page categories

Le pagine tipicamente protette da PPL includono:

- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, specialmente quelle che contengono logica critica
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Altre strutture kernel ad alto valore dove una patch permetterebbe di bypassare controlli di firma o manipolare credenziali

L'idea è che anche se la memoria del kernel è completamente compromessa, l'attaccante non può semplicemente patchare o riscrivere queste pagine, a meno che non comprometta anche le routine PPL o bypassi PPL.

#### Known Bypasses & Vulnerabilities

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

- Un writeup pubblico di Project Zero descrive un bypass che coinvolge **stale TLB entries**.
- L'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.

- Questo exploit richiedeva controllo fine sulla physical mapping e sul comportamento della TLB. Dimostra che un confine di sicurezza che si basa sulla correttezza di TLB / mapping deve essere estremamente attento all'invalidazione della TLB e alla consistenza delle mappature.

- Project Zero ha commentato che bypass come questo sono sottili e rari, ma possibili in sistemi complessi. Tuttavia li considerano una mitigazione solida.

2. **Other potential hazards & constraints**

- Se un exploit kernel può direttamente entrare nelle routine PPL (chiamando i wrapper PPL), potrebbe bypassare le restrizioni. Quindi la validazione degli argomenti è critica.
- Bug nel codice PPL stesso (es. overflow aritmetico, controlli di bound) possono permettere modifiche out-of-bounds all'interno di PPL. Project Zero ha osservato che un bug in `pmap_remove_options_internal()` è stato sfruttato nel loro bypass.
- Il boundary PPL è irrevocabilmente legato all'enforcement hardware (APRR, memory controller), quindi è forte quanto l'implementazione hardware.

#### Example
<details>
<summary>Code Example</summary>
Ecco un pseudocodice semplificato / logica che mostra come un kernel potrebbe chiamare PPL per modificare pagine protette:
```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

Il kernel può eseguire molte operazioni normali, ma solo tramite le routine ppl_call_* può cambiare protected mappings o patch code.

Esempio Un kernel exploit tenta di sovrascrivere the entitlement table, o di disabilitare l'enforcement di code-sign modificando un kernel signature blob. Poiché quella page è PPL-protected, la write viene bloccata a meno di passare tramite il PPL interface. Quindi anche con kernel code execution, non puoi bypassare i vincoli di code-sign o modificare credential data arbitrariamente. Su iOS 17+ alcuni device usano SPTM per isolare ulteriormente le PPL-managed pages.

PPL → SPTM / Replacements / Future

  • Sui moderni SoCs di Apple (A15 o successivi, M2 o successivi), Apple supporta SPTM (Secure Page Table Monitor), che replaces PPL per page table protections.
  • Apple indica nella documentazione: “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.”
  • L’architettura SPTM probabilmente sposta più enforcement delle policy in un monitor con privilegi superiori esterno al controllo del kernel, riducendo ulteriormente il trust boundary.

MTE | EMTE | MIE

Ecco una descrizione ad alto livello di come EMTE opera nel setup MIE di Apple:

  1. Tag assignment
  • Quando la memory viene allocata (es. in kernel o user space tramite secure allocators), a quel blocco viene assegnato un secret tag.
  • Il pointer restituito all’user o al kernel include quel tag nei suoi high bits (usando TBI / top byte ignore mechanisms).
  1. Tag checking on access
  • Ogni volta che un load o store viene eseguito usando un pointer, l’hardware verifica che il tag del pointer corrisponda al tag del memory block (allocation tag). In caso di mismatch, faulta immediatamente (dato che è synchronous).
  • Poiché è synchronous, non esiste una finestra di “delayed detection”.
  1. Retagging on free / reuse
  • Quando la memory viene freed, l’allocator cambia il tag del block (quindi i pointer più vecchi con old tags non corrispondono più).
  • Un use-after-free pointer avrà quindi un tag stale e mismatch quando viene accesso.
  1. Neighbor-tag differentiation to catch overflows
  • Allocazioni adiacenti ricevono tag distinti. Se un buffer overflow si riversa nella memory del neighbor, il tag mismatch causa un fault.
  • Questo è particolarmente efficace nel catturare piccoli overflows che attraversano il boundary.
  1. Tag confidentiality enforcement
  • Apple deve prevenire che i tag values vengano leaked (perché se un attacker learns il tag, potrebbe craftare pointer con i correct tags).
  • Includono protezioni (microarchitectural / speculative controls) per evitare side-channel leakage dei tag bits.
  1. Kernel and user-space integration
  • Apple usa EMTE non solo in user-space ma anche in componenti kernel / OS-critical (per proteggere il kernel da memory corruption).
  • L’hardware/OS assicura che le regole dei tag si applichino anche quando il kernel esegue per conto di user space.
Esempio ``` 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>

#### Limitazioni & sfide

- **Overflow intrablocco**: Se l'overflow rimane nella stessa allocazione (non attraversa il boundary) e il tag resta lo stesso, il tag mismatch non lo rileva.
- **Limitazione della larghezza del tag**: Solo pochi bit (es. 4 bit, o un piccolo dominio) sono disponibili per il tag—namespace limitato.
- **Side-channel leaks**: Se i bit del tag possono essere leaked (via cache / speculative execution), l'attaccante può apprendere i tag validi e bypassare. Apple’s tag confidentiality enforcement è pensata per mitigare questo.
- **Overhead di performance**: I check del tag ad ogni load/store aggiungono costo; Apple deve ottimizzare l'hardware per ridurre l'overhead.
- **Compatibilità & fallback**: Su hardware più vecchio o parti che non supportano EMTE, deve esistere un fallback. Apple dichiara che MIE è abilitato solo su dispositivi con supporto.
- **Logica complessa dell'allocator**: L'allocator deve gestire i tag, il retagging, l'allineamento dei boundary e evitare collisioni di mis-tag. Bug nella logica dell'allocator potrebbero introdurre vulnerabilità.
- **Memoria mista / aree ibride**: Parte della memoria potrebbe rimanere senza tag (legacy), rendendo l'interoperabilità più complessa.
- **Attacchi speculativi / transient**: Come con molte protezioni microarchitetturali, speculative execution o micro-op fusions potrebbero bypassare i check in modo transitorio o leakare bit di tag.
- **Limitato a regioni supportate**: Apple potrebbe far valere EMTE solo in aree selezionate e ad alto rischio (kernel, subsistemi critici per la sicurezza), non universalmente.



---

## Miglioramenti chiave / differenze rispetto a MTE standard

Ecco i miglioramenti e i cambiamenti su cui Apple pone enfasi:

| Feature | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Supports synchronous and asynchronous modes. In async, tag mismatches are reported later (delayed)| Apple insists on **synchronous mode** by default—tag mismatches are caught immediately, no delay/race windows allowed.|
| **Coverage of non-tagged memory** | Accesses to non-tagged memory (e.g. globals) may bypass checks in some implementations | EMTE requires that accesses from a tagged region to non-tagged memory also validate tag knowledge, making it harder to bypass by mixing allocations.|
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Apple adds **Tag Confidentiality Enforcement**, which attempts to prevent leakage of tag values (via speculative side-channels etc.).|
| **Allocator integration & retagging** | MTE leaves much of allocator logic to software | Apple’s secure typed allocators (kalloc_type, xzone malloc, etc.) integrate with EMTE: when memory is allocated or freed, tags are managed at fine granularity.|
| **Always-on by default** | In many platforms, MTE is optional or off by default | Apple enables EMTE / MIE by default on supported hardware (e.g. iPhone 17 / A19) for kernel and many user processes.|

Poiché Apple controlla sia l'hardware sia lo stack software, può far rispettare EMTE in modo stringente, evitare penalità di performance e chiudere buchi dovuti a side-channel.

---

## Come EMTE funziona in pratica (Apple / MIE)

Di seguito una descrizione ad alto livello di come EMTE opera sotto la configurazione MIE di Apple:

1. **Assegnazione del tag**
- Quando la memoria viene allocata (es. in kernel o user space tramite secure allocators), a quel blocco viene assegnato un **tag segreto**.
- Il puntatore restituito all'utente o al kernel include quel tag nei bit più alti (usando TBI / top byte ignore mechanisms).

2. **Verifica del tag all'accesso**
- Ogni volta che viene eseguito un load o store usando un puntatore, l'hardware verifica che il tag del puntatore corrisponda al tag del blocco di memoria (allocation tag). In caso di mismatch, genera un fault immediatamente (essendo sincrono).
- Poiché è sincrono, non esiste una finestra di rilevamento ritardato.

3. **Retagging alla free / riuso**
- Quando la memoria viene liberata (free), l'allocator cambia il tag del blocco (così i puntatori vecchi con tag obsoleti non corrispondono più).
- Un use-after-free avrà quindi un tag stale e darà mismatch quando viene usato.

4. **Differenziazione dei tag dei vicini per intercettare overflows**
- Le allocazioni adiacenti ricevono tag distinti. Se un buffer overflow straborda nella memoria del vicino, il tag mismatch causa un fault.
- Questo è particolarmente efficace nel catturare piccoli overflows che attraversano boundary.

5. **Applicazione della riservatezza dei tag**
- Apple deve impedire che i valori dei tag vengano leakati (perché se l'attaccante conosce il tag, potrebbe costruire puntatori con tag corretti).
- Include protezioni (controlli microarchitetturali / speculative) per evitare leakage dei bit di tag.

6. **Integrazione kernel e user-space**
- Apple usa EMTE non solo in user-space ma anche in componenti critici del kernel/OS (per proteggere il kernel da corruzioni di memoria).
- L'hardware/OS assicura che le regole sui tag siano applicate anche quando il kernel agisce per conto dello user space.

Poiché EMTE è integrato in MIE, Apple lo usa in modalità sincrona sulle superfici d'attacco chiave, non come opzione o modalità di debugging.



---

## Exception handling in XNU

Quando si verifica un'**exception** (es., `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, ecc.), il layer Mach del kernel XNU è responsabile di intercettarla prima che diventi un segnale in stile UNIX (come `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).

Questo processo coinvolge più livelli di propagazione e gestione delle eccezioni prima di raggiungere lo user space o di essere convertito in un BSD signal.


### Flusso delle eccezioni (Panoramica)

1.  **La CPU genera un'eccezione sincrona** (es., dereferenziazione di un puntatore invalido, PAC failure, istruzione illegale, ecc.).

2.  **Il low-level trap handler** viene eseguito (`trap.c`, `exception.c` nel sorgente XNU).

3.  Il trap handler chiama **`exception_triage()`**, il cuore della gestione delle Mach exception.

4.  `exception_triage()` decide come instradare l'eccezione:

-   Prima alla **thread's exception port**.

-   Poi alla **task's exception port**.

-   Poi alla **host's exception port** (spesso `launchd` o `ReportCrash`).

Se nessuna di queste porte gestisce l'eccezione, il kernel può:

-   **Convertirla in un BSD signal** (per i processi in user-space).

-   **Effettuare un panic** (per eccezioni in kernel-space).


### Funzione core: `exception_triage()`

La funzione `exception_triage()` instrada le Mach exceptions lungo la catena di possibili handler finché uno non la gestisce o finché diventa fatale. È definita in `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);

Flusso di chiamate tipico:

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

Se tutte falliscono → gestito da bsd_exception() → tradotto in un segnale come SIGSEGV.

Exception Ports

Ogni oggetto Mach (thread, task, host) può registrare exception ports, verso cui vengono inviati i messaggi di eccezione.

Sono definite dall’API:

task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()

Each exception port has:

  • A mask (which exceptions it wants to receive)
  • A port name (Mach port to receive messages)
  • A behavior (how the kernel sends the message)
  • A flavor (which thread state to include)

Debuggers and Exception Handling

A debugger (e.g., LLDB) sets an exception port on the target task or thread, usually using task_set_exception_ports().

When an exception occurs:

  • The Mach message is sent to the debugger process.
  • The debugger can decide to handle (resume, modify registers, skip instruction) or not handle the exception.
  • If the debugger doesn’t handle it, the exception propagates to the next level (task → host).

Flow of EXC_BAD_ACCESS

  1. Thread dereferences invalid pointer → CPU raises Data Abort.

  2. Kernel trap handler calls exception_triage(EXC_BAD_ACCESS, ...).

  3. Message sent to:

  • Thread port → (debugger can intercept breakpoint).

  • If debugger ignores → Task port → (process-level handler).

  • If ignored → Host port (usually ReportCrash).

  1. If no one handles → bsd_exception() translates to SIGSEGV.

PAC Exceptions

When Pointer Authentication (PAC) fails (signature mismatch), a special Mach exception is raised:

  • EXC_ARM_PAC (type)
  • Codes may include details (e.g., key type, pointer type).

If the binary has the flag TFRO_PAC_EXC_FATAL, the kernel treats PAC failures as fatal, bypassing debugger interception. This is to prevent attackers from using debuggers to bypass PAC checks and it’s enabled for platform binaries.

Software Breakpoints

A software breakpoint (int3 on x86, brk on ARM64) is implemented by causing a deliberate fault.
The debugger catches this via the exception port:

  • Modifies instruction pointer or memory.
  • Restores original instruction.
  • Resumes execution.

This same mechanism is what allows you to “catch” a PAC exception — unless TFRO_PAC_EXC_FATAL is set, in which case it never reaches the debugger.

Conversion to BSD Signals

If no handler accepts the exception:

  • Kernel calls task_exception_notify() → bsd_exception().

  • This maps Mach exceptions to signals:

Mach ExceptionSegnale
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 → Core of exception_triage(), exception_deliver_*().

  • bsd/kern/kern_sig.c → Signal delivery logic.

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

  • osfmk/mach/exc.h → Exception codes and structures.

  • osfmk/kern/task.c → Task exception port setup.


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

The kernel used a zone allocator (kalloc) divided into fixed-size “zones.” Each zone only stores allocations of a single size class.

From the screenshot:

Zone NameElement SizeExample Use
default.kalloc.1616 bytesVery small kernel structs, pointers.
default.kalloc.3232 bytesSmall structs, object headers.
default.kalloc.6464 bytesIPC messages, tiny kernel buffers.
default.kalloc.128128 bytesMedium objects like parts of OSObject.
default.kalloc.12801280 bytesLarge structures, IOSurface/graphics metadata.

How it worked:

  • Each allocation request gets rounded up to the nearest zone size. (E.g., a 50-byte request lands in the kalloc.64 zone).
  • Memory in each zone was kept in a freelist — chunks freed by the kernel went back into that zone.
  • If you overflowed a 64-byte buffer, you’d overwrite the next object in the same zone.

This is why heap spraying / feng shui was so effective: you could predict object neighbors by spraying allocations of the same size class.

The freelist

Inside each kalloc zone, freed objects weren’t returned directly to the system — they went into a freelist, a linked list of available chunks.

  • When a chunk was freed, the kernel wrote a pointer at the start of that chunk → the address of the next free chunk in the same zone.

  • The zone kept a HEAD pointer to the first free chunk.

  • Allocation always used the current HEAD:

  1. Pop HEAD (return that memory to the caller).

  2. Update HEAD = HEAD->next (stored in the freed chunk’s header).

  • Freeing pushed chunks back:

  • freed_chunk->next = HEAD

  • HEAD = freed_chunk

So the freelist was just a linked list built inside the freed memory itself.

Normal state:

Zone page (64-byte chunks for example):
[ A ] [ F ] [ F ] [ A ] [ F ] [ A ] [ F ]

Freelist view:
HEAD ──► [ F ] ──► [ F ] ──► [ F ] ──► [ F ] ──► NULL
(next ptrs stored at start of freed chunks)

Sfruttare la freelist

Poiché i primi 8 byte di un free chunk = freelist pointer, un attacker potrebbe corromperlo:

  1. Heap overflow in un freed chunk adiacente → sovrascriverne il “next” pointer.

  2. Use-after-free write in un freed object → sovrascrivere il suo “next” pointer.

Poi, alla successiva allocazione di quella dimensione:

  • L’allocator estrae il chunk corrotto.
  • Segue il “next” pointer fornito dall’attacker.
  • Restituisce un pointer a memoria arbitraria, permettendo fake object primitives o targeted overwrite.

Esempio visivo di 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.

Questo design del freelist rese l’exploitation altamente efficace prima dell’hardening: vicini prevedibili dovuti a heap sprays, raw pointer freelist links, e l’assenza di separazione per tipo permetteva agli attaccanti di escalare bug UAF/overflow fino al controllo arbitrario della memoria del kernel.

Heap Grooming / Feng Shui

The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
In questo modo, quando avviene la corruzione di memoria, l’attaccante può sovrascrivere in modo affidabile l’oggetto vittima con dati controllati.

Steps:

  1. Spray allocations (fill the holes)
  • Col tempo, il kernel heap si frammenta: alcune zone presentano buchi dove vecchi oggetti sono stati freed.
  • L’attaccante prima effettua molte allocazioni dummy per riempire queste lacune, così l’heap diventa “compattato” e prevedibile.
  1. Force new pages
  • Una volta riempiti i buchi, le allocazioni successive devono essere tratte da nuove pagine aggiunte alla zone.
  • Pagine fresche significano che gli oggetti saranno raggruppati insieme, non sparsi su memoria frammentata.
  • Questo conferisce all’attaccante un controllo molto migliore sui neighbors.
  1. Place attacker objects
  • L’attaccante ora esegue di nuovo uno spray, creando molti oggetti controllati dall’attaccante in quelle nuove pagine.
  • Questi oggetti sono prevedibili in dimensione e posizione (dato che appartengono tutti alla stessa zone).
  1. Free a controlled object (make a gap)
  • L’attaccante libera deliberatamente uno dei propri oggetti.
  • Questo crea un “buco” nell’heap, che l’allocator riutilizzerà per la prossima allocazione di quella dimensione.
  1. Victim object lands in the hole
  • L’attaccante induce il kernel ad allocare l’oggetto vittima (quello che vuole corrompere).
  • Poiché il buco è la prima slot disponibile nel freelist, la vittima viene posizionata esattamente dove l’attaccante aveva liberato il proprio oggetto.
  1. Overflow / UAF into victim
  • Ora l’attaccante ha oggetti controllati intorno alla vittima.
  • Sovraccaricando da uno dei propri oggetti (o riusando uno freed), può sovrascrivere in modo affidabile i campi di memoria della vittima con valori scelti.

Perché funziona:

  • Predictability dell’allocator di zone: le allocazioni della stessa dimensione provengono sempre dalla stessa zone.
  • Comportamento del freelist: le nuove allocazioni riutilizzano per prime i chunk più recentemente freed.
  • Heap sprays: l’attaccante riempie la memoria con contenuti prevedibili e controlla il layout.
  • Risultato finale: l’attaccante controlla dove l’oggetto vittima viene allocato e quali dati stanno accanto ad esso.

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

Apple ha rinforzato l’allocator e ha reso il heap grooming molto più difficile:

1. From Classic kalloc to kalloc_type

  • Before: esisteva una singola zone kalloc.<size> per ogni classe di dimensione (16, 32, 64, … 1280, ecc.). Qualsiasi oggetto di quella dimensione veniva posizionato lì → gli oggetti dell’attaccante potevano trovarsi accanto ad oggetti kernel privilegiati.
  • Now:
  • Gli oggetti kernel sono allocati da typed zones (kalloc_type).
  • Ogni tipo di oggetto (es., ipc_port_t, task_t, OSString, OSData) ha la sua zone dedicata, anche se hanno la stessa dimensione.
  • La mappatura tra object type ↔ zone è generata dal kalloc_type system a compile time.

Un attaccante non può più garantire che dati controllati (OSData) finiscano adiacenti a oggetti kernel sensibili (task_t) della stessa dimensione.

2. Slabs and Per-CPU Caches

  • L’heap è diviso in slabs (pagine di memoria suddivise in chunk di dimensione fissa per quella zone).
  • Ogni zone ha una per-CPU cache per ridurre la contention.
  • Percorso di allocazione:
  1. Provare la per-CPU cache.
  2. Se vuota, prendere dal global freelist.
  3. Se il freelist è vuoto, allocare una nuova slab (una o più pagine).
  • Vantaggio: questa decentralizzazione rende gli heap sprays meno deterministici, dato che le allocazioni possono essere soddisfatte dalle cache di CPU diverse.

3. Randomization inside zones

  • All’interno di una zone, gli elementi freed non vengono restituiti in semplice ordine FIFO/LIFO.
  • Il moderno XNU usa encoded freelist pointers (safe-linking come in Linux, introdotto ~iOS 14).
  • Ogni freelist pointer è XOR-encoded con un cookie segreto per zona.
  • Questo impedisce agli attaccanti di forgiare un fake freelist pointer se ottengono un write primitive.
  • Alcune allocazioni sono randomizzate nella loro collocazione all’interno di una slab, quindi lo spraying non garantisce più l’adiacenza.

4. Guarded Allocations

  • Certi oggetti kernel critici (es., credentials, strutture task) sono allocati in guarded zones.
  • Queste zone inseriscono guard pages (memoria non mappata) tra slabs o usano redzones intorno agli oggetti.
  • Qualsiasi overflow nella guard page scatena un fault → panic immediato invece di corruzione silenziosa.

5. Page Protection Layer (PPL) and SPTM

  • Anche se controlli un oggetto freed, non puoi modificare tutta la memoria del kernel:
  • PPL (Page Protection Layer) impone che certe regioni (es., code signing data, entitlements) siano read-only anche per il kernel stesso.
  • Su dispositivi A15/M2+, questo ruolo è sostituito/rafforzato da SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
  • Questi strati hardware-enforced impediscono agli attaccanti di scalare da una singola corruzione heap al patching arbitrario di strutture di sicurezza critiche.
  • (Added / Enhanced): inoltre, PAC (Pointer Authentication Codes) è usato nel kernel per proteggere i puntatori (specialmente function pointers, vtables) in modo che forgiare o corromperli sia più difficile.
  • (Added / Enhanced): le zone possono applicare zone_require / zone enforcement, cioè che un oggetto freed può essere restituito solo attraverso la sua typed zone corretta; cross-zone frees invalide possono causare panic o essere rifiutate. (Apple accenna a questo nei loro post sulla memory safety)

6. Large Allocations

  • Non tutte le allocazioni passano per kalloc_type.
  • Richieste molto grandi (sopra ~16 KB) bypassano le typed zones e sono servite direttamente da kernel VM (kmem) tramite allocazioni di pagine.
  • Queste sono meno prevedibili, ma anche meno sfruttabili, dato che non condividono slabs con altri oggetti.

7. Allocation Patterns Attackers Target

Anche con queste protezioni, gli attaccanti cercano ancora:

  • Reference count objects: se puoi manipolare i contatori retain/release, puoi causare use-after-free.
  • Objects with function pointers (vtables): corromperne uno può comunque portare a control flow.
  • Shared memory objects (IOSurface, Mach ports): restano target perché fungono da ponte user ↔ kernel.

Ma — a differenza di prima — non puoi semplicemente sprayare OSData e aspettarti che stia vicino a un task_t. Hai bisogno di bug specifici di tipo o info leaks per avere successo.

Example: Allocation Flow in Modern Heap

Supponiamo che userspace chiami IOKit per allocare un oggetto OSData:

  1. Type lookupOSData mappa alla zone kalloc_type_osdata (size 64 bytes).
  2. Controlla la per-CPU cache per elementi liberi.
  • Se trovato → restituisci uno.
  • Se vuoto → vai al global freelist.
  • Se il freelist è vuoto → allocare una nuova slab (pagina di 4KB → 64 chunk da 64 bytes).
  1. Restituire il chunk al chiamante.

Protezione dei freelist pointer:

  • Ogni chunk freed memorizza l’indirizzo del prossimo chunk libero, ma codificato con una chiave segreta.
  • Sovrascrivere quel campo con dati controllati dall’attaccante non funzionerà a meno di conoscere la chiave.

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)

Nelle versioni più recenti dei sistemi Apple (specialmente iOS 17+), Apple ha introdotto un allocator userland più sicuro, xzone malloc (XZM). Questo è l’analogo user-space del kalloc_type del kernel, applicando awareness per tipo, isolamento dei metadata e salvaguardie di memory tagging.

Goals & Design Principles

  • Type segregation / type awareness: raggruppare le allocazioni per tipo o uso (pointer vs data) per prevenire type confusion e riuso cross-type.
  • Metadata isolation: separare i metadata dell’heap (es. free lists, bit di size/state) dal payload degli oggetti così che scritture out-of-bounds abbiano meno probabilità di corrompere i metadata.
  • Guard pages / redzones: inserire pagine non mappate o padding intorno alle allocazioni per intercettare overflow.
  • Memory tagging (EMTE / MIE): lavorare insieme al tagging hardware per rilevare use-after-free, out-of-bounds e accessi invalidi.
  • Scalable performance: mantenere basso overhead, evitare frammentazione e supportare molte allocazioni al secondo con bassa latenza.

Architecture & Components

Di seguito gli elementi principali nell’allocator xzone:

Segment Groups & Zones

  • Segment groups partizionano lo spazio degli indirizzi per categorie di uso: es. data, pointer_xzones, data_large, pointer_large.
  • Ogni segment group contiene segments (range VM) che ospitano allocazioni per quella categoria.
  • Associata a ogni segment c’è una metadata slab (area VM separata) che memorizza i metadata (es. bit free/used, size class) per quel segment. Questa out-of-line (OOL) metadata assicura che i metadata non siano mescolati con i payload degli oggetti, mitigando la corruzione da overflow.
  • I segments sono suddivisi in chunks (slices) che a loro volta sono scomposti in blocks (unità di allocazione). Un chunk è legato a una specifica size class e segment group (cioè tutti i block in un chunk condividono la stessa size & category).
  • Per allocazioni small/medium si usano chunk a dimensione fissa; per large/huge può fare map separatamente.

Chunks & Blocks

  • Un chunk è una regione (spesso varie pagine) dedicata alle allocazioni di una size class all’interno di un group.
  • Dentro un chunk, i blocks sono slot disponibili per allocazioni. I blocks freed sono tracciati tramite la metadata slab — es. via bitmap o free lists memorizzate out-of-line.
  • Tra chunks (o al loro interno), possono essere inserite guard slices / guard pages (es. slices non mappate) per catturare scritture out-of-bounds.

Type / Type ID

  • Ogni sito di allocazione (o chiamata a malloc, calloc, ecc.) è associato a un type identifier (un malloc_type_id_t) che codifica che tipo di oggetto viene allocato. Quel type ID è passato all’allocator, che lo usa per selezionare quale zone / segment servire l’allocazione.
  • Per questo motivo, anche se due allocazioni hanno la stessa dimensione, possono andare in zone completamente diverse se i loro tipi differiscono.
  • Nelle prime versioni di iOS 17, non tutte le API (es. CFAllocator) erano pienamente type-aware; Apple ha corretto alcune di queste debolezze in iOS 18.

Allocation & Freeing Workflow

Ecco un flow ad alto livello di come operano allocazione e deallocazione in xzone:

  1. malloc / calloc / realloc / typed alloc viene invocato con una size e un type ID.
  2. L’allocator usa il type ID per scegliere il corretto segment group / zone.
  3. All’interno di quella zone/segment cerca un chunk che abbia free blocks della size richiesta.
  • Può consultare local caches / per-thread pools o free block lists dalla metadata.
  • Se non c’è nessun block libero, può allocare un nuovo chunk in quella zona.
  1. La metadata slab viene aggiornata (bit free cleared, bookkeeping).
  2. Se il memory tagging (EMTE) è attivo, il block restituito riceve un tag assegnato, e la metadata viene aggiornata per riflettere lo stato “live”.
  3. Quando viene chiamato free():
  • Il block viene marcato come freed nella metadata (via OOL slab).
  • Il block può essere messo in una free list o pooled per il riuso.
  • Opzionalmente, il contenuto del block può essere cancellato o poisoned per ridurre leak di dati o sfruttamento UAF.
  • Il tag hardware associato al block può essere invalidato o re-tagged.
  • Se un intero chunk diventa free (tutti i blocks freed), l’allocator può reclaim quel chunk (unmapparlo o restituirlo al OS) sotto memory pressure.

Security Features & Hardening

Queste sono le difese integrate nel moderno xzone:

FeaturePurposeNotes
Metadata decouplingPrevent overflow from corrupting metadataMetadata vive in una region VM separata (metadata slab)
Guard pages / unmapped slicesCatch out-of-bounds writesAiuta a rilevare buffer overflows invece di corrompere silenziosamente blocks adiacenti
Type-based segregationPrevent cross-type reuse & type confusionAnche same-size allocations di tipi diversi vanno in zone separate
Memory Tagging (EMTE / MIE)Detect invalid access, stale references, OOB, UAFxzone lavora in concerto con hardware EMTE in modalità sincrona (“Memory Integrity Enforcement”)
Delayed reuse / poisoning / zapReduce chance of use-after-free exploitationI blocks freed possono essere poisoned, azzerati, o messi in quarantena prima del riuso
Chunk reclamation / dynamic unmappingReduce memory waste and fragmentationInteri chunks possono essere unmapped quando inutilizzati
Randomization / placement variationPrevent deterministic adjacencyI blocks in un chunk e la selezione del chunk possono avere aspetti randomici
Segregation of “data-only” allocationsSeparate allocations that don’t store pointersRiduce il controllo dell’attaccante su metadata o campi di controllo

Interaction with Memory Integrity Enforcement (MIE / EMTE)

  • MIE (Memory Integrity Enforcement) di Apple è il framework hardware + OS che porta Enhanced Memory Tagging Extension (EMTE) in modalità always-on e sincrona sulle superfici di attacco principali.
  • L’allocator xzone è una base fondamentale di MIE in user space: le allocazioni fatte tramite xzone ricevono tag, e gli accessi sono controllati dall’hardware.
  • In MIE, l’allocator, l’assegnazione dei tag, la gestione della metadata e l’enforcement della confidenzialità dei tag sono integrati per assicurare che errori di memoria (es. stale reads, OOB, UAF) vengano rilevati immediatamente, non sfruttati in seguito.

Se vuoi, posso anche generare una cheat-sheet o un diagramma degli internals di xzone per il tuo libro. Vuoi che lo faccia dopo?
: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: Un binario watcher dedicato profila continuamente il dispositivo e interrompe la kill-chain quando viene rilevato un ambiente di ricerca. Ispeziona security.mac.amfi.developer_mode_status, la presenza di una console diagnosticd, le località US o IL, tracce di jailbreak come Cydia, processi come bash, tcpdump, frida, sshd, o checkrain, mobile AV apps (McAfee, AvastMobileSecurity, NortonMobileSecurity), impostazioni proxy HTTP personalizzate e root CA personalizzate. Il mancato superamento di qualsiasi controllo blocca la consegna di payload successivi.
  • Helper surveillance hooks: Il componente helper comunica con altre fasi attraverso /tmp/helper.sock, quindi carica set di hook chiamati DMHooker e UMHooker. Questi hook intercettano i percorsi audio VOIP (le registrazioni vengono salvate sotto /private/var/tmp/l/voip_%lu_%u_PART.m4a), implementano un keylogger a livello di sistema, scattano foto senza UI e agganciano SpringBoard per sopprimere le notifiche che tali azioni normalmente genererebbero. L’helper agisce quindi come uno strato furtivo di validazione e di sorveglianza leggera prima che vengano impiantati componenti più pesanti come Predator.

WebKit DFG Store-Barrier UAF + ANGLE PBO OOB (iOS 26.1)

Webkit Dfg Store Barrier Uaf Angle Oob

iMessage/Media Parser Zero-Click Chains

Imessage Media Parser Zero Click Coreaudio Pac Bypass

References

Tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks