Sfruttamento iOS
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
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Mitigazioni degli exploit iOS
1. Code Signing / Runtime Signature Verification
Introdotto nelle prime versioni (iPhone OS → iOS) Questa è una delle protezioni fondamentali: tutto il codice eseguibile (app, dynamic libraries, codice JIT, extension, framework, cache) deve essere firmato criptograficamente da una catena di certificati radicata nella trust di Apple. A runtime, prima di caricare un binario in memoria (o prima di effettuare salti attraverso certi confini), il sistema verifica la sua firma. Se il codice è stato modificato (bit-flipped, patched) o non è firmato, il caricamento fallisce.
- Blocca: la fase “classic payload drop + execute” nelle exploit chain; arbitrary code injection; modificare un binario esistente per inserire logica malevola.
- Dettagli del meccanismo:
- Il Mach-O loader (e il dynamic linker) controlla le code pages, i segmenti, le entitlements, i team IDs, e che la signature copra il contenuto del file.
- Per regioni di memoria come JIT caches o codice generato dinamicamente, Apple impone che le pagine siano firmate o validate tramite API speciali (es.
mprotectcon controlli code-sign). - La signature include entitlements e identificatori; l’OS impone che certe API o capacità privilegiate richiedano specifiche entitlements che non possono essere falsificate.
Example
Supponiamo che un exploit ottenga code execution in un processo e provi a scrivere shellcode nell’heap e a 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 quella regione di memoria eseguibile.2. CoreTrust
Introdotto intorno all’era iOS 14+ (o gradualmente su dispositivi più recenti / versioni successive di iOS) CoreTrust è il sottosistema che esegue la runtime signature validation dei binari (inclusi system e user binaries) contro il certificato root di Apple invece di fare affidamento su store di trust userland memorizzati in cache.
- Blocca: manomissioni post-install dei binari, tecniche di jailbreaking che cercano di sostituire o patchare system libraries o user app; ingannare il sistema sostituendo binari trusted con controparti malevole.
- Dettagli del meccanismo:
- Invece di fidarsi di un database di trust locale o di una cache di certificati, CoreTrust recupera o si riferisce direttamente al root di Apple o verifica certificati intermedi in una catena sicura.
- Assicura che modifiche (es. nel filesystem) a binari esistenti vengano rilevate e rifiutate.
- Lega entitlements, team IDs, code signing flags e altra metadata al binario al momento del load.
Example
Un jailbreak potrebbe provare a sostituire `SpringBoard` o `libsystem` con una versione patchata per ottenere persistenza. Ma quando il loader dell’OS o CoreTrust verifica, nota la mismatched signature (o entitlements modificati) e rifiuta di eseguire.3. Data Execution Prevention (DEP / NX / W^X)
Introdotto in molti OS precedentemente; iOS ha NX-bit / w^x da molto tempo DEP impone che le pagine marcate writable (per dati) siano non-executable, e le pagine marcate executable siano non-writable. Non si può semplicemente scrivere shellcode in una regione heap o stack e eseguirlo.
- Blocca: esecuzione diretta di shellcode; buffer-overflow classico → salto a shellcode iniettato.
- Dettagli del meccanismo:
- L’MMU / i flags di protezione della memoria (tramite page tables) impongono la separazione.
- Qualsiasi tentativo di marcare una pagina writable come executable innesca un controllo di sistema (ed è o proibito o richiede approvazione code-sign).
- In molti casi, rendere le pagine executable richiede passare tramite API dell’OS che impongono vincoli o controlli aggiuntivi.
Example
Un overflow scrive shellcode sull’heap. L’attaccante tenta `mprotect(heap_addr, size, PROT_EXEC)` per renderla eseguibile. Ma il sistema rifiuta o valida che la nuova pagina deve superare vincoli di code-sign (cosa che lo shellcode non può fare).4. Address Space Layout Randomization (ASLR)
Introdotto in iOS ~4–5 era (circa iOS 4–5) ASLR randomizza gli indirizzi base di regioni chiave di memoria: libraries, heap, stack, ecc., ad ogni avvio del processo. Gli indirizzi dei gadget si spostano tra esecuzioni.
- Blocca: hardcoding di indirizzi di gadget per ROP/JOP; exploit chain statiche; saltare cieco a offset conosciuti.
- Dettagli del meccanismo:
- Ogni library / modulo dinamico caricato viene ribasato 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.
- In combinazione con mitigazioni contro information-leak, obbliga l’attaccante a prima leakare un indirizzo o un puntatore per scoprire gli base address a runtime.
Example
Una ROP chain si aspetta un gadget a `0x….lib + offset`. Ma dato che `lib` è rilocata diversamente a ogni esecuzione, la chain hardcoded 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)
Introdotto in iOS ~ (era iOS 5 / iOS 6) Analogamente alla ASLR user, KASLR randomizza la base del kernel text e altre strutture kernel al boot.
- Blocca: exploit a livello kernel che fanno affidamento su posizioni fisse di codice o dati kernel; exploit statici del kernel.
- Dettagli del meccanismo:
- Ad ogni boot, l’indirizzo base del kernel viene randomizzato (entro un range).
- Strutture dati kernel (come
task_structs,vm_map, ecc.) possono essere anche rilocate o cambiate di offset. - Gli attaccanti devono prima leakare puntatori kernel o usare vulnerabilità di information disclosure per calcolare offset corretti prima di hijackare strutture o codice kernel.
Example
Una vulnerabilità locale mira a corrompere un kernel function pointer (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 da corrompere.6. Kernel Patch Protection (KPP / AMCC)
Introdotto in iOS più recenti / hardware A-series (post circa iOS 15–16 o chip più recenti) KPP (aka AMCC) monitora continuamente l’integrità delle kernel text pages (tramite hash o checksum). Se rileva manomissioni (patch, inline hooks, modifiche al codice) al di fuori delle finestre consentite, innesca un kernel panic o un reboot.
- Blocca: patch persistenti del kernel (modifica di istruzioni kernel), inline hooks, sovrascritture statiche di funzioni.
- Dettagli del meccanismo:
- Un modulo hardware o firmware monitora la regione kernel text.
- Periodicamente o su richiesta ricalcola gli hash delle pagine e confronta con i valori attesi.
- Se si verificano mismatch al di fuori di finestre di update benigno, fa panic al dispositivo (per evitare patch malevole persistenti).
- Gli attaccanti devono 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 pagina di codice non corrisponde più al valore atteso e innesca un kernel panic, crashando il dispositivo prima che la patch possa stabilizzarsi.7. Kernel Text Read‐Only Region (KTRR)
Introdotto in SoC moderni (post ~A12 / hardware più recenti) KTRR è un meccanismo hardware-enforced: una volta che il kernel text viene locked early durante il boot, diventa read-only da EL1 (il kernel), impedendo ulteriori scritture sulle code pages.
- Blocca: qualsiasi modifica al codice kernel dopo il boot (es. patching, code injection in-place) al livello di privilegio EL1.
- Dettagli del meccanismo:
- Durante il boot (nella fase secure/bootloader), il memory controller (o un’unità hardware sicura) marca le physical pages contenenti 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 privilege-escalation salta in EL1 e prova a scrivere un trampoline in una funzione kernel (es. nel handler `syscall`). Ma poiché le pagine sono lockate come read-only da KTRR, la scrittura fallisce (o innesca fault), quindi le patch non vengono applicate.8. Pointer Authentication Codes (PAC)
Introdotto con ARMv8.3 (hardware), Apple a partire da A12 / iOS ~12+
- PAC è una feature hardware introdotta in ARMv8.3-A per rilevare la manomissione di valori di puntatore (return addresses, function pointers, certi data pointers) incorporando una piccola firma crittografica (un “MAC”) nei bit alti non usati del puntatore.
- La signature (“PAC”) è calcolata sul valore del puntatore più un modifier (un valore di contesto, es. stack pointer o qualche dato distinguibile). In questo modo lo stesso valore di puntatore in contesti diversi ottiene una PAC diversa.
- 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 non valida, il puntatore diventa “poisoned” (o viene sollevato un fault).
- Le chiavi usate per produrre/validare le PAC vivono in registri privilegiati (EL1, kernel) e non sono direttamente leggibili da user mode.
- Poiché non tutti i 64 bit di un puntatore sono usati in molti sistemi (es. address space a 48-bit), i bit superiori sono “spare” e possono contenere la PAC senza alterare l’indirizzo effettivo.
Fondamento architetturale & tipi di chiavi
-
ARMv8.3 introduce cinque chiavi a 128-bit (ognuna implementata tramite due registri di sistema a 64-bit) per pointer authentication.
-
APIAKey — per instruction pointers (dominio “I”, chiave A)
-
APIBKey — seconda chiave per instruction pointers (dominio “I”, chiave B)
-
APDAKey — per data pointers (dominio “D”, chiave A)
-
APDBKey — per data pointers (dominio “D”, chiave B)
-
APGAKey — chiave “generic”, per firmare dati non-pointer o altri usi generici
-
Queste chiavi sono memorizzate in registri di sistema privilegiati (accessibili solo a EL1/EL2 ecc.), non accessibili da user mode.
-
Il PAC è calcolato tramite una funzione crittografica (ARM suggerisce QARMA come algoritmo) usando:
- Il valore del puntatore (porzione canonica)
- Un modifier (un valore di contesto, come un salt)
- La chiave segreta
- Alcuna logica di tweak interna Se la PAC risultante corrisponde a quella memorizzata nei bit alti del puntatore, l’autenticazione ha successo.
Famiglie di istruzioni
La convenzione di naming è: PAC / AUT / XPAC, poi le lettere di dominio.
PACxxistruzioni firmano un puntatore e inseriscono una PACAUTxxistruzioni autenticano + strip (validano e rimuovono la PAC)XPACxxistruzioni rimuovono senza validare
Domini / suffissi:
| Mnemonic | Significato / Dominio | Chiave / Dominio | Esempio d’uso in Assembly |
|---|---|---|---|
| PACIA | Firma instruction pointer con APIAKey | “I, A” | PACIA X0, X1 — firma il puntatore in X0 usando APIAKey con modifier X1 |
| PACIB | Firma instruction pointer con APIBKey | “I, B” | PACIB X2, X3 |
| PACDA | Firma data pointer con APDAKey | “D, A” | PACDA X4, X5 |
| PACDB | Firma data pointer con APDBKey | “D, B” | PACDB X6, X7 |
| PACG / PACGA | Firma generica (non-pointer) con APGAKey | “G” | PACGA X8, X9, X10 (firma X9 con modifier X10 in X8) |
| AUTIA | Autentica instruction pointer APIA & strip PAC | “I, A” | AUTIA X0, X1 — controlla la PAC su X0 usando modifier X1, poi la rimuove |
| AUTIB | Autentica dominio APIB | “I, B” | AUTIB X2, X3 |
| AUTDA | Autentica data pointer APDA | “D, A” | AUTDA X4, X5 |
| AUTDB | Autentica data pointer APDB | “D, B” | AUTDB X6, X7 |
| AUTGA | Autentica generico / blob (APGA) | “G” | AUTGA X8, X9, X10 (valida generic) |
| XPACI | Rimuovi PAC (instruction pointer, senza validazione) | “I” | XPACI X0 — rimuove PAC da X0 (dominio instruction) |
| XPACD | Rimuovi PAC (data pointer, senza validazione) | “D” | XPACD X4 — rimuove PAC dal data pointer in X4 |
Esistono forme specializzate / alias:
PACIASPè shorthand perPACIA X30, SP(firma il link register usando SP come modifier)AUTIASPèAUTIA X30, SP(autentica il link register con SP)- Forme combinate come
RETAA,RETAB(authenticate-and-return) oBLRAA(authenticate & branch) esistono nelle estensioni ARM / supporto del compiler. - Anche varianti a modifier zero:
PACIZA/PACIZBdove il modifier è implicitamente zero, ecc.
Modifiers
Lo scopo principale del modifier è di legare la PAC a un contesto specifico così che lo stesso indirizzo firmato in contesti diversi produca PAC diverse. Questo previene il semplice reuse di puntatori tra frame o oggetti. È come aggiungere un salt a un hash.
Quindi:
- Il modifier è un valore di contesto (un altro registro) che viene mischiato nella computazione 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 legata al frame di stack specifico. Se si tenta di riusare LR in un frame diverso, il modifier cambia, quindi la validazione PAC fallisce.
- Lo stesso valore di puntatore firmato con modifier diversi produce PAC diverse.
- Il modifier non deve essere segreto, ma idealmente non è controllato dall’attaccante.
- Per istruzioni che firmano o verificano puntatori dove non esiste un modifier significativo, certe forme usano zero o una costante implicita.
Personalizzazioni & osservazioni Apple / iOS / XNU
- L’implementazione PAC di Apple include diversificatori per boot così che le chiavi o i tweak cambino ad ogni boot, impedendo il riuso cross-boot.
- Includono anche mitigazioni cross-domain così che PAC firmate in user mode non possano essere facilmente riusate in kernel mode, ecc.
- Su Apple M1 / Apple Silicon, il reverse engineering ha mostrato che esistono 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 pointer in dati kernel, contesti thread firmati, ecc.
- Google Project Zero ha mostrato come, sotto una potente primitive di read/write in kernel, si potessero forgiare PAC kernel (per le chiavi A) su dispositivi A12-era, ma Apple ha patchato molte di quelle vie.
- Nel sistema Apple, alcune chiavi sono globali nel kernel, mentre i processi utente possono ottenere randomness per chiavi per-process.
Bypass di PAC
- Kernel-mode PAC: teorico vs bypass reali
- 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 ha trovato alcuni bypass parziali (signing gadgets, reuse di signed states, indirect branches non protette) ma nessun bypass generico completo. bazad.github.io
- Le personalizzazioni “Dark Magic” di Apple riducono ulteriormente le surface exploitable (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 si basano su gadget molto specifici o bug di implementazione; non sono bypass di uso generale.
Quindi il kernel PAC è considerato molto robusto, sebbene non perfetto.
- Bypass PAC in user-mode / runtime
Questi sono più comuni, e sfruttano imperfezioni in come PAC viene applicata o usata 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ì largamente condiviso, function pointers all’interno della shared cache sono “pre-signed” e poi usati da molti processi. Gli attaccanti prendono di mira questi puntatori già firmati come “PAC oracles”.
-
Alcuni bypass cercano di estrarre o riusare puntatori firmati con A-key presenti nella shared cache e riutilizzarli in gadget.
-
Il talk “No Clicks Required” descrive la costruzione di un oracle sulla shared cache per dedurre address relativi e combinarli con puntatori firmati per bypassare PAC. saelo.github.io
-
Inoltre, 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édlsymrestituisce un puntatore legittimamente firmato, usarlo aggira la necessità di forgiare la PAC. -
Il blog di Epsilon dettagli come alcuni bypass sfruttano questo: chiamare
dlsym("someSym")restituisce 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
dlsymsu iOS 18.4 restituiscono puntatori che sono firmati in modo incorretto (o con diversificatori buggy), permettendo un bypass involontario 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 Altre relocation DYLD / runtime
-
Il loader DYLD e la logica di dynamic relocation sono complesse e a volte mappano temporaneamente pagine come read/write per effettuare relocations, poi le rimettono read-only. Gli attaccanti sfruttano queste finestre. Il talk di Synacktiv descrive “Operation Triangulation”, un bypass basato su timing delle PAC via relocations 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 exploit chain WebKit, il loader DYLD è spesso un target per PAC bypass. 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 exploit chain userland, runtime Objective-C come
NSPredicate,NSExpressionoNSInvocationvengono 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, la tecnica richiede adattamenti. Ma la tecnica SLOP (SeLector Oriented Programming) è stata estesa anche sotto PAC. Project Zero
-
La tecnica originale SLOP permetteva di concatenare chiamate ObjC creando fake invocations; 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 per bypass.
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>Esempio</summary>
A 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 della mancata corrispondenza del PAC. La catena fallisce.
L'analisi di Project Zero su A12 (iPhone XS) ha mostrato come viene usato il PAC di Apple e metodi per forgiare PAC se un attacker ha un primitivo di read/write di memoria.
</details>
### 9. **Branch Target Identification (BTI)**
**Introdotto con ARMv8.5 (hardware più recente)**
BTI è una funzionalità hardware che verifica i target di branch indiretti: quando si esegue `blr` o chiamate/salti indiretti, il target deve iniziare con un BTI landing pad (`BTI j` o `BTI c`). Saltare in indirizzi di gadget che non hanno il landing pad provoca un'eccezione.
L'implementazione di LLVM annota tre varianti di istruzioni BTI e come esse si mappano ai tipi di branch.
| BTI Variant | What it permits (which branch types) | Typical placement / use case |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Target delle branch indirette in stile *call* (es. `BLR`, o `BR` usando X16/X17) | Inserito all'entry di funzioni che possono essere chiamate indirettamente |
| **BTI J** | Target delle branch in stile *jump* (es. `BR` usato per tail calls) | Posizionato all'inizio di blocchi raggiungibili da jump tables o tail-calls |
| **BTI JC** | Agisce come C e J | Può essere targettato sia da branch di tipo call sia jump |
- Nel codice compilato con branch target enforcement, i compiler inseriscono un'istruzione BTI (C, J, o JC) in ogni target valido di indirect-branch (inizio di funzioni o blocchi raggiungibili da jump) così che le branch indirette abbiano successo solo verso quei punti.
- **Direct branches / calls** (cioè `B`, `BL` a indirizzo fisso) **non sono limitati** da BTI. L'assunzione è che le code pages siano trusted e l'attaccante non possa cambiarle (quindi le direct branches sono sicure).
- Inoltre, le istruzioni **RET / return** generalmente non sono soggette a restrizioni BTI perché gli indirizzi di return sono protetti via PAC o meccanismi di return signing.
#### Meccanismo e enforcement
- Quando la CPU decodifica una **indirect branch (BLR / BR)** in una pagina marcata come “guarded / BTI-enabled”, verifica se la prima istruzione dell'indirizzo target è un BTI valido (C, J, o JC come permesso). Se non lo è, si verifica una **Branch Target Exception**.
- L'encoding dell'istruzione BTI è progettato per riutilizzare opcode precedentemente riservati per NOPs (nelle versioni ARM precedenti). Quindi i binari BTI-enabled rimangono backward-compatible: su hardware senza supporto BTI, quelle istruzioni agiscono come NOP.
- I pass compiler che aggiungono BTI le inseriscono solo dove necessario: funzioni che possono essere chiamate indirettamente, o blocchi di base targettati da jump.
- 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 tables).
#### Sinergia BTI + PAC
PAC protegge il valore del puntatore (la source) — assicura che la catena di indirect calls / returns non sia stata manomessa.
BTI assicura che anche un puntatore valido debba puntare solo a entry point opportunamente marcati.
Combinati, un attacker ha bisogno sia di un puntatore valido con PAC corretto sia che il target abbia un BTI posizionato lì. Questo aumenta la difficoltà nella costruzione di gadget d'exploit.
#### Esempio
<details>
<summary>Esempio</summary>
Un exploit cerca di pivotare in un gadget a `0xABCDEF` che non inizia con `BTI c`. La CPU, quando esegue `blr x0`, controlla il target e fa fault perché l'allineamento dell'istruzione non include un valido 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)**
**Introdotto nelle estensioni ARMv8 più recenti / supporto iOS (per kernel hardened)**
#### PAN (Privileged Access Never)
- **PAN** è una funzionalità 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 sia esplicitamente disabilitato.
- L'idea: anche se il kernel è ingannato o compromesso, non può dereferenziare arbitrariamente puntatori user-space senza prima *clearare* PAN, riducendo così i rischi di exploit in stile **ret2usr** o uso improprio di buffer controllati dall'utente.
- Quando PAN è abilitato (PSTATE.PAN = 1), qualsiasi istruzione privilegiata di load/store che accede a un indirizzo virtuale “accessibile a EL0” causa un **permission fault**.
- Il kernel, quando deve legittimamente accedere alla memoria user-space (es. copiare dati da/a buffer utente), deve **disabilitare temporaneamente PAN** (o usare istruzioni di load/store non-privilegiate) per permettere quell'accesso.
- In Linux su ARM64, il supporto PAN è stato introdotto circa nel 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 di memoria utente.
**Sfumatura chiave / limitazione / bug**
- Come notato da Siguza e altri, un bug di specifica (o comportamento ambiguo) nel design ARM significa che le **execute-only user mappings** (`--x`) potrebbero **non innescare PAN**. In altre parole, se una pagina utente è marcata eseguibile ma senza permesso di lettura, il tentativo del kernel di leggerla potrebbe bypassare PAN perché l'architettura considera “accessible at EL0” come richiesto il permesso di lettura, non solo l'esecuzione. Questo porta a un bypass di PAN in certe configurazioni.
- A causa di ciò, se iOS / XNU permettono pagine utente execute-only (come potrebbero fare alcuni setup JIT o code-cache), il kernel potrebbe accidentalmente leggere da esse anche con PAN abilitato. Questa è un'area sottile e nota, sfruttabile in alcuni sistemi ARMv8+.
#### PXN (Privileged eXecute Never)
- **PXN** è un flag della page table (nelle voci 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 da pagine user-space anche se il controllo fosse deviato. Di fatto, impedisce l'esecuzione di codice user-space a livello kernel.
- 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 delle page table ARMv8, le voci leaf hanno un bit `PXN` (e anche `UXN` per unprivileged execute-never) nei loro attribute bits.
Quindi anche se il kernel ha un puntatore a funzione corrotto che punta alla memoria user, e tenta di brancharci, il bit PXN causerebbe un fault.
#### Modello di permessi di memoria & come PAN e PXN si mappano ai bit della page table
Per capire come PAN / PXN funzionano, è necessario vedere come funziona il modello di traduzione e permessi ARM (semplificato):
- Ogni entry di page o block ha campi di attributo inclusi **AP[2:1]** per i permessi di accesso (read/write, privileged vs unprivileged) e i bit **UXN / PXN** per le restrizioni execute-never.
- Quando PSTATE.PAN è 1 (abilitato), l'hardware applica semantiche modificate: gli accessi privilegiati a pagine marcate come “accessible by EL0” (cioè accessibili dall'utente) sono proibiti (fault).
- A causa del bug menzionato, le pagine marcate soltanto come eseguibili (nessun permesso di lettura) potrebbero non essere considerate “accessible by EL0” in alcune implementazioni, bypassando così PAN.
- Quando il bit PXN di una pagina è impostato, anche se il fetch delle istruzioni proviene da un livello di privilegio superiore, l'esecuzione è proibita.
#### Uso kernel di PAN / PXN in un OS hardening (es. iOS / XNU)
In un design di kernel hardenato (come quello che Apple potrebbe usare):
- Il kernel abilita PAN di default (quindi il codice privilegiato è vincolato).
- Nei percorsi che devono legittimamente leggere o scrivere buffer utente (es. copia di buffer di syscall, I/O, read/write di puntatori utente), il kernel disabilita temporaneamente **PAN** o usa istruzioni speciali per sovrascrivere.
- Dopo aver terminato l'accesso ai dati utente, deve riabilitare PAN.
- PXN è applicato tramite page tables: le pagine utente hanno PXN = 1 (quindi il kernel non può eseguirle), le pagine kernel non hanno PXN (quindi il codice kernel può essere eseguito).
- Il kernel deve assicurarsi che nessun percorso di codice causi flussi di esecuzione verso regioni di memoria utente (che aggirerebbero PXN) — quindi le catene di exploit che si basano su “saltare in shellcode controllato dall'utente” vengono bloccate.
A causa del bypass PAN tramite pagine execute-only, in un sistema reale Apple potrebbe disabilitare o non permettere pagine utente execute-only, o applicare patch per aggirare la debolezza di specifica.
#### Superfici d'attacco, bypass e mitigazioni
- **PAN bypass via execute-only pages**: come discusso, la specifica lascia un gap: pagine utente con solo execute (nessun permesso di lettura) potrebbero non essere considerate “accessible at EL0”, quindi PAN non bloccherà le letture del kernel da tali pagine su alcune implementazioni. Questo dà all'attaccante una via insolita per fornire dati tramite sezioni “execute-only”.
- **Exploit di finestra temporale**: se il kernel disabilita PAN per una finestra più lunga del necessario, una race o una via malevola potrebbe sfruttare quella finestra per eseguire accessi non voluti alla memoria utente.
- **Dimenticanza del riabilitare**: se i percorsi di codice non riabilitano PAN, operazioni kernel successive potrebbero accedere in modo errato alla memoria utente.
- **Misconfigurazione di PXN**: se le page table non impostano PXN sulle pagine utente o mappano erroneamente pagine di codice utente, il kernel potrebbe essere indotto ad eseguire codice user-space.
- **Speculation / side-channels**: analogo a bypass speculativi, potrebbero esserci effetti microarchitetturali transitori che causano violazioni temporanee dei controlli PAN / PXN (anche se tali attacchi dipendono fortemente dal design CPU).
- **Interazioni complesse**: in feature più avanzate (es. JIT, shared memory, regioni di codice just-in-time), il kernel può aver bisogno di controllo granulare per permettere certi accessi o esecuzioni in regioni mappate user; progettarli in sicurezza sotto i vincoli PAN/PXN non è banale.
#### Esempio
<details>
<summary>Esempio di codice</summary>
Qui ci sono sequenze pseudo-assembly illustrative che mostrano l'abilitazione/disabilitazione di PAN attorno all'accesso alla memoria utente, e come potrebbe verificarsi un fault.
// 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 avesse **non** impostato PXN su quella pagina utente, il branch potrebbe riuscire — il che sarebbe insicuro.
Se il kernel dimentica di riattivare PAN dopo l'accesso alla memoria utente, si apre una finestra in cui la logica del kernel successiva potrebbe leggere/scrivere accidentalmente memoria utente arbitraria.
Se il puntatore utente punta a una pagina execute-only (pagina utente con solo permesso di esecuzione, senza 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à del kernel tenta di prendere un puntatore a funzione fornito dall'utente e chiamarlo in contesto kernel (es. `call user_buffer`). Sotto PAN/PXN, quell'operazione è disabilitata o genera fault.
</details>
---
### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introdotto in ARMv8.5 / versioni più recenti (o estensione opzionale)**
TBI significa che il top byte (byte più significativo) di un puntatore a 64 bit viene ignorato dalla traduzione degli indirizzi. Questo permette all'OS o all'hardware di incorporare **bit di tag** nel top byte del puntatore senza influenzare l'indirizzo effettivo.
- TBI sta per **Top Byte Ignore** (a volte chiamato *Address Tagging*). È una caratteristica hardware (disponibile in molte implementazioni ARMv8+) che **ignora i top 8 bit** (bit 63:56) di un puntatore a 64 bit quando esegue la **traduzione degli indirizzi / load/store / instruction fetch**.
- Di fatto, la CPU tratta un puntatore `0xTTxxxx_xxxx_xxxx` (dove `TT` = top byte) come `0x00xxxx_xxxx_xxxx` ai fini della traduzione degli indirizzi, ignorando (mascherando) il top byte. Il top byte può essere usato dal software per memorizzare **metadata / bit di tag**.
- Questo fornisce al software uno spazio “gratuito” in-band per inserire un byte di tag in ogni puntatore senza alterare quale locazione di memoria viene referenziata.
- L'architettura assicura che load, store e instruction fetch trattino il puntatore con il top byte mascherato (cioè tag rimosso) prima di effettuare l'accesso alla memoria.
Così TBI disaccoppia il **puntatore logico** (pointer + tag) dall'**indirizzo fisico** usato per le operazioni di memoria.
#### Perché TBI: casi d'uso e motivazione
- **Pointer tagging / metadata**: È possibile memorizzare metadata extra (es. tipo dell'oggetto, versione, bounds, tag di integrità) 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 tag logico e lo confronta con un **allocation tag** memorizzato in memoria.
- **Sicurezza e integrità migliorate**: Combinando TBI con pointer authentication (PAC) o controlli runtime, si può forzare non solo il valore del puntatore ma anche che il tag sia corretto. Un attaccante che sovrascrive un puntatore senza il tag corretto otterrà un tag mismatch.
- **Compatibilità**: Poiché TBI è opzionale e i bit di tag vengono ignorati dall'hardware, il codice legacy non taggato continua a funzionare normalmente. I bit di tag diventano effettivamente “don't care” per il codice esistente.
#### Esempio
<details>
<summary>Example</summary>
Un puntatore a funzione includeva un tag nel suo top byte (es. `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)**
**Introdotto nelle versioni più recenti di iOS / hardware moderno (iOS ~17 / Apple silicon / modelli di fascia alta)** (alcuni report mostrano PPL in macOS / Apple silicon, ma Apple sta portando protezioni analoghe su iOS)
- PPL è progettato come un **confine di protezione intra-kernel**: anche se il kernel (EL1) è compromesso e ha capacità di read/write, **non dovrebbe poter modificare liberamente** certe **pagine sensibili** (in particolare page tables, metadata di code-signing, pagine di codice 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 a livello kernel: anche con R/W/E arbitrario in modalità kernel, il codice exploit deve comunque entrare nel dominio PPL (o bypassare PPL) per modificare strutture critiche.
- Su Apple silicon più recenti (A15+ / M2+), Apple sta passando a **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)
- L'hardware Apple utilizza un meccanismo chiamato **APRR (Access Permission ReRouting)**, che permette alle entry delle page table (PTE) di contenere piccoli indici, anziché bit di permesso completi. Quegli indici sono mappati tramite registri APRR a permessi effettivi. Questo consente una rimappatura dinamica dei permessi per dominio.
- PPL sfrutta APRR per segregare il privilegio all'interno del contesto kernel: solo il dominio PPL è autorizzato ad aggiornare la mappatura tra indici e permessi effettivi. Cioè, quando codice non-PPL del kernel scrive una PTE o tenta di cambiare bit di permesso, la logica APRR lo impedisce (o impone una mappatura in sola lettura).
- Il codice PPL stesso gira in una regione ristretta (es. `__PPLTEXT`) che normalmente non è eseguibile o scrivibile fino a quando i gate di ingresso non lo permettono temporaneamente. Il kernel chiama i punti di ingresso 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 kernel, o modificare page tables), chiama una routine wrapper PPL, che fa validazioni e poi effettua la transizione nel dominio PPL. Fuori da quel dominio, le pagine protette sono effettivamente in sola lettura o non modificabili dal kernel principale.
- Durante l'entry in PPL, le mappature APRR vengono regolate in modo che le pagine nella regione PPL siano impostate come **eseguibili & scrivibili** all'interno di PPL. Al termine, vengono riportate in sola lettura / non scrivibili. Questo assicura che soltanto routine PPL attentamente revisionate possano scrivere sulle pagine protette.
- Fuori da PPL, i tentativi del codice kernel di scrivere su quelle pagine protette causano fault (permission denied) perché la mappatura APRR per quel dominio di codice non permette la scrittura.
#### Categorie di pagine protette
Le pagine tipicamente protette da PPL includono:
- Strutture di page table (translation table entries, metadata di mapping)
- Pagine di codice kernel, specialmente quelle contenenti logica critica
- Metadata di code-sign (trust caches, blob di firme)
- Tabelle di entitlements, tabelle di enforcement delle firme
- 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 sotto controllo, l'attaccante non può semplicemente patchare o riscrivere queste pagine, a meno che non comprometta anche le routine PPL o non 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. Allocare due pagine fisiche A e B, marcarle come pagine PPL (quindi protette).
2. Mappare due indirizzi virtuali P e Q le cui pagine di translation L3 provengono da A e B.
3. Avviare un thread che accede continuamente a Q, mantenendo viva la sua entry nella TLB.
4. Chiamare `pmap_remove_options()` per rimuovere mapping a partire da P; a causa di un bug, il codice rimuove per errore le TTE sia per P che per Q, ma invalida solo l'entry TLB per P, lasciando l'entry stale di Q attiva.
5. Riutilizzare B (la pagina della tabella di Q) per mappare memoria arbitraria (es. pagine protette PPL). Poiché l'entry stale TLB mappa ancora la vecchia mapping di Q, quella mapping rimane valida per quel contesto.
6. Tramite questo, l'attaccante può mettere al loro posto mapping scrivibili di pagine protette PPL senza passare per l'interfaccia PPL.
- Questo exploit richiedeva un controllo fine del mapping fisico e del comportamento della TLB. Dimostra che un boundary di sicurezza che si basa sulla correttezza di TLB/mapping deve essere estremamente accurato sulle invalidazioni TLB e sulla consistenza dei mapping.
- Project Zero ha commentato che bypass come questo sono sottili e rari, ma possibili in sistemi complessi. Tuttavia, considerano PPL una mitigazione solida.
2. **Altri rischi potenziali & vincoli**
- Se un exploit kernel può entrare direttamente nelle routine PPL (via chiamate ai wrapper PPL), potrebbe bypassare le restrizioni. Quindi la validazione degli argomenti è critica.
- Bug nel codice PPL stesso (es. overflow aritmetico, controlli di bordo) 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 confine PPL è irrevocabilmente legato all'enforcement hardware (APRR, memory controller), quindi è forte solo quanto l'implementazione hardware.
#### Esempio
<details>
<summary>Code Example</summary>
Ecco un pseudocodice / logica semplificata che mostra come il kernel potrebbe chiamare PPL per modificare pagine protette:
</details>
```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ò modificare mappature protette o patchare il codice.
Esempio
Un exploit del kernel prova a sovrascrivere l'entitlement table, o a disabilitare il code-sign enforcement modificando un kernel signature blob. Poiché quella pagina è protetta da PPL, la scrittura viene bloccata a meno di passare dall'interfaccia PPL. Quindi, anche con code execution nel kernel, non è possibile bypassare i vincoli di code-sign né modificare arbitrariamente i dati di credential. Su iOS 17+ alcuni dispositivi usano SPTM per isolare ulteriormente le pagine gestite da PPL.PPL → SPTM / Replacements / Future
- Sui moderni SoC di Apple (A15 o successivi, M2 o successivi), Apple supporta SPTM (Secure Page Table Monitor), che replaces PPL per le protezioni delle page table.
- 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 di policy in un monitor con privilegi più alti al di fuori del controllo del kernel, riducendo ulteriormente il trust boundary.
MTE | EMTE | MIE
Ecco una descrizione di alto livello di come EMTE opera nell’ambiente MIE di Apple:
- Assegnazione del tag
- Quando viene allocata memoria (es. nel kernel o in user space tramite allocatori sicuri), a quel blocco viene assegnato un tag segreto.
- Il puntatore restituito all’utente o al kernel include quel tag nei bit più alti (usando meccanismi TBI / top byte ignore).
- Controllo 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). Se non corrisponde, faulta immediatamente (essendo sincrono).
- Poiché è sincrono, non esiste una finestra di “rilevamento ritardato”.
- Retagging al free / riuso
- Quando la memoria viene liberata, l’allocator cambia il tag del blocco (così i puntatori più vecchi con tag obsoleti non corrispondono più).
- Un use-after-free pointer quindi avrà un tag stale e non corrisponderà quando verrà usato.
- Differenziazione dei tag tra vicini per catturare overflow
- Allocazioni adiacenti ricevono tag distinti. Se un buffer overflow si estende nella memoria del vicino, il mismatch di tag causa un fault.
- Questo è particolarmente efficace per rilevare piccoli overflow che attraversano il confine.
- Enforcement della riservatezza dei tag
- Apple deve prevenire che i valori dei tag vengano leaked (perché se un attaccante apprende il tag, potrebbe costruire puntatori con i tag corretti).
- Includono protezioni (controlli microarchitetturali / speculativi) per evitare side-channel leakage dei bit di tag.
- 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 dalla corruption di memoria).
- L’hardware/OS garantisce che le regole sui tag si applichino anche quando il kernel esegue per conto dello 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
- **Intrablock overflows**: Se l'overflow resta nella stessa allocazione (non oltrepassa il confine) e il tag rimane lo stesso, il tag mismatch non lo rileva.
- **Tag width limitation**: Sono disponibili solo pochi bit per il tag (es. 4 bit, o dominio piccolo)—namespace limitato.
- **Side-channel leak**: Se i bit del tag possono essere leakati (via cache / speculative execution), un attacker può apprendere i tag validi e bypassare. La Tag Confidentiality Enforcement di Apple è pensata per mitigare questo.
- **Performance overhead**: I controlli di tag su ogni load/store aggiungono costo; Apple deve ottimizzare l'hardware per mantenere basso l'overhead.
- **Compatibility & fallback**: Su hardware più vecchio o parti che non supportano EMTE, deve esistere un fallback. Apple dichiara che MIE è abilitato solo sui devices con supporto.
- **Complex allocator logic**: L'allocator deve gestire tag, retagging, allineare i confini e evitare collisioni di mis-tag. Bug nella logica dell'allocator potrebbero introdurre vulnerabilità.
- **Mixed memory / hybrid areas**: Parte della memoria potrebbe rimanere untagged (legacy), rendendo l'interoperabilità più complessa.
- **Speculative / transient attacks**: Come per molte protezioni microarchitetturali, speculative execution o fusioni di micro-op potrebbero bypassare i controlli transientemente o leakare bit di tag.
- **Limited to supported regions**: Apple potrebbe imporre EMTE solo in aree selezionate e ad alto rischio (kernel, subsistemi critici per la sicurezza), non universalmente.
---
## Key enhancements / differences compared to standard MTE
Here are the improvements and changes Apple emphasizes:
| 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ò applicare EMTE in modo stringente, evitare problemi di performance e chiudere falle di side-channel.
---
## How EMTE works in practice (Apple / MIE)
Ecco una descrizione a livello alto di come EMTE opera nello setup MIE di Apple:
1. **Tag assignment**
- Quando la memoria viene allocata (es. in kernel o user space tramite secure allocators), viene assegnato un **secret tag** a quel blocco.
- Il pointer restituito all'utente o al kernel include quel tag nei bit alti (usando TBI / top byte ignore mechanisms).
2. **Tag checking on access**
- Ogni volta che viene eseguito un load o store usando un pointer, l'hardware verifica che il tag del pointer corrisponda al tag del blocco di memoria (allocation tag). Se mismatch, fault immediato (dato che è synchronous).
- Poiché è synchronous, non esiste una finestra di rilevamento ritardato.
3. **Retagging on free / reuse**
- Quando la memoria viene freed, l'allocator cambia il tag del blocco (così i pointer più vecchi con tag obsoleti non corrispondono più).
- Un use-after-free pointer avrà quindi un tag stale e causerebbe un mismatch all'accesso.
4. **Neighbor-tag differentiation to catch overflows**
- Allocazioni adiacenti ricevono tag distinti. Se un buffer overflow tracima nella memoria del vicino, il tag mismatch genera un fault.
- Questo è particolarmente efficace per catturare small overflows che oltrepassano il boundary.
5. **Tag confidentiality enforcement**
- Apple deve impedire che i valori dei tag vengano leakati (perché se un attacker impara il tag, può costruire pointer con tag corretti).
- Includono protezioni (controlli microarchitetturali / speculative) per evitare side-channel leakage dei bit di tag.
6. **Kernel and user-space integration**
- Apple usa EMTE non solo in user-space ma anche in kernel / componenti critiche dell'OS (per proteggere il kernel dalla corruption di memoria).
- L'hardware/OS garantisce che le regole di tag valgano anche quando il kernel esegue per conto dell'user space.
Dato che EMTE è integrato in MIE, Apple usa EMTE in synchronous mode sulle superfici d'attacco chiave, non come opzione opt-in o solo in modalità debug.
---
## 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 propagation e handling dell'exception prima di raggiungere lo user space o essere convertita in un BSD signal.
### Exception Flow (High-Level)
1. **CPU triggers a synchronous exception** (es., invalid pointer dereference, PAC failure, illegal instruction, ecc.).
2. **Low-level trap handler** runs (`trap.c`, `exception.c` in XNU source).
3. The trap handler calls **`exception_triage()`**, the core of the Mach exception handling.
4. `exception_triage()` decides how to route the exception:
- First to the **thread's exception port**.
- Then to the **task's exception port**.
- Then to the **host's exception port** (often `launchd` or `ReportCrash`).
If none of these ports handle the exception, the kernel may:
- **Convert it into a BSD signal** (for user-space processes).
- **Panic** (for kernel-space exceptions).
### Core Function: `exception_triage()`
La funzione `exception_triage()` instrada le Mach exceptions lungo la catena di possibili handler finché uno non la gestisce o finché diventa fatal. È 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.
Porte di eccezione
Ogni oggetto Mach (thread, task, host) può registrare porte di eccezione, dove vengono inviati i messaggi di eccezione.
Sono definite dall’API:
task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()
Ogni exception port ha:
- Una mask (quali exception vuole ricevere)
- Un port name (Mach port che riceve i messaggi)
- Un behavior (come il kernel invia il messaggio)
- Un flavor (quale thread state includere)
Debuggers and Exception Handling
Un debugger (es., LLDB) imposta un exception port sul task o thread target, solitamente usando task_set_exception_ports().
Quando si verifica un’eccezione:
- Il messaggio Mach viene inviato al processo del debugger.
- Il debugger può decidere di gestire (resume, modificare registri, saltare l’istruzione) o non gestire l’eccezione.
- Se il debugger non la gestisce, l’eccezione si propaga al livello successivo (task → host).
Flow of EXC_BAD_ACCESS
-
Il thread dereferenzia un puntatore non valido → la CPU solleva un Data Abort.
-
Il trap handler del kernel chiama
exception_triage(EXC_BAD_ACCESS, ...). -
Messaggio inviato a:
-
Thread port → (il debugger può intercettare il breakpoint).
-
Se il debugger ignora → Task port → (handler a livello di processo).
-
Se ignorato → Host port (di solito ReportCrash).
- Se nessuno lo gestisce →
bsd_exception()traduce inSIGSEGV.
PAC Exceptions
Quando Pointer Authentication (PAC) fallisce (signature mismatch), viene sollevata una special Mach exception:
EXC_ARM_PAC(type)- I codes possono includere dettagli (es., key type, pointer type).
Se il binario ha il flag TFRO_PAC_EXC_FATAL, il kernel tratta i fallimenti PAC come fatali, bypassando l’intercettazione del debugger. Questo serve a prevenire che un attacker usi il debugger per bypassare i controlli PAC ed è abilitato per i platform binaries.
Software Breakpoints
Un software breakpoint (int3 su x86, brk su ARM64) è implementato provocando un fault deliberato.
Il debugger lo cattura tramite l’exception port:
- Modifica il program counter o la memoria.
- Ripristina l’istruzione originale.
- Riprende l’esecuzione.
Questo stesso meccanismo è quello che ti permette di “catturare” una PAC exception — a meno che TFRO_PAC_EXC_FATAL sia impostato, nel qual caso non raggiunge mai il debugger.
Conversion to BSD Signals
Se nessun handler accetta l’eccezione:
-
Il kernel chiama
task_exception_notify() → bsd_exception(). -
Questo mappa le Mach exceptions ai segnali:
| Mach Exception | Signal |
|---|---|
| EXC_BAD_ACCESS | SIGSEGV or SIGBUS |
| EXC_BAD_INSTRUCTION | SIGILL |
| EXC_ARITHMETIC | SIGFPE |
| EXC_SOFTWARE | SIGTRAP |
| EXC_BREAKPOINT | SIGTRAP |
| EXC_CRASH | SIGKILL |
| EXC_ARM_PAC | SIGILL (on non-fatal) |
### Key Files in XNU Source
-
osfmk/kern/exception.c→ Core diexception_triage(),exception_deliver_*(). -
bsd/kern/kern_sig.c→ Logica di delivery dei segnali. -
osfmk/arm64/trap.c→ Low-level trap handlers. -
osfmk/mach/exc.h→ Exception codes e strutture. -
osfmk/kern/task.c→ Setup degli exception port per i task.
Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)
Il kernel usava un zone allocator (kalloc) diviso in “zone” di dimensione fissa.
Ogni zona memorizzava solo allocazioni di una singola size class.
Dallo screenshot:
| Zone Name | Element Size | Example Use |
|---|---|---|
default.kalloc.16 | 16 bytes | Very small kernel structs, pointers. |
default.kalloc.32 | 32 bytes | Small structs, object headers. |
default.kalloc.64 | 64 bytes | IPC messages, tiny kernel buffers. |
default.kalloc.128 | 128 bytes | Medium objects like parts of OSObject. |
| … | … | … |
default.kalloc.1280 | 1280 bytes | Large structures, IOSurface/graphics metadata. |
Come funzionava:
- Ogni richiesta di allocazione veniva arrotondata per eccesso alla dimensione della zona più vicina.
(E.g., una richiesta di 50 byte finiva nella zona
kalloc.64). - La memoria in ciascuna zona veniva tenuta in una freelist — i chunk liberati dal kernel tornavano in quella zona.
- Se facevi overflow su un buffer di 64 byte, sovrascrivevi l’oggetto successivo nella stessa zona.
Questo è il motivo per cui heap spraying / feng shui era così efficace: potevi prevedere i vicini degli oggetti spruzzando allocazioni della stessa size class.
The freelist
All’interno di ogni zona kalloc, gli oggetti liberati non venivano restituiti direttamente al sistema — andavano in una freelist, una linked list di chunk disponibili.
-
Quando un chunk veniva liberato, il kernel scriveva un puntatore all’inizio di quel chunk → l’indirizzo del prossimo chunk libero nella stessa zona.
-
La zona manteneva un puntatore HEAD al primo chunk libero.
-
L’allocazione usava sempre l’HEAD corrente:
-
Pop HEAD (restituisce quella memoria al chiamante).
-
Update HEAD = HEAD->next (memorizzato nell’header del chunk liberato).
-
Il freeing ri-inseriva i chunk:
-
freed_chunk->next = HEAD -
HEAD = freed_chunk
Quindi la freelist era semplicemente una linked list costruita dentro la memoria liberata stessa.
Stato normale:
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 attaccante potrebbe corromperli:
-
Heap overflow in un freed chunk adiacente → sovrascrivere il suo “next” pointer.
-
Use-after-free: scrivere in un freed object → sovrascrivere il suo “next” pointer.
Poi, alla successiva allocazione di quella dimensione:
- L’allocator estrae il corrupted chunk.
- Segue il “next” pointer fornito dall’attaccante.
- Restituisce un pointer verso memoria arbitraria, abilitando 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.
This freelist design made exploitation highly effective pre-hardening: predictable neighbors from heap sprays, raw pointer freelist links, and no type separation allowed attackers to escalate UAF/overflow bugs into arbitrary kernel memory control.
Heap Grooming / Feng Shui
The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data.
Steps:
- Spray allocations (fill the holes)
- Over time, the kernel heap gets fragmented: some zones have holes where old objects were freed.
- The attacker first makes lots of dummy allocations to fill these gaps, so the heap becomes “packed” and predictable.
- Force new pages
- Once the holes are filled, the next allocations must come from new pages added to the zone.
- Fresh pages mean objects will be clustered together, not scattered across old fragmented memory.
- This gives the attacker much better control of neighbors.
- Place attacker objects
- The attacker now sprays again, creating lots of attacker-controlled objects in those new pages.
- These objects are predictable in size and placement (since they all belong to the same zone).
- Free a controlled object (make a gap)
- The attacker deliberately frees one of their own objects.
- This creates a “hole” in the heap, which the allocator will later reuse for the next allocation of that size.
- Victim object lands in the hole
- The attacker triggers the kernel to allocate the victim object (the one they want to corrupt).
- Since the hole is the first available slot in the freelist, the victim is placed exactly where the attacker freed their object.
- Overflow / UAF into victim
- Now the attacker has attacker-controlled objects around the victim.
- By overflowing from one of their own objects (or reusing a freed one), they can reliably overwrite the victim’s memory fields with chosen values.
Why it works:
- Zone allocator predictability: allocations of the same size always come from the same zone.
- Freelist behavior: new allocations reuse the most recently freed chunk first.
- Heap sprays: attacker fills memory with predictable content and controls layout.
- End result: attacker controls where the victim object lands and what data sits next to it.
Modern Kernel Heap (iOS 15+/A12+ SoCs)
Apple hardened the allocator and made heap grooming much harder:
1. From Classic kalloc to kalloc_type
- Before: a single
kalloc.<size>zone existed for each size class (16, 32, 64, … 1280, etc.). Any object of that size was placed there → attacker objects could sit next to privileged kernel objects. - Now:
- Kernel objects are allocated from typed zones (
kalloc_type). - Each type of object (e.g.,
ipc_port_t,task_t,OSString,OSData) has its own dedicated zone, even if they’re the same size. - The mapping between object type ↔ zone is generated from the kalloc_type system at compile time.
An attacker can no longer guarantee that controlled data (OSData) ends up adjacent to sensitive kernel objects (task_t) of the same size.
2. Slabs and Per-CPU Caches
- The heap is divided into slabs (pages of memory carved into fixed-size chunks for that zone).
- Each zone has a per-CPU cache to reduce contention.
- Allocation path:
- Try per-CPU cache.
- If empty, pull from the global freelist.
- If freelist is empty, allocate a new slab (one or more pages).
- Benefit: This decentralization makes heap sprays less deterministic, since allocations may be satisfied from different CPUs’ caches.
3. Randomization inside zones
- Within a zone, freed elements are not handed back in simple FIFO/LIFO order.
- Modern XNU uses encoded freelist pointers (safe-linking like Linux, introduced ~iOS 14).
- Each freelist pointer is XOR-encoded with a per-zone secret cookie.
- This prevents attackers from forging a fake freelist pointer if they gain a write primitive.
- Some allocations are randomized in their placement within a slab, so spraying doesn’t guarantee adjacency.
4. Guarded Allocations
- Certain critical kernel objects (e.g., credentials, task structures) are allocated in guarded zones.
- These zones insert guard pages (unmapped memory) between slabs or use redzones around objects.
- Any overflow into the guard page triggers a fault → immediate panic instead of silent corruption.
5. Page Protection Layer (PPL) and SPTM
- Even if you control a freed object, you can’t modify all of kernel memory:
- PPL (Page Protection Layer) enforces that certain regions (e.g., code signing data, entitlements) are read-only even to the kernel itself.
- On A15/M2+ devices, this role is replaced/enhanced by SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
- These hardware-enforced layers mean attackers can’t escalate from a single heap corruption to arbitrary patching of critical security structures.
- (Added / Enhanced): also, PAC (Pointer Authentication Codes) is used in the kernel to protect pointers (especially function pointers, vtables) so that forging or corrupting them becomes harder.
- (Added / Enhanced): zones may enforce zone_require / zone enforcement, i.e. that an object freed can only be returned through its correct typed zone; invalid cross-zone frees may panic or be rejected. (Apple alludes to this in their memory safety posts)
6. Large Allocations
- Not all allocations go through
kalloc_type. - Very large requests (above ~16 KB) bypass typed zones and are served directly from kernel VM (kmem) via page allocations.
- These are less predictable, but also less exploitable, since they don’t share slabs with other objects.
7. Allocation Patterns Attackers Target
Even with these protections, attackers still look for:
- Reference count objects: if you can tamper with retain/release counters, you may cause use-after-free.
- Objects with function pointers (vtables): corrupting one still yields control flow.
- Shared memory objects (IOSurface, Mach ports): these are still attack targets because they bridge user ↔ kernel.
But — unlike before — you can’t just spray OSData and expect it to neighbor a task_t. You need type-specific bugs or info leaks to succeed.
Example: Allocation Flow in Modern Heap
Suppose userspace calls into IOKit to allocate an OSData object:
- Type lookup →
OSDatamaps tokalloc_type_osdatazone (size 64 bytes). - Check per-CPU cache for free elements.
- If found → return one.
- If empty → go to global freelist.
- If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes).
- Return chunk to caller.
Freelist pointer protection:
- Each freed chunk stores the address of the next free chunk, but encoded with a secret key.
- Overwriting that field with attacker data won’t work unless you know the key.
Comparison Table
| Feature | Old Heap (Pre-iOS 15) | Modern Heap (iOS 15+ / A12+) |
|---|---|---|
| Allocation granularity | Fixed size buckets (kalloc.16, kalloc.32, etc.) | Size + type-based buckets (kalloc_type) |
| Placement predictability | High (same-size objects side by side) | Low (same-type grouping + randomness) |
| Freelist management | Raw pointers in freed chunks (easy to corrupt) | Encoded pointers (safe-linking style) |
| Adjacent object control | Easy via sprays/frees (feng shui predictable) | Hard — typed zones separate attacker objects |
| Kernel data/code protections | Few hardware protections | PPL / SPTM protect page tables & code pages, and PAC protects pointers |
| Allocation reuse validation | None (freelist pointers raw) | zone_require / zone enforcement |
| Exploit reliability | High with heap sprays | Much lower, requires logic bugs or info leaks |
| Large allocations handling | All small allocations managed equally | Large ones bypass zones → handled via VM |
Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)
In recent Apple OS versions (especially iOS 17+), Apple introduced a more secure userland allocator, xzone malloc (XZM). This is the user-space analog to the kernel’s kalloc_type, applying type awareness, metadata isolation, and memory tagging safeguards.
Goals & Design Principles
- Type segregation / type awareness: group allocations by type or usage (pointer vs data) to prevent type confusion and cross-type reuse.
- Metadata isolation: separate heap metadata (e.g. free lists, size/state bits) from object payloads so that out-of-bounds writes are less likely to corrupt metadata.
- Guard pages / redzones: insert unmapped pages or padding around allocations to catch overflows.
- Memory tagging (EMTE / MIE): work in conjunction with hardware tagging to detect use-after-free, out-of-bounds, and invalid accesses.
- Scalable performance: maintain low overhead, avoid excessive fragmentation, and support many allocations per second with low latency.
Architecture & Components
Below are the main elements in the xzone allocator:
Segment Groups & Zones
- Segment groups partition the address space by usage categories: e.g.
data,pointer_xzones,data_large,pointer_large. - Each segment group contains segments (VM ranges) that host allocations for that category.
- Associated with each segment is a metadata slab (separate VM area) that stores metadata (e.g. free/used bits, size classes) for that segment. This out-of-line (OOL) metadata ensures that metadata is not intermingled with object payloads, mitigating corruption from overflows.
- Segments are carved into chunks (slices) which in turn are subdivided into blocks (allocation units). A chunk is tied to a specific size class and segment group (i.e. all blocks in a chunk share the same size & category).
- For small / medium allocations, it will use fixed-size chunks; for large/huges, it may map separately.
Chunks & Blocks
- A chunk is a region (often several pages) dedicated to allocations of one size class within a group.
- Inside a chunk, blocks are slots available for allocations. Freed blocks are tracked via the metadata slab — e.g. via bitmaps or free lists stored out-of-line.
- Between chunks (or within), guard slices / guard pages may be inserted (e.g. unmapped slices) to catch out-of-bounds writes.
Type / Type ID
- Every allocation site (or call to malloc, calloc, etc.) is associated with a type identifier (a
malloc_type_id_t) which encodes what kind of object is being allocated. That type ID is passed to the allocator, which uses it to select which zone / segment to serve the allocation. - Because of this, even if two allocations have the same size, they may go into entirely different zones if their types differ.
- In early iOS 17 versions, not all APIs (e.g. CFAllocator) were fully type-aware; Apple addressed some of those weaknesses in iOS 18.
Allocation & Freeing Workflow
Here is a high-level flow of how allocation and deallocation operate in xzone:
- malloc / calloc / realloc / typed alloc is invoked with a size and type ID.
- The allocator uses the type ID to pick the correct segment group / zone.
- Within that zone/segment, it seeks a chunk that has free blocks of the requested size.
- It may consult local caches / per-thread pools or free block lists from metadata.
- If no free block is available, it may allocate a new chunk in that zone.
- The metadata slab is updated (free bit cleared, bookkeeping).
- If memory tagging (EMTE) is in play, the returned block gets a tag assigned, and metadata is updated to reflect its “live” state.
- When
free()is called:
- The block is marked as freed in metadata (via OOL slab).
- The block may be placed into a free list or pooled for reuse.
- Optionally, block contents may be cleared or poisoned to reduce data leaks or use-after-free exploitation.
- The hardware tag associated with the block may be invalidated or re-tagged.
- If an entire chunk becomes free (all blocks freed), the allocator may reclaim that chunk (unmap it or return to OS) under memory pressure.
Security Features & Hardening
These are the defenses built into modern userland xzone:
| Feature | Purpose | Notes |
|---|---|---|
| Metadata decoupling | Prevent overflow from corrupting metadata | Metadata lives in separate VM region (metadata slab) |
| Guard pages / unmapped slices | Catch out-of-bounds writes | Helps detect buffer overflows rather than silently corrupting adjacent blocks |
| Type-based segregation | Prevent cross-type reuse & type confusion | Even same-size allocations from different types go to different zones |
| Memory Tagging (EMTE / MIE) | Detect invalid access, stale references, OOB, UAF | xzone works in concert with hardware EMTE in synchronous mode (“Memory Integrity Enforcement”) |
| Delayed reuse / poisoning / zap | Reduce chance of use-after-free exploitation | Freed blocks may be poisoned, zeroed, or quarantined before reuse |
| Chunk reclamation / dynamic unmapping | Reduce memory waste and fragmentation | Entire chunks may be unmapped when unused |
| Randomization / placement variation | Prevent deterministic adjacency | Blocks in a chunk and chunk selection may have randomized aspects |
| Segregation of “data-only” allocations | Separate allocations that don’t store pointers | Reduces attacker control over metadata or control fields |
Interaction with Memory Integrity Enforcement (MIE / EMTE)
- Apple’s MIE (Memory Integrity Enforcement) is the hardware + OS framework that brings Enhanced Memory Tagging Extension (EMTE) into always-on, synchronous mode across major attack surfaces.
- xzone allocator is a fundamental foundation of MIE in user space: allocations done via xzone get tags, and accesses are checked by hardware.
- In MIE, the allocator, tag assignment, metadata management, and tag confidentiality enforcement are integrated to ensure that memory errors (e.g. stale reads, OOB, UAF) are caught immediately, not exploited later.
- If you like, I can also generate a cheat-sheet or diagram of xzone internals for your book. Do you want me to do that next?
- :contentReference[oai:20]{index=20}
(Old) Physical Use-After-Free via IOSurface
Ghidra Install BinDiff
Download BinDiff DMG from https://www.zynamics.com/bindiff/manual and install it.
Open Ghidra with ghidraRun and go to File –> Install Extensions, press the add button and select the path /Applications/BinDiff/Extra/Ghidra/BinExport and click OK and isntall it even if there is a version mismatch.
Using BinDiff with Kernel versions
- Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be
.ipswfiles. - Decompress until you get the bin format of the kernelcache of both
.ipswfiles. You have information on how to do this on:
macOS Kernel Extensions & Kernelcache
- Open Ghidra with
ghidraRun, create a new project and load the kernelcaches. - Open each kernelcache so they are automatically analyzed by Ghidra.
- Then, on the project Window of Ghidra, right click each kernelcache, select
Export, select formatBinary BinExport (v2) for BinDiffand export them. - Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache.
Finding the right XNU version
If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).
For example, the versions 15.1 RC, 15.1 and 15.1.1 use the version Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006.
iMessage/Media Parser Zero-Click Chains
Imessage Media Parser Zero Click Coreaudio Pac Bypass
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
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
HackTricks

