iOS Exploiting
Tip
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.
iOS Exploit Mitigations
1. Code Signing / Runtime Signature Verification
Introduced early (iPhone OS → iOS) Dit is een van die fundamentele beskermings: alle uitvoerbare kode (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) moet kriptografies deur ’n sertifikaatketting wat in Apple se trust wortel gewortel is, onderteken wees. Tydens runtime, voordat ’n binêre in geheue gelaai word (of voordat spronge oor sekere grense uitgevoer word), kontroleer die stelsel die handtekening daarvan. As die kode gewysig is (bit-flipped, gepatch) of nie onderteken is nie, misluk die laai.
- Thwarts: die “classic payload drop + execute” stadium in exploit chains; arbitrary code injection; die wysiging van ’n bestaande binêre om kwaadwillige logika in te voeg.
- Mechanism detail:
- Die Mach-O loader (en dynamic linker) kontroleer code pages, segments, entitlements, team IDs, en dat die handtekening die lêer se inhoud dek.
- Vir geheuegebiede soos JIT caches of dinamies gegenereerde kode, dwing Apple dat pages onderteken of gevalideer word via spesiale APIs (bv.
mprotectmet code-sign checks). - Die handtekening sluit entitlements en identifiers in; die OS dwing af dat sekere APIs of bevoorregte vermoëns spesifieke entitlements benodig wat nie vervalst kan word nie.
Example
Gestel 'n exploit kry code execution in 'n proses en probeer shellcode in 'n heap skryf en daarna daarna na dit spring. Op iOS sou daardie page beide as executable gemerk moet wees **en** aan code-signature vereistes moet voldoen. Aangesien die shellcode nie met Apple se sertifikaat geteken is nie, misluk die sprong of weier die stelsel om daardie geheuegebied executable te maak.2. CoreTrust
Introduced around iOS 14+ era (or gradually in newer devices / later iOS) CoreTrust is die subsisteem wat runtime signature validation van binaries uitvoer (insluitend stelsel- en gebruikersbinaries) teen Apple’s root certificate eerder as om op ’n gecachede userland trust store te vertrou.
- Thwarts: post-install tampering van binaries, jailbreaking-tegnieke wat probeer om stelselbiblioteke of gebruikersapps te vervang of te patch; die stelsel mislei deur vertroude binaries met kwaadwillige eweknieë te vervang.
- Mechanism detail:
- In plaas daarvan om ’n plaaslike trust-databasis of sertifikaat-cache te vertrou, haal CoreTrust direk Apple se root of verifieer intermediate certificates in ’n veilige ketting.
- Dit verseker dat wysigings (bv. in die filesystem) aan bestaande binaries opgespoor en geweier word.
- Dit bind entitlements, team IDs, code signing flags en ander metadata aan die binêre by laaityd.
Example
'n Jailbreak kan probeer om `SpringBoard` of `libsystem` met 'n gepatcheerde weergawe te vervang om persistentie te kry. Maar wanneer die OS se loader of CoreTrust nagaan, sien dit die handtekeningverskil (of gemodifiseerde entitlements) en weier dit om uit te voer.3. Data Execution Prevention (DEP / NX / W^X)
Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time DEP dwing af dat pages wat as writable gemerk is (vir data) nie-executable is, en pages wat executable is nie-writable. Jy kan nie net shellcode in ’n heap of stack skryf en dit uitvoer nie.
- Thwarts: direkte shellcode-uitvoering; klassieke buffer-overflow → sprong na ingespiete shellcode.
- Mechanism detail:
- Die MMU / memory protection flags (via page tables) handhaaf die skeiding.
- Enige poging om ’n writable page executable te merk, skep ’n stelselkontrole (en is of verbode of vereis code-sign goedkeuring).
- In baie gevalle vereis dit om pages executable te maak dat jy OS APIs gebruik wat addisionele behelsels of kontroles afdwing.
Example
'n Overflow skryf shellcode op die heap. Die aanvaller probeer `mprotect(heap_addr, size, PROT_EXEC)` om dit uitvoerbaar te maak. Maar die stelsel weier of valideer dat die nuwe page aan code-sign vereistes moet voldoen (wat die shellcode nie kan).4. Address Space Layout Randomization (ASLR)
Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) ASLR randomiseer die basisadresse van sleutel geheuegebiede: libraries, heap, stack, ens., by elke proseslansering. Gadgets se adresse verskuif tussen draaie.
- Thwarts: hardcoding van gadget-adresses vir ROP/JOP; statiese exploit chains; blind spronge na bekende offsets.
- Mechanism detail:
- Elke gelaaide library / dynamic module word by ’n randomised offset herbasis.
- Stack- en heap-basiswysers word gerandomiseer (binne sekere entropy-beperkings).
- Soms word ander gebiede (bv. mmap allocations) ook gerandomiseer.
- Gekombineer met information-leak mitigations, dwing dit die aanvaller om eers ’n adres of pointer te leak om basisadresse tydens runtime te ontdek.
Example
'n ROP chain verwag gadget by `0x….lib + offset`. Maar aangesien `lib` elke run anders herlocateer word, misluk die hardcoded chain. 'n Exploit moet eers die basisadres van die module leak voordat dit gadget-adresses kan bereken.5. Kernel Address Space Layout Randomization (KASLR)
Introduced in iOS ~ (iOS 5 / iOS 6 timeframe) Analoog aan user ASLR, randomiseer KASLR die basis van die kernel text en ander kernel-strukture by opstart.
- Thwarts: kernel-level exploits wat op ’n vasgestelde ligging van kernel-kode of data staatmaak; statiese kernel exploits.
- Mechanism detail:
- By elke opstart word die kernel se basisadres gerandomiseer (binne ’n reeks).
- Kernel data strukture (soos
task_structs,vm_map, ens.) kan ook verskuif of ge-offset wees. - Aanvallers moet eers kernel pointers lek of ’n information disclosure kwetsbaarheid gebruik om offsets te bereken voordat hulle kernel strukture of kode kan kap.
Example
'n Lokale kwetsbaarheid mik om 'n kernel function pointer (bv. in 'n `vtable`) op `KERN_BASE + offset` te korrupteer. Maar aangesien `KERN_BASE` onbekend is, moet die aanvaller dit eers lek (bv. via 'n read primitive) voordat hy die korrekte adres vir korrupsie kan bereken.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) monitor kontinu die integriteit van kernel text pages (via hash of checksum). As dit aanpassings (patches, inline hooks, code-wysigings) buite toegelate vensters opmerk, veroorsaak dit ’n kernel panic of herbegin.
- Thwarts: persistent kernel patching (wysiging van kernel-instruksies), inline hooks, statiese funksie-overskrywings.
- Mechanism detail:
- ’n Hardware- of firmwaremodule monitor die kernel text area.
- Dit her-hash periodeklik of op aanvraag die pages en vergelyk met verwagte waardes.
- As mismatches voorkom buite benign update-vensters, panikeer dit die toestel (om persistent kwaadwillige patching te voorkom).
- Aanvallers moet òf deteksiervensters vermy òf wettige patch-paaie gebruik.
Example
'n Exploit probeer 'n kernel-funksie proloog patch (bv. `memcmp`) om oproepe te onderskep. Maar KPP sien dat die code page se hash nie meer die verwagte waarde ooreenstem nie en veroorsaak 'n kernel panic, wat die toestel laat crash voordat die patch kon stabiliseer.7. Kernel Text Read‐Only Region (KTRR)
Introduced in modern SoCs (post ~A12 / newer hardware) KTRR is ’n hardware-afgedwingde meganisme: sodra die kernel text vroeg tydens boot ge-slot is, word dit read-only vanaf EL1 (die kernel), wat verdere skryfpogings vir code pages voorkom.
- Thwarts: enige wysigings aan kernel-kode na boot toe (bv. patching, in-place code injection) op EL1 privilege-vlak.
- Mechanism detail:
- Tydens boot (in secure/bootloader stadium) merk die memory controller (of ’n veilige hardware-eenheid) die fisiese pages wat kernel text bevat as read-only.
- Selfs as ’n exploit volle kernel-privileges kry, kan dit nie daardie pages skryf om instruksies te patch nie.
- Om dit te verander, moet die aanvaller eers die boot-ketting kompromitteer, of KTRR self subverteer.
Example
'n Privilege-escalation exploit spring in EL1 en probeer 'n trampoline skryf in 'n kernel-funksie (bv. in 'n `syscall` handler). Maar omdat die pages deur KTRR read-only gesluit is, misluk die skryf (of veroorsaak fout), sodat patches nie toegepas word nie.8. Pointer Authentication Codes (PAC)
Introduced with ARMv8.3 (hardware), Apple beginning with A12 / iOS ~12+
- PAC is ’n hardware-funksie geïntroduseer in ARMv8.3-A om die manipulasie van pointer-waardes (return addresses, function pointers, sekere datapunters) te ontdek deur ’n klein kriptografiese handtekening (’n “MAC”) in ongebruikte hoë-bits van die pointer in te bak.
- Die handtekening (“PAC”) word oor die pointer-waarde plus ’n modifier (’n kontekstwaarde, bv. stack pointer of ander distinkte data) bereken. Sodoende kry dieselfde pointer-waarde in verskillende kontekste ’n ander PAC.
- By gebruik tyd, voordat daar gedereferenceer of na daardie pointer gespring word, verifieer ’n authenticate instruksie die PAC. As dit geldig is, word die PAC verwyder en die suiwer pointer verkry; as dit ongeldig is, raak die pointer “poisoned” (of ’n fout word geaktiveer).
- Die sleutels wat vir die berekening/validasie van PAC gebruik word, woon in bevoorregte registers (EL1, kernel) en is nie direk van user mode leesbaar nie.
- Omdat nie alle 64 bits van ’n pointer in baie stelsels gebruik word nie (bv. 48-bit address space), kan die boonste bits die PAC bevat sonder om die effektiewe adres te verander.
Architectural Basis & Key Types
-
ARMv8.3 bring vyf 128-bit sleutels (elkeen geïmplementeer via twee 64-bit stelselregisters) vir pointer authentication.
-
APIAKey — vir instruction pointers (domein “I”, sleutel A)
-
APIBKey — tweede instruction pointer sleutel (domein “I”, sleutel B)
-
APDAKey — vir data pointers (domein “D”, sleutel A)
-
APDBKey — vir data pointers (domein “D”, sleutel B)
-
APGAKey — “generic” sleutel, vir die teken van nie-pointer data of ander generiese gebruike
-
Hierdie sleutels word in bevoorregte stelselregisters gestoor (slegs by EL1/EL2 ens. toeganklik), nie toeganklik van user mode nie.
-
Die PAC word bereken via ’n kriptografiese funksie (ARM stel QARMA voor as die algoritme) wat gebruik maak van:
- Die pointer-waarde (kanoniese gedeelte)
- ’n modifier (’n kontekstwaarde, soos ’n salt)
- Die geheime sleutel
- Sommige interne tweak-logika As die resulterende PAC ooreenstem met wat in die boonste bits van die pointer gestoor is, slaag authenticatie.
Instruction Families
Die naamkonvensie is: PAC / AUT / XPAC, gevolg deur domeinletters.
PACxxinstruksies sign ’n pointer en sit ’n PAC inAUTxxinstruksies authenticate + strip (valideer en verwyder die PAC)XPACxxinstruksies strip sonder validasie
Domains / suffixes:
| Mnemonic | Meaning / Domain | Key / Domain | Example Usage in Assembly |
|---|---|---|---|
| PACIA | Sign instruction pointer with APIAKey | “I, A” | PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1 |
| PACIB | Sign instruction pointer with APIBKey | “I, B” | PACIB X2, X3 |
| PACDA | Sign data pointer with APDAKey | “D, A” | PACDA X4, X5 |
| PACDB | Sign data pointer with APDBKey | “D, B” | PACDB X6, X7 |
| PACG / PACGA | Generic (non-pointer) signing with APGAKey | “G” | PACGA X8, X9, X10 (sign X9 with modifier X10 into X8) |
| AUTIA | Authenticate APIA-signed instruction pointer & strip PAC | “I, A” | AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip |
| AUTIB | Authenticate APIB domain | “I, B” | AUTIB X2, X3 |
| AUTDA | Authenticate APDA-signed data pointer | “D, A” | AUTDA X4, X5 |
| AUTDB | Authenticate APDB-signed data pointer | “D, B” | AUTDB X6, X7 |
| AUTGA | Authenticate generic / blob (APGA) | “G” | AUTGA X8, X9, X10 (validate generic) |
| XPACI | Strip PAC (instruction pointer, no validation) | “I” | XPACI X0 — remove PAC from X0 (instruction domain) |
| XPACD | Strip PAC (data pointer, no validation) | “D” | XPACD X4 — remove PAC from data pointer in X4 |
Daar is gespesialiseerde / alias vorms:
PACIASPis shorthand virPACIA X30, SP(sign the link register using SP as modifier)AUTIASPisAUTIA X30, SP(authenticate link register with SP)- Gekombineerde vorms soos
RETAA,RETAB(authenticate-and-return) ofBLRAA(authenticate & branch) bestaan in ARM-uitbreidings / compiler-ondersteuning. - Ook zero-modifier variasies:
PACIZA/PACIZBwaar die modifier implisiet nul is, ens.
Modifiers
Die hoofdoel van die modifier is om die PAC aan ’n spesifieke konteks te bind sodat dieselfde adres wat in verskillende kontekste geteken is, verskillende PACs gee. Dit verhoed eenvoudige hergebruik van pointers oor rame of voorwerpe. Dit is soos om ’n salt by ’n hash te voeg.
Daarom:
- Die modifier is ’n kontekstwaarde (nog ’n register) wat in die PAC-berekening gemeng word. Tipiese keuses: die stack pointer (
SP), ’n frame pointer, of ’n object ID. - Om SP as modifier te gebruik is algemeen vir return address signing: die PAC raak gekoppel aan die spesifieke stack frame. As jy probeer om die LR her te gebruik in ’n ander frame, verander die modifier, so PAC-validasie misluk.
- Dieselfde pointer-waarde geteken onder verskillende modifiers gee ander PACs.
- Die modifier hoef nie geheim te wees nie, maar ideaal is dit nie aanvaller-gekontroleer nie.
- Vir instruksies wat pointers teken of verifieer waar geen betekenisvolle modifier bestaan nie, gebruik sommige vorms nul of ’n implisiete konstante.
Apple / iOS / XNU Customizations & Observations
- Apple se PAC-implementasie sluit per-boot diversifiers in sodat sleutels of tweaks by elke opstart verander, wat hergebruik oor opstarts verhinder.
- Hulle sluit ook cross-domain mitigations in sodat PACs wat in user mode geteken is nie maklik in kernel mode hergebruik kan word nie, ens.
- Op Apple M1 / Apple Silicon het reverse engineering gewys dat daar nege modifier-tipes en Apple-spesifieke stelselregisters vir sleutelbeheer bestaan.
- Apple gebruik PAC in baie kernel-substelsels: return address signing, pointer integriteit in kernel data, getekende thread contexts, ens.
- Google Project Zero het gewys hoe onder ’n kragtige memory read/write primitive in die kernel, iemand kernel PACs (vir A sleutels) op A12-era toestelle kon vervals, maar Apple het baie van daardie paaie gepatch.
- In Apple se stelsel is sommige sleutels globaal oor die kernel, terwyl gebruikersprosesse per-proses sleutel-randomness kan kry.
PAC Bypasses
- Kernel-mode PAC: theoretical vs real bypasses
- Omdat kernel PAC-sleutels en logika streng beheer word (bevoorregte registers, diversifiers, domein-isolasie), is die vervalsing van arbitrêre getekende kernel-pointers baie moeilik.
- Azad se 2020 “iOS Kernel PAC, One Year Later” rapporteer dat in iOS 12-13 hy ’n paar gedeeltelike bypasses gevind het (signing gadgets, reuse van signed states, onbeschermde indirekte takke) maar geen volledige generiese bypass nie. bazad.github.io
- Apple se “Dark Magic” aanpassings beperk verder die exploreerbare oppervlaktes (domeinwisseling, per-sleutel inskakel-bits). i.blackhat.com
- Daar is ’n bekende kernel PAC bypass CVE-2023-32424 op Apple silicon (M1/M2) gerapporteer deur Zecao Cai et al. i.blackhat.com
- Maar hierdie bypasses is dikwels afhanklik van baie spesifieke gadgets of implementasie-bugs; hulle is nie generiese bypasses nie.
Dus word kernel PAC as uiters robuust beskou, alhoewel nie perfek nie.
- User-mode / runtime PAC bypass techniques
Hierdie is meer algemeen, en benut onvolmaakthede in hoe PAC toegepas of gebruik word in dynamic linking / runtime frameworks. Hieronder is klasse, met voorbeelde.
2.1 Shared Cache / A key issues
-
Die dyld shared cache is ’n groot pre-linked blob van stelsel frameworks en libraries. Omdat dit wyd gedeel word, is function pointers binne die shared cache reeds “pre-signed” en word deur baie prosesse gebruik. Aanvallers mik vir hierdie reeds-getekende pointers as “PAC oracles”.
-
Sommige bypass-tegnieke probeer om A-key getekende pointers wat in die shared cache voorkom te onttrek of te hergebruik en dit in gadgets te kombineer.
-
Die “No Clicks Required” praatjie beskryf die bou van ’n oracle oor die shared cache om relatiewe adresse af te lei en dit met signed pointers te kombineer om PAC te omseil. saelo.github.io
-
Ook is imports van function pointers vanaf shared libraries in userspace gevind om onvoldoende beskerm deur PAC, wat ’n aanvaller toelaat om function pointers te kry sonder om hul handtekening te verander. (Project Zero bug entry) bugs.chromium.org
2.2 dlsym(3) / dynamic symbol resolution
-
’n Bekende bypass is om
dlsym()te roep om ’n alreeds signed function pointer (met A-key, diversifier nul) te kry en dit dan te gebruik. Omdatdlsym’n wettige signed pointer teruggee, omseil dit die behoefte om PAC te vervals. -
Epsilon se blog beskryf hoe sommige bypasses
dlsymmisbruik: die oproepdlsym("someSym")lewer ’n signed pointer en kan vir indirekte aanroepe gebruik word. blog.epsilon-sec.com -
Synacktiv se “iOS 18.4 — dlsym considered harmful” beskryf ’n fout: sekere simbole wat via
dlsymop iOS 18.4 opgelos is, gee pointers terug wat verkeerd geteken is (of met buggy diversifiers), wat ’n onbedoelde PAC-bypass moontlik maak. Synacktiv -
Die logika in dyld vir dlsym sluit in: wanneer
result->isCode, teken hulle die teruggegewe pointer met__builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), d.w.s. konteks nul. blog.epsilon-sec.com
Dus is dlsym ’n gereelde vektor in user-mode PAC bypasses.
2.3 Other DYLD / runtime relocations
-
Die DYLD loader en dinamiese relocasie-logika is kompleks en tydelik map sommige pages as read/write om relocations uit te voer, en skakel dan terug na read-only. Aanvallers benut hierdie vensters. Synacktiv se praatjie beskryf “Operation Triangulation”, ’n timing-gebaseerde PAC-bypass via dinamiese relocations. Synacktiv
-
DYLD pages is nou beskerm met SPRR / VM_FLAGS_TPRO (sekere beskermingsvlae vir dyld). Maar vroeër weergawes het swakker beskerming gehad. Synacktiv
-
In WebKit exploit chains is die DYLD loader gereeld ’n teiken vir PAC-bypasses. Die slides noem dat baie PAC-bypasses die DYLD loader geteiken het (via relocations, interposer hooks). Synacktiv
2.4 NSPredicate / NSExpression / ObjC / SLOP
-
In userland exploit chains word Objective-C runtime-metodes soos
NSPredicate,NSExpressionofNSInvocationgebruik om control calls te smokkel sonder duidelike pointer-forgeries. -
Op ouer iOS (voor PAC) het ’n exploit ’n fake NSInvocation-voorwerp gebruik om arbitraire selectors op beheerde geheue te roep. Met PAC is wysigings nodig. Maar die tegniek SLOP (SeLector Oriented Programming) is onder PAC uitgebrei. Project Zero
-
Die oorspronklike SLOP-tegniek het die ketting van ObjC-aanroepe toegelaat deur fake invocations te skep; die bypass berus daarop dat ISA of selector pointers soms nie volwaardig PAC-beskerm is nie. Project Zero
-
In omgewings waar pointer authentication net gedeeltelik toegepas word, is metodes / selectors / target pointers nie altyd PAC-beskerm nie, wat ruimte vir bypass gee.
Example Flow
Example Signing & Authenticating
``` ; Example: function prologue / return address protection my_func: stp x29, x30, [sp, #-0x20]! ; push frame pointer + LR mov x29, sp PACIASP ; sign LR (x30) using SP as modifier ; … body … mov sp, x29 ldp x29, x30, [sp], #0x20 ; restore AUTIASP ; authenticate & strip PAC ret; Example: indirect function pointer stored in a struct ; suppose X1 contains a function pointer PACDA X1, X2 ; sign data pointer X1 with context X2 STR X1, [X0] ; store signed pointer
; later retrieval: LDR X1, [X0] AUTDA X1, X2 ; authenticate & strip BLR X1 ; branch to valid target
; Example: stripping for comparison (unsafe) LDR X1, [X0] XPACI X1 ; strip PAC (instruction domain) CMP X1, #some_label_address BEQ matched_label
</details>
<details>
<summary>Example</summary>
A buffer overflow overskryf 'n return address op die stack. Die aanvaller skryf die teiken gadget-adres maar kan nie die korrekte PAC bereken nie. Wanneer die funksie terugkeer, fout die CPU se `AUTIA` instruksie omdat die PAC nie ooreenstem nie. Die ketting misluk.
Project Zero se ontleding op A12 (iPhone XS) het getoon hoe Apple se PAC gebruik word en metodes om PACs te valseer as 'n aanvaller 'n memory read/write primitive het.
</details>
### 9. **Branch Target Identification (BTI)**
**Introduced with ARMv8.5 (later hardware)**
BTI is 'n hardeware-funksie wat **indirect branch targets** kontroleer: wanneer `blr` of indirekte calls/spronge uitgevoer word, moet die teiken begin met 'n **BTI landing pad** (`BTI j` of `BTI c`). Om na gadget-adresse te spring wat nie die landing pad het nie, veroorsaak 'n uitzondering.
LLVM se implementasie merk drie variante van BTI-instruksies en hoe hulle op branch-tipes kaart.
| BTI Variant | What it permits (which branch types) | Typical placement / use case |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Targets of *call*-style indirect branches (e.g. `BLR`, or `BR` using X16/X17) | Put at entry of functions that may be called indirectly |
| **BTI J** | Targets of *jump*-style branches (e.g. `BR` used for tail calls) | Placed at the beginning of blocks reachable by jump tables or tail-calls |
| **BTI JC** | Acts as both C and J | Can be targeted by either call or jump branches |
- In kode saamgestel met branch target enforcement, voeg compilers 'n BTI-instruksie (C, J, of JC) by elke geldige indirect-branch teiken (funksie-beginne of blocks bereikbaar deur spronge) sodat indirekte spronge net na daardie plekke suksesvol kan wees.
- **Direct branches / calls** (d.w.s. vaste-adres `B`, `BL`) word **nie deur BTI beperk** nie. Die veronderstelling is dat kodebladsye vertrou word en 'n aanvaller dit nie kan verander nie (dus is direkte spronge veilig).
- Ook is **RET / return** instruksies gewoonlik nie deur BTI beperk nie omdat return-adresse beskerm word via PAC of return signing-meganismes.
#### Mechanism and enforcement
- Wanneer die CPU 'n **indirect branch (BLR / BR)** decodeer in 'n bladsy gemerk as “guarded / BTI-enabled,” kontroleer dit of die teikenadres se eerste instruksie 'n geldige BTI is (C, J, of JC soos toegelaat). Indien nie, plaasvind 'n **Branch Target Exception**.
- Die BTI-instruksie-enkoding is ontwerp om opcodes te hergebruik wat vroeër vir NOPs gereserveer is (in vroëer ARM-weergawes). Dus bly BTI-enabled binaries backward-compatible: op hardeware sonder BTI-ondersteuning tree daardie instruksies op as NOPs.
- Die compiler-passe wat BTIs byvoeg, voeg hulle slegs waar nodig in: funksies wat indirek aangeroep mag word, of basic blocks geteiken deur spronge.
- Sommige patches en LLVM-kode wys dat BTI nie vir *alle* basic blocks ingevoeg word nie — net daardie wat potensiële branch-teikens is (bv. van switch / jump tables).
#### BTI + PAC synergy
PAC beskerm die pointerwaarde (die bron) — verseker dat die ketting van indirekte calls / returns nie gemanipuleer is nie.
BTI verseker dat selfs 'n geldige pointer slegs na behoorlik gemerkte entry-punte kan wys.
Gekombineerd benodig 'n aanvaller beide 'n geldige pointer met korrekte PAC en dat die teiken 'n BTI daarby het. Dit verhoog die moeilikheid om exploit-gadgets te konstrueer.
#### Example
<details>
<summary>Example</summary>
'n Exploit probeer pivot in 'n gadget by `0xABCDEF` wat nie met `BTI c` begin nie. Die CPU, by die uitvoering van `blr x0`, kontroleer die teiken en fout omdat die instruksie-uitlijning nie 'n geldige landing pad insluit nie. Dus word baie gadgets onbruikbaar tensy hulle 'n BTI-prefix het.
</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** is 'n funksie ingestel in **ARMv8.1-A** wat voorkom dat **privileged code** (EL1 of EL2) **lees of skryf** na geheue wat as **user-accessible (EL0)** gemerk is, tensy PAN eksplisiet gedeaktiveer word.
- Die idee: selfs as die kernel getrus of gekompromitteer word, kan dit nie lukraak user-space pointers dereference sonder eers PAN te *clear* nie, wat die risiko's van **`ret2usr`** styl exploits of misbruik van user-gekontroleerde buffers verminder.
- Wanneer PAN aangeskakel is (PSTATE.PAN = 1), veroorsaak enige privileged load/store instruksie wat 'n virtuele adres toegang wat “accessible at EL0” is 'n **permission fault**.
- Die kernel, wanneer dit wettig user-space geheue moet benader (bv. kopieer data na/van user buffers), moet **PAN tydelik deaktiveer** (of na “unprivileged load/store” instruksies skakel) om daardie toegang toe te laat.
- In Linux op ARM64 is PAN-ondersteuning ingestel omstreeks 2015: kernel patches het detectie van die funksie bygevoeg en `get_user` / `put_user` ens. vervang met variante wat PAN rondom user memory accesses clear.
**Key nuance / limitation / bug**
- Soos deur Siguza en ander aangetoon, beteken 'n spesifikasie-bug (of onduidelike gedrag) in ARM se ontwerp dat **execute-only user mappings** (`--x`) dalk **nie PAN trigger** nie. Met ander woorde, as 'n user-bladsy uitvoerbaar maar nie leesbaar is nie, kan die kernel se leespoging PAN omseil omdat die argitektuur “accessible at EL0” as vereisende leespermit beskou, nie net uitvoerbaarheid nie. Dit lei tot 'n PAN-bypass in sekere konfigurasies.
- Vanweë dit, as iOS / XNU execute-only user bladsye toelaat (soos sommige JIT of code-cache opstellings mag), kan die kernel per ongeluk van hulle lees selfs met PAN aangeskakel. Dit is 'n bekende subtiele eksploiteerbare area in sommige ARMv8+ stelsels.
#### PXN (Privileged eXecute Never)
- **PXN** is 'n bladsy-tabel vlag (in die page table entries, leaf of block entries) wat aandui dat die bladsy **nie-uitvoerbaar is wanneer in privileged mode** (d.w.s. wanneer EL1 dit uitvoer).
- PXN verhinder die kernel (of enige privileged code) daarvan om na user-space bladsye te spring of instruksies daaruit uit te voer selfs as control afgedwaal word. In effek blokkeer dit kernel-vlak control-flow redirection na user geheue.
- Gekombineerd met PAN verseker dit:
1. Kernel kan nie (standaard) user-space data lees of skryf nie (PAN)
2. Kernel kan nie user-space kode uitvoer nie (PXN)
- In die ARMv8 page table-formaat het leaf entries 'n `PXN` bit (en ook `UXN` vir unprivileged execute-never) in hul attribuutbisse.
Dus selfs as die kernel 'n gekorrupte function pointer het wat na user geheue wys, en dit probeer daarheen branch, sal die PXN-bit 'n fout veroorsaak.
#### Memory-permission model & how PAN and PXN map to page table bits
Om te verstaan hoe PAN / PXN werk, moet jy sien hoe ARM se translation en permission model werk (vereenvoudig):
- Elke bladsy of block entry het attribuutvelde insluitend **AP[2:1]** vir toegangspolisies (lees/skryf, privileged teenoor unprivileged) en **UXN / PXN** bits vir execute-never beperkings.
- Wanneer PSTATE.PAN op 1 is (aangeskakel), voer die hardeware gewysigde semantiek af: privileged toegang tot bladsye gemerk as “accessible by EL0” (d.w.s. user-accessible) word geweier (fault).
- Vanweë die genoemde bug, mag bladsye wat slegs uitvoerbaar gemerk is (geen leespermit) nie as “accessible by EL0” beskou word onder sekere implementasies nie, en so kan PAN omseil word.
- Wanneer 'n bladsy se PXN-bit gestel is, word uitvoering verbied selfs as die instruksie-fetch van 'n hoër privilege-vlak kom.
#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)
In 'n geharde kernel-ontwerp (soos wat Apple moontlik gebruik):
- Die kernel skakel PAN per verstek aan (sodat privileged code beperk word).
- In paadjies wat wettig user-space geheue moet lees of skryf (bv. syscall buffer copy, I/O, read/write user pointer), deaktiveer die kernel tydelik **PAN** of gebruik spesiale instruksies om dit te oorbrug.
- Na voltooiing van user data toegang, moet dit PAN weer heraktiveer.
- PXN word afgedwing via page tables: user bladsye het PXN = 1 (sodat kernel dit nie kan uitvoer nie), kernel bladsye het nie PXN nie (sodat kernel-kode kan uitvoer).
- Die kernel moet verseker dat geen kodepaaie uitvoering na user geheue veroorsaak nie (wat PXN sou omseil) — sodat exploit-kettings wat op “jump into user-controlled shellcode” staatmaak, geblokkeer word.
Weens die genoemde PAN-bypass deur execute-only bladsye, kan 'n werklike stelsel Apple dwing om execute-only user bladsye te deaktiveer of om regstellings rondom die spesifikasie-kwessie te implementeer.
#### Attack surfaces, bypasses, and mitigations
- **PAN bypass via execute-only pages**: soos bespreek, die spesifikasie laat 'n gaping toe: user bladsye met execute-only (geen leesperms) mag nie as “accessible at EL0” tel nie, sodat PAN kernel-lesings van sulke bladsye in sekere implementasies nie sal blokkkeer nie. Dit gee 'n aanvaller 'n ongewone pad om data via “execute-only” afdelings te voer.
- **Temporal window exploit**: as die kernel PAN deaktiveer vir 'n langer venster as nodig, kan 'n race of kwadepad daardie venster uitbuit om onbedoelde user memory access te doen.
- **Forgotten re-enable**: as kodepaaie versuim om PAN weer te aktiveer, kan daaropvolgende kernel-bewerkings verkeerdelik user memory benader.
- **Misconfiguration of PXN**: as page tables PXN nie op user bladsye stel of verkeerdelik user kode bladsye kaart nie, kan die kernel mislei word om user-space kode uit te voer.
- **Speculation / side-channels**: analoog aan spekulatiewe omseilings, kan daar mikroargitektoniese newe-effekte wees wat transiente skending van PAN / PXN kontroles veroorsaak (alhoewel sulke aanvalle sterk afhanklik is van CPU-ontwerp).
- **Complex interactions**: in meer gevorderde funksies (bv. JIT, shared memory, code-cache) het die kernel fynbeheer nodig om sekere geheue-toegange of uitvoering in user-gemapped streke toe te laat; om dit veilig binne PAN/PXN beperkings te ontwerp is nie eenvoudig nie.
#### Example
<details>
<summary>Code Example</summary>
Hier is illustratiewe pseudo-assembly reekse wat wys hoe PAN rondom user memory access aangeskakel/deaktiveer word, en hoe 'n fout kan voorkom.
// 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
As die kernel nie PXN op daardie user page ingestel het nie, kan die branch slaag — wat onveilig sou wees.
As die kernel vergeet om PAN weer te aktiveer ná toegang tot user memory, skep dit ’n venster waarby verdere kernel-logika per ongeluk arbitrêre user memory kan lees/skryf.
As die user pointer in ’n execute-only page val (user page met slegs execute-permissie, geen read/write), kan onder die PAN-spec-bug `ldr W2, [X1]` dalk **nie** ’n fault gee nie, selfs met PAN geaktiveer, wat ’n bypass-exploit moontlik maak, afhangend van implementering.
</details>
<details>
<summary>Example</summary>
’n kernel-vulnerabiliteit probeer ’n user-provided function pointer neem en dit in kernel context aanroep (d.w.s. `call user_buffer`). Onder PAN/PXN is daardie operasie verbied of sal dit fault.
</details>
---
### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Introduced in ARMv8.5 / newer (or optional extension)**
TBI beteken dat die top byte (mees-belangrike byte) van ’n 64-bit pointer deur address translation geïgnoreer word. Dit laat OS of hardware toe om **tag bits** in die pointer se top byte in te sluit sonder om die werklike adres te beïnvloed.
- TBI staan vir **Top Byte Ignore** (soms *Address Tagging* genoem). Dit is ’n hardware kenmerk (in baie ARMv8+ implementasies beskikbaar) wat die **top 8 bits** (bits 63:56) van ’n 64-bit pointer **ignoreer** wanneer adresvertaling / load/store / instruction fetch uitgevoer word.
- Die CPU behandel ’n pointer `0xTTxxxx_xxxx_xxxx` (waar `TT` = top byte) effens as `0x00xxxx_xxxx_xxxx` vir doeleindes van adresvertaling — die top byte word gemasker. Die top byte kan deur software gebruik word om **metadata / tag bits** te stoor.
- Dit gee software “gratis” in-band ruimte om ’n byte tag in elke pointer in te sluit sonder om te verander watter geheueplek die pointer verwys.
- Die argitektuur verseker dat loads, stores en instruction fetch die pointer met die top byte gemasker (d.w.s. tag verwyder) hanteer voordat die werklike memory access plaasvind.
Dus skei TBI die **logiese pointer** (pointer + tag) van die **fisiese adres** wat vir memory-operasies gebruik word.
#### Waarom TBI: Gebruikgevalle en motivering
- **Pointer tagging / metadata**: Jy kan ekstra metadata (bv. object type, version, bounds, integrity tags) in daardie top byte stoor. Wanneer jy later die pointer gebruik, word die tag op hardwarevlak geïgnoreer, so jy hoef dit nie handmatig te verwyder vir die memory access nie.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI is die basiese hardware-meganisme waarop MTE bou. In ARMv8.5 gebruik die **Memory Tagging Extension** bits 59:56 van die pointer as ’n **logiese tag** en vergelyk dit met ’n **allocation tag** wat in memory gestoor is.
- **Verbeterde sekuriteit & integriteit**: Deur TBI met pointer authentication (PAC) of runtime checks te kombineer, kan jy vereis dat nie net die pointerwaarde maar ook die tag korrek is. ’n Aanvaller wat ’n pointer oorskryf sonder die korrekte tag sal tot ’n mismatched tag lei.
- **Kompatibiliteit**: Aangesien TBI opsioneel is en tag bits deur hardware geïgnoreer word, sal bestaande ongetagde code normaalweg voortgaan om te werk. Die tag bits word effektief “don’t care” bits vir legacy code.
#### Example
<details>
<summary>Example</summary>
’n function pointer het ’n tag in sy top byte ingesluit (bv. `0xAA`). ’n Exploit oorskryf die lae bits van die pointer maar versuim om die tag aan te raak, so wanneer die kernel verifieer of sanitiseer, misluk of word die pointer verwerp.
</details>
---
### 12. **Page Protection Layer (PPL)**
**Introduced in late iOS / modern hardware (iOS ~17 / Apple silicon / high-end models)** (sommige verslae toon PPL in macOS / Apple silicon, maar Apple bring analoog-beskerming na iOS)
- PPL is ontwerp as ’n **intra-kernel protection boundary**: selfs as die kernel (EL1) gekompromitteer is en lees/skryf bevoegdhede het, behoort dit nie vrylik in staat te wees om sekere **sensitiewe pages** (veral page tables, code-signing metadata, kernel code pages, entitlements, trust caches, ens.) te wysig nie.
- Dit skep effektief ’n **“kernel binne die kernel”** — ’n kleiner vertroude komponent (PPL) met **verhoogde bevoegdhede** wat alleen beskermde pages kan wysig. Ander kernel-code moet in PPL routine inroep om veranderinge te maak.
- Dit verminder die aanval-oppervlak vir kernel-exploits: selfs met volle arbitrary R/W/execute in kernel mode, moet exploit-code ook op een of ander manier die PPL-domein betree (of PPL omseil) om kritieke strukture te verander.
- Op nuwer Apple silicon (A15+ / M2+) beweeg Apple na **SPTM (Secure Page Table Monitor)**, wat in baie gevalle PPL vir page-table-beskerming vervang op daardie platforms.
Hier is hoe PPL vermoedelik werk, gebaseer op openbare ontledings:
#### Gebruik van APRR / permission routing (APRR = Access Permission ReRouting)
- Apple hardware gebruik ’n meganisme genaamd **APRR (Access Permission ReRouting)**, wat toelaat dat page table entries (PTEs) klein indeksies bevat, eerder as volle permission bits. Daardie indeksies word via APRR-registers na werklike permissies gekarteer. Dit laat dinamiese herkaarteer van permissies per domein toe.
- PPL benut APRR om bevoegdheid binne kernel-konteks te skei: slegs die PPL-domein mag die mapping tussen indeksies en effektiewe permissies opdateer. Dit beteken dat wanneer nie-PPL kernel-code ’n PTE skryf of probeer om permission bits te flip, die APRR-logika dit verhoed (of nie-wysigbaar afdwing).
- PPL-kode self hardloop in ’n beperkte streek (bv. `__PPLTEXT`) wat normaalweg nie-uitvoerbaar of nie-skryfbaar is totdat entry gates dit tydelik toelaat. Die kernel roep PPL entry points (“PPL routines”) aan om sensitiewe operasies uit te voer.
#### Gate / Entry & Exit
- Wanneer die kernel ’n beskermde page moet wysig (bv. om permissies van ’n kernel code page te verander, of page tables te wysig), roep dit ’n **PPL wrapper**-routine aan, wat validering doen en dan na die PPL-domein oorskakel. Buite daardie domein is die beskermde pages effektief read-only of nie-wysigbaar deur die hoofkernel.
- Tydens PPL-entry word die APRR-mappings aangepas sodat memory pages in die PPL-gebied binne PPL as **executable & writable** gestel word. By exit word hulle teruggesit na read-only / nie-wysigbaar. Dit verseker dat slegs goed-geouditeerde PPL-routines beskermde pages kan skryf.
- Buite PPL sal pogings deur kernel-code om daardie beskermde pages te skryf fault (permission denied) omdat die APRR-mapping vir daardie code-domein nie skryf toestem nie.
#### Beskermde page-kategorieë
Die pages wat PPL tipies beskerm sluit in:
- Page table strukture (translation table entries, mapping metadata)
- Kernel code pages, veral dié met kritieke logika
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Ander hoë-waarde kernel strukture waar ’n patch of herskrywing signature checks of credential-manipulasie sou kan omseil
Die idee is dat selfs as kernel memory volledig beheer word, kan die aanvaller nie eenvoudig hierdie pages patch of oorskryf nie, tensy hulle ook PPL-routines kompromitteer of PPL omseil.
#### Known Bypasses & Vulnerabilities
1. **Project Zero’s PPL bypass (stale TLB trick)**
- ’n Openbare uiteensetting deur Project Zero beskryf ’n bypass wat stale TLB entries misbruik.
- Die idee:
1. Allegeer twee physical pages A en B, merk hulle as PPL pages (sodat hulle beskerm is).
2. Map twee virtual addresses P en Q waarvan die L3 translation table pages van A en B kom.
3. Draai ’n draad wat voortdurend Q besoek, wat sy TLB entry lewendig hou.
4. Roep `pmap_remove_options()` om mappings te verwyder beginnende by P; weens ’n bug verwyder die kode per ongeluk die TTEs vir beide P en Q, maar invalideer slegs die TLB entry vir P, wat Q se stale entry lewendig laat.
5. Hergebruik B (page Q se table) om arbitrêre memory te map (bv. PPL-beskermde pages). Omdat die stale TLB entry steeds Q se ou mapping map, bly daardie mapping geldig vir daardie konteks.
6. Deur dit kan die aanvaller ’n writable mapping van PPL-beskermde pages plaas sonder om via die PPL-interface te werk.
- Hierdie exploit het fyn beheer van physical mapping en TLB-gedrag vereis. Dit toon dat ’n sekuriteitsgrens wat op TLB / mapping korrektheid staatmaak, baie sorgvuldige TLB-invalidasies en mapping-konsistensie nodig het.
- Project Zero het opgemerk dat bypasses soos hierdie subtiel en skaars is, maar in komplekse sisteme moontlik. Desondanks beskou hulle PPL as ’n stewige mitigatie.
2. **Other potential hazards & constraints**
- As ’n kernel-exploit direk PPL-routines kan betree (deur die PPL wrappers aan te roep), kan dit beperkings omseil. Dus is argument-validering kritiek.
- Bugs in die PPL-kode self (bv. arithmetic overflow, boundary checks) kan binne PPL uit-die-grense wysigings toelaat. Project Zero het opgemerk dat so ’n bug in `pmap_remove_options_internal()` in hul bypass uitgebuit is.
- Die PPL-grens is onverbreeklik gebind aan hardware-dwinging (APRR, memory controller), so dit is net so sterk as die hardware-implemantasie.
#### Example
<details>
<summary>Code Example</summary>
Hier is ’n vereenvoudigde pseudocode / logika wat wys hoe ’n kernel dalk in PPL kan aanroep om beskermde pages te wysig:
```c
// In kernel (outside PPL domain)
function kernel_modify_pptable(pt_addr, new_entry) {
// validate arguments, etc.
return ppl_call_modify(pt_addr, new_entry) // call PPL wrapper
}
// In PPL (trusted domain)
function ppl_call_modify(pt_addr, new_entry) {
// temporarily enable write access to protected pages (via APRR adjustments)
aprr_set_index_for_write(PPL_INDEX)
// perform the modification
*pt_addr = new_entry
// restore permissions (make pages read-only again)
aprr_restore_default()
return success
}
// If kernel code outside PPL does:
*pt_addr = new_entry // a direct write
// It will fault because APRR mapping for non-PPL domain disallows write to that page
The kernel can do many normal operations, but only through ppl_call_* routines can it change protected mappings or patch code.
Example
A kernel exploit tries to overwrite the entitlement table, or disable code-sign enforcement by modifying a kernel signature blob. Because that page is PPL-protected, the write is blocked unless going through the PPL interface. So even with kernel code execution, you cannot bypass code-sign constraints or modify credential data arbitrarily. On iOS 17+ certain devices use SPTM to further isolate PPL-managed pages.PPL → SPTM / Replacements / Future
- On Apple’s modern SoCs (A15 or later, M2 or later), Apple supports SPTM (Secure Page Table Monitor), which replaces PPL for page table protections.
- Apple calls out in documentation: “Page Protection Layer (PPL) and Secure Page Table Monitor (SPTM) enforce execution of signed and trusted code … PPL manages the page table permission overrides … Secure Page Table Monitor replaces PPL on supported platforms.”
- The SPTM architecture likely shifts more policy enforcement into a higher-privileged monitor outside kernel control, further reducing the trust boundary.
MTE | EMTE | MIE
Here’s a higher-level description of how EMTE operates under Apple’s MIE setup:
- Tag assignment
- When memory is allocated (e.g. in kernel or user space via secure allocators), a secret tag is assigned to that block.
- The pointer returned to the user or kernel includes that tag in its high bits (using TBI / top byte ignore mechanisms).
- Tag checking on access
- Whenever a load or store is executed using a pointer, the hardware checks that the pointer’s tag matches the memory block’s tag (allocation tag). If mismatch, it faults immediately (since synchronous).
- Because it’s synchronous, there is no “delayed detection” window.
- Retagging on free / reuse
- When memory is freed, the allocator changes the block’s tag (so older pointers with old tags no longer match).
- A use-after-free pointer would therefore have a stale tag and mismatch when accessed.
- Neighbor-tag differentiation to catch overflows
- Adjacent allocations are given distinct tags. If a buffer overflow spills into neighbor’s memory, tag mismatch causes a fault.
- This is especially powerful in catching small overflows that cross boundary.
- Tag confidentiality enforcement
- Apple must prevent tag values being leaked (because if attacker learns the tag, they could craft pointers with correct tags).
- They include protections (microarchitectural / speculative controls) to avoid side-channel leakage of tag bits.
- Kernel and user-space integration
- Apple uses EMTE not just in user-space but also in kernel / OS-critical components (to guard kernel against memory corruption).
- The hardware/OS ensures tag rules apply even when kernel is executing on behalf of user space.
Example
``` 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>
#### Beperkings en uitdagings
- **Intrablock overflows**: As die overflow binne dieselfde toewysing bly (oorgang oor die grens nie plaasvind nie) en die tag dieselfde bly, sal tag mismatch dit nie vang nie.
- **Tag width limitation**: Slegs 'n paar bits (bv. 4 bits, of 'n klein domein) is beskikbaar vir die tag — beperkte naamruimte.
- **Side-channel leaks**: As tag bits via cache / speculative execution geleak kan word, kan 'n aanvaller geldige tags leer en dit omseil. Apple’s tag confidentiality enforcement is bedoel om dit te versag.
- **Performance overhead**: Tag checks by elke load/store voeg koste by; Apple moet die hardware optimaliseer om die oorhoofse koste laag te hou.
- **Compatibility & fallback**: Op ouer hardeware of dele wat nie EMTE ondersteun nie, moet 'n fallback bestaan. Apple beweer MIE is slegs geaktiveer op toestelle met ondersteuning.
- **Complex allocator logic**: Die allocator moet tags, retagging, uitlyningsgrense bestuur en mis-tag botsings vermy. Foute in allocator-logika kan kwesbaarhede inbring.
- **Mixed memory / hybrid areas**: Sommige geheue mag ongetag bly (legacy), wat interoperabiliteit moeiliker maak.
- **Speculative / transient attacks**: Soos met baie mikro-argitektoniese beskermings, kan speculative execution of micro-op fusions kontroles tydelik omseil of tag bits leak.
- **Limited to supported regions**: Apple mag EMTE slegs afdwing in selektiewe, hoërisiko-gebiede (kernel, security-critical subsystems), nie universeel nie.
---
## Sleutelverbeterings / verskille vergeleke met standaard MTE
Hier is die verbeterings en veranderinge wat Apple beklemtoon:
| Kenmerk | Original MTE | EMTE (Apple’s enhanced) / MIE |
|---|---|---|
| **Check mode** | Ondersteun sinchrone en asinchrone modusse. In async word tag mismatches later gerapporteer (vertraag) | Apple beklemtoon **synchronous mode** as verstek — tag mismatches word onmiddellik gevang, geen vertragings-/wedrenvensters toegelaat nie.|
| **Coverage of non-tagged memory** | Toegang tot non-tagged memory (bv. globals) kan kontroles omseil in sommige implementasies | EMTE vereis dat toegang vanaf 'n getagde streek na non-tagged memory ook tag knowledge valideer, wat dit moeiliker maak om te omseil deur allocations te meng.|
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Apple adds **Tag Confidentiality Enforcement**, wat probeer om leakage of tag values te voorkom (via speculative side-channels, ens.).|
| **Allocator integration & retagging** | MTE laat 'n groot deel van die allocator-logika aan die sagteware oor | Apple’s secure typed allocators (kalloc_type, xzone malloc, ens.) integreer met EMTE: wanneer geheue toegeken of vrygemaak word, word tags op fyn granulariteit bestuur.|
| **Always-on by default** | In baie platforms is MTE opsioneel of van verstek af gedeaktiveer | Apple skakel EMTE / MIE standaard aan op ondersteunende hardeware (bv. iPhone 17 / A19) vir kernel en baie user processes.|
Omdat Apple beide die hardeware en sagteware-stapel beheer, kan dit EMTE nou afdwing, prestasievalkrolle vermy en side-channel gapings toemaak.
---
## Hoe EMTE in die praktyk werk (Apple / MIE)
Hier is 'n hoërvlak-beskrywing van hoe EMTE werk onder Apple se MIE-opstelling:
1. **Tag assignment**
- Wanneer geheue toegeken word (bv. in die kernel of user space via secure allocators), word 'n **secret tag** aan daardie blok toegeken.
- Die pointer wat aan die gebruiker of kernel teruggegee word, sluit daardie tag in sy hoë bits in (deur TBI / top byte ignore-meganismes).
2. **Tag checking on access**
- Wanneer 'n load of store uitgevoer word met 'n pointer, kontroleer die hardware dat die pointer se tag ooreenstem met die geheueblok se tag (allocation tag). By mismatch, fault dit onmiddellik (aangesien dit synchronous is).
- Omdat dit synchronous is, is daar geen "vertraagde opsporing" venster nie.
3. **Retagging on free / reuse**
- Wanneer geheue vrygemaak word, verander die allocator die blok se tag (sodat ouer pointers met ou tags nie meer ooreenstem nie).
- 'n Use-after-free pointer sal dus 'n verouderde tag hê en mismatch wanneer dit geaccess word.
4. **Neighbor-tag differentiation to catch overflows**
- Aangrensende allocations kry onderskeibare tags. As 'n buffer overflow in die buurman se geheue spoel, veroorsaak tag mismatch 'n fault.
- Dit is besonder kragtig in die opsporing van klein overflows wat oor 'n grens kruis.
5. **Tag confidentiality enforcement**
- Apple moet voorkom dat tagwaardes geleak word (want as 'n aanvaller die tag leer, kan hy pointers met korrekte tags vervaardig).
- Hulle sluit beskerming in (mikro-argitektonies / speculative kontroles) om side-channel leakage van tag-bits te voorkom.
6. **Kernel and user-space integration**
- Apple gebruik EMTE nie net in user-space nie, maar ook in kernel / OS-kritieke komponente (om die kernel teen geheue-korrupsie te beskerm).
- Die hardware/OS verseker dat tag-reëls van toepassing bly selfs wanneer die kernel namens user space uitvoer.
Omdat EMTE in MIE ingebou is, gebruik Apple EMTE in synchronous mode oor sleutel-attack surfaces, nie as opsionele of debugging-modus nie.
---
## Uitsonderingshantering in XNU
Wanneer 'n **exception** voorkom (bv. `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, ens.), is die Mach-laag van die XNU-kern verantwoordelik om dit te onderskep voordat dit 'n UNIX-styl signal (soos `SIGSEGV`, `SIGBUS`, `SIGILL`, ...) word.
Hierdie proses behels verskeie lae van exception-propagasie en hantering voordat dit gebruikersruimte bereik of na 'n BSD-signal omskep word.
### Uitsonderingsvloei (Hoëvlak)
1. CPU trigger 'n synchronous exception (bv. ongeldig pointer-dereference, PAC failure, illegal instruction, ens.).
2. Laevlak trap handler loop (trap.c, exception.c in XNU source).
3. Die trap handler roep **`exception_triage()`** aan, die kern van die Mach exception handling.
4. `exception_triage()` besluit hoe om die exception te route:
- Eerstens na die **thread's exception port**.
- Dan na die **task's exception port**.
- Dan na die **host's exception port** (dikwels `launchd` of `ReportCrash`).
As geen van hierdie porte die exception hanteer nie, kan die kern:
- Dit omskep na 'n BSD signal (vir user-space prosesse).
- Panic (vir kernel-space exceptions).
### Kernfunksie: `exception_triage()`
Die funksie `exception_triage()` stuur Mach exceptions op deur die ketting van moontlike handlers totdat een dit hanteer of totdat dit finaal fataal is. Dit is gedefinieer in `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);
Tipiese oproepvloei:
exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()
As alles misluk → hanteer deur bsd_exception() → vertaal na ’n sein soos SIGSEGV.
Exception Ports
Elke Mach object (thread, task, host) kan exception ports registreer, waarheen exception messages gestuur word.
Hulle word deur die API gedefinieer:
task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()
Elke exception port het:
- ’n masker (watter exceptions dit wil ontvang)
- ’n poortnaam (Mach port om boodskappe te ontvang)
- ’n gedrag (hoe die kernel die boodskap stuur)
- ’n variant (watter thread state ingesluit word)
Debuggers and Exception Handling
’n debugger (bv. LLDB) stel ’n exception port op die teikentaak of thread, gewoonlik met task_set_exception_ports().
Wanneer ’n exception voorkom:
- Die Mach-boodskap word na die debugger-proses gestuur.
- Die debugger kan besluit om die exception te hanteer (resume, registreer verander, instruksie oorslaan) of nie te hanteer nie.
- As die debugger dit nie hanteer nie, propagereer die exception na die volgende vlak (task → host).
Flow of EXC_BAD_ACCESS
-
Thread dereferensieer ’n ongeldige pointer → CPU gooi Data Abort.
-
Kernel trap handler roep
exception_triage(EXC_BAD_ACCESS, ...). -
Boodskap gestuur na:
-
Thread port → (debugger kan breakpoint onderskep).
-
As debugger ignoreer → Task port → (process-level handler).
-
As dit ignoreer word → Host port (gewoonlik ReportCrash).
- As niemand dit hanteer nie →
bsd_exception()vertaal dit naSIGSEGV.
PAC Exceptions
Wanneer Pointer Authentication (PAC) faal (handtekening-wedstryding), word ’n spesiale Mach-exception gegooi:
EXC_ARM_PAC(tipe)- Kodes kan besonderhede insluit (bv. sleuteltipe, pointertipe).
As die binary die vlag TFRO_PAC_EXC_FATAL het, behandel die kernel PAC-foute as fatal, wat debugger-onderskep omseil. Dit is om te verhoed dat aanvallers debuggers gebruik om PAC-checks te omseil en dit is aangeskakel vir platform binaries.
Software Breakpoints
’n software breakpoint (int3 op x86, brk op ARM64) word geïmplementeer deur ’n doelbewuste fout te veroorsaak.
Die debugger vang dit via die exception port:
- Verander die instruction pointer of geheue.
- Herstel oorspronklike instruksie.
- Hervat uitvoering.
Dieselfde meganisme laat jou toe om ’n PAC-exception te “vang” — tensy TFRO_PAC_EXC_FATAL gestel is, in welk geval dit nooit by die debugger uitkom nie.
Conversion to BSD Signals
As geen handler die exception aanvaar nie:
-
Kernel roep
task_exception_notify() → bsd_exception(). -
Dit karteer Mach-exceptions na signals:
| Mach Exception | Signal |
|---|---|
| EXC_BAD_ACCESS | SIGSEGV or SIGBUS |
| EXC_BAD_INSTRUCTION | SIGILL |
| EXC_ARITHMETIC | SIGFPE |
| EXC_SOFTWARE | SIGTRAP |
| EXC_BREAKPOINT | SIGTRAP |
| EXC_CRASH | SIGKILL |
| EXC_ARM_PAC | SIGILL (on non-fatal) |
### Key Files in XNU Source
-
osfmk/kern/exception.c→ Kern vanexception_triage(),exception_deliver_*(). -
bsd/kern/kern_sig.c→ Logika vir signal-aflewering. -
osfmk/arm64/trap.c→ Lae-vlak trap handlers. -
osfmk/mach/exc.h→ Exception kodes en strukture. -
osfmk/kern/task.c→ Task exception port opstelling.
Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)
Die kernel het ’n zone allocator (kalloc) gebruik wat in vaste-grootte “zones” verdeel is.
Elke zone berg slegs allocations van ’n enkele grootte-klas.
Uit die skermskoot:
| Zone Name | Element Size | Example Use |
|---|---|---|
default.kalloc.16 | 16 bytes | Baie klein kernel structs, pointers. |
default.kalloc.32 | 32 bytes | Klein structs, object headers. |
default.kalloc.64 | 64 bytes | IPC messages, baie klein kernel buffers. |
default.kalloc.128 | 128 bytes | Medium objects soos dele van OSObject. |
| … | … | … |
default.kalloc.1280 | 1280 bytes | Groot strukture, IOSurface/graphics metadata. |
Hoe dit gewerk het:
- Elke allocation versoek word opgerond na die naaste zone-grootte.
(Bv., ’n 50-byte versoek gaan in die
kalloc.64zone). - Geheue in elke zone is in ’n freelist gehou — stukke wat deur die kernel vrygestel is, het teruggegaan na daardie zone.
- As jy ’n 64-byte buffer overflow het, sou jy die volgende object in dieselfde zone oor-skryf.
Hierdie is waarom heap spraying / feng shui so effektief was: jy kon objek-buurmanne voorspel deur allocations van dieselfde grootte-klas te spuit.
The freelist
Binne elke kalloc zone is vrygestelde objekte nie direk teruggegee aan die stelsel nie — hulle het in ’n freelist gegaan, ’n gekoppelde lys van beskikbare stukke.
-
Toe ’n stuk vrygestel is, het die kernel ’n pointer aan die begin van daardie stuk geskryf → die adres van die volgende vrye stuk in dieselfde zone.
-
Die zone het ’n HEAD pointer gehou na die eerste vrye stuk.
-
Allocation het altyd die huidige HEAD gebruik:
-
Pop HEAD (gee daardie geheue terug aan die aanroeper).
-
Update HEAD = HEAD->next (gestoor in die vrygestelde stuk se header).
-
Freeing het stukke teruggedruk:
-
freed_chunk->next = HEAD -
HEAD = freed_chunk
Dus was die freelist net ’n gekoppelde lys gebou binne die vrygestelde geheue self.
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)
Exploiting the freelist
Omdat die eerste 8 bytes van ’n free chunk die freelist pointer is, kan ’n aanvaller dit korrupteer:
-
Heap overflow in ’n aangrensende freed chunk → overskryf sy “next” pointer.
-
Use-after-free skryf in ’n freed object → overskryf sy “next” pointer.
Dan, by die volgende toewysing van daardie grootte:
- Die allocator pop die gekorrupte chunk.
- Volg die deur die aanvaller verskafde “next” pointer.
- Gee ’n pointer terug na arbitrêre geheue, wat fake object primitives of targeted overwrite moontlik maak.
Visuele voorbeeld van freelist poisoning:
Before corruption:
HEAD ──► [ F1 ] ──► [ F2 ] ──► [ F3 ] ──► NULL
After attacker overwrite of F1->next:
HEAD ──► [ F1 ]
(next) ──► 0xDEAD_BEEF_CAFE_BABE (attacker-chosen)
Next alloc of this zone → kernel hands out memory at attacker-controlled address.
This freelist design made exploitation highly effective pre-hardening: predictable neighbors from heap sprays, raw pointer freelist links, and no type separation allowed attackers to escalate UAF/overflow bugs into arbitrary kernel memory control.
Heap Grooming / Feng Shui
The goal of heap grooming is to shape the heap layout so that when an attacker triggers an overflow or use-after-free, the target (victim) object sits right next to an attacker-controlled object.
That way, when memory corruption happens, the attacker can reliably overwrite the victim object with controlled data.
Steps:
- Spray allocations (fill the holes)
- Over time, the kernel heap gets fragmented: some zones have holes where old objects were freed.
- The attacker first makes lots of dummy allocations to fill these gaps, so the heap becomes “packed” and predictable.
- Force new pages
- Once the holes are filled, the next allocations must come from new pages added to the zone.
- Fresh pages mean objects will be clustered together, not scattered across old fragmented memory.
- This gives the attacker much better control of neighbors.
- Place attacker objects
- The attacker now sprays again, creating lots of attacker-controlled objects in those new pages.
- These objects are predictable in size and placement (since they all belong to the same zone).
- Free a controlled object (make a gap)
- The attacker deliberately frees one of their own objects.
- This creates a “hole” in the heap, which the allocator will later reuse for the next allocation of that size.
- Victim object lands in the hole
- The attacker triggers the kernel to allocate the victim object (the one they want to corrupt).
- Since the hole is the first available slot in the freelist, the victim is placed exactly where the attacker freed their object.
- Overflow / UAF into victim
- Now the attacker has attacker-controlled objects around the victim.
- By overflowing from one of their own objects (or reusing a freed one), they can reliably overwrite the victim’s memory fields with chosen values.
Why it works:
- Zone allocator predictability: allocations of the same size always come from the same zone.
- Freelist behavior: new allocations reuse the most recently freed chunk first.
- Heap sprays: attacker fills memory with predictable content and controls layout.
- End result: attacker controls where the victim object lands and what data sits next to it.
Modern Kernel Heap (iOS 15+/A12+ SoCs)
Apple hardened the allocator and made heap grooming much harder:
1. From Classic kalloc to kalloc_type
- Before: a single
kalloc.<size>zone existed for each size class (16, 32, 64, … 1280, etc.). Any object of that size was placed there → attacker objects could sit next to privileged kernel objects. - Now:
- Kernel objects are allocated from typed zones (
kalloc_type). - Each type of object (e.g.,
ipc_port_t,task_t,OSString,OSData) has its own dedicated zone, even if they’re the same size. - The mapping between object type ↔ zone is generated from the kalloc_type system at compile time.
An attacker can no longer guarantee that controlled data (OSData) ends up adjacent to sensitive kernel objects (task_t) of the same size.
2. Slabs and Per-CPU Caches
- The heap is divided into slabs (pages of memory carved into fixed-size chunks for that zone).
- Each zone has a per-CPU cache to reduce contention.
- Allocation path:
- Try per-CPU cache.
- If empty, pull from the global freelist.
- If freelist is empty, allocate a new slab (one or more pages).
- Benefit: This decentralization makes heap sprays less deterministic, since allocations may be satisfied from different CPUs’ caches.
3. Randomization inside zones
- Within a zone, freed elements are not handed back in simple FIFO/LIFO order.
- Modern XNU uses encoded freelist pointers (safe-linking like Linux, introduced ~iOS 14).
- Each freelist pointer is XOR-encoded with a per-zone secret cookie.
- This prevents attackers from forging a fake freelist pointer if they gain a write primitive.
- Some allocations are randomized in their placement within a slab, so spraying doesn’t guarantee adjacency.
4. Guarded Allocations
- Certain critical kernel objects (e.g., credentials, task structures) are allocated in guarded zones.
- These zones insert guard pages (unmapped memory) between slabs or use redzones around objects.
- Any overflow into the guard page triggers a fault → immediate panic instead of silent corruption.
5. Page Protection Layer (PPL) and SPTM
- Even if you control a freed object, you can’t modify all of kernel memory:
- PPL (Page Protection Layer) enforces that certain regions (e.g., code signing data, entitlements) are read-only even to the kernel itself.
- On A15/M2+ devices, this role is replaced/enhanced by SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
- These hardware-enforced layers mean attackers can’t escalate from a single heap corruption to arbitrary patching of critical security structures.
- (Added / Enhanced): also, PAC (Pointer Authentication Codes) is used in the kernel to protect pointers (especially function pointers, vtables) so that forging or corrupting them becomes harder.
- (Added / Enhanced): zones may enforce zone_require / zone enforcement, i.e. that an object freed can only be returned through its correct typed zone; invalid cross-zone frees may panic or be rejected. (Apple alludes to this in their memory safety posts)
6. Large Allocations
- Not all allocations go through
kalloc_type. - Very large requests (above ~16 KB) bypass typed zones and are served directly from kernel VM (kmem) via page allocations.
- These are less predictable, but also less exploitable, since they don’t share slabs with other objects.
7. Allocation Patterns Attackers Target
Even with these protections, attackers still look for:
- Reference count objects: if you can tamper with retain/release counters, you may cause use-after-free.
- Objects with function pointers (vtables): corrupting one still yields control flow.
- Shared memory objects (IOSurface, Mach ports): these are still attack targets because they bridge user ↔ kernel.
But — unlike before — you can’t just spray OSData and expect it to neighbor a task_t. You need type-specific bugs or info leaks to succeed.
Example: Allocation Flow in Modern Heap
Suppose userspace calls into IOKit to allocate an OSData object:
- Type lookup →
OSDatamaps tokalloc_type_osdatazone (size 64 bytes). - Check per-CPU cache for free elements.
- If found → return one.
- If empty → go to global freelist.
- If freelist empty → allocate a new slab (page of 4KB → 64 chunks of 64 bytes).
- Return chunk to caller.
Freelist pointer protection:
- Each freed chunk stores the address of the next free chunk, but encoded with a secret key.
- Overwriting that field with attacker data won’t work unless you know the key.
Comparison Table
| Feature | Old Heap (Pre-iOS 15) | Modern Heap (iOS 15+ / A12+) |
|---|---|---|
| Allocation granularity | Fixed size buckets (kalloc.16, kalloc.32, etc.) | Size + type-based buckets (kalloc_type) |
| Placement predictability | High (same-size objects side by side) | Low (same-type grouping + randomness) |
| Freelist management | Raw pointers in freed chunks (easy to corrupt) | Encoded pointers (safe-linking style) |
| Adjacent object control | Easy via sprays/frees (feng shui predictable) | Hard — typed zones separate attacker objects |
| Kernel data/code protections | Few hardware protections | PPL / SPTM protect page tables & code pages, and PAC protects pointers |
| Allocation reuse validation | None (freelist pointers raw) | zone_require / zone enforcement |
| Exploit reliability | High with heap sprays | Much lower, requires logic bugs or info leaks |
| Large allocations handling | All small allocations managed equally | Large ones bypass zones → handled via VM |
Modern Userland Heap (iOS, macOS — type-aware / xzone malloc)
In recent Apple OS versions (especially iOS 17+), Apple introduced a more secure userland allocator, xzone malloc (XZM). This is the user-space analog to the kernel’s kalloc_type, applying type awareness, metadata isolation, and memory tagging safeguards.
Goals & Design Principles
- Type segregation / type awareness: group allocations by type or usage (pointer vs data) to prevent type confusion and cross-type reuse.
- Metadata isolation: separate heap metadata (e.g. free lists, size/state bits) from object payloads so that out-of-bounds writes are less likely to corrupt metadata.
- Guard pages / redzones: insert unmapped pages or padding around allocations to catch overflows.
- Memory tagging (EMTE / MIE): work in conjunction with hardware tagging to detect use-after-free, out-of-bounds, and invalid accesses.
- Scalable performance: maintain low overhead, avoid excessive fragmentation, and support many allocations per second with low latency.
Architecture & Components
Below are the main elements in the xzone allocator:
Segment Groups & Zones
- Segment groups partition the address space by usage categories: e.g.
data,pointer_xzones,data_large,pointer_large. - Each segment group contains segments (VM ranges) that host allocations for that category.
- Associated with each segment is a metadata slab (separate VM area) that stores metadata (e.g. free/used bits, size classes) for that segment. This out-of-line (OOL) metadata ensures that metadata is not intermingled with object payloads, mitigating corruption from overflows.
- Segments are carved into chunks (slices) which in turn are subdivided into blocks (allocation units). A chunk is tied to a specific size class and segment group (i.e. all blocks in a chunk share the same size & category).
- For small / medium allocations, it will use fixed-size chunks; for large/huges, it may map separately.
Chunks & Blocks
- A chunk is a region (often several pages) dedicated to allocations of one size class within a group.
- Inside a chunk, blocks are slots available for allocations. Freed blocks are tracked via the metadata slab — e.g. via bitmaps or free lists stored out-of-line.
- Between chunks (or within), guard slices / guard pages may be inserted (e.g. unmapped slices) to catch out-of-bounds writes.
Type / Type ID
- Every allocation site (or call to malloc, calloc, etc.) is associated with a type identifier (a
malloc_type_id_t) which encodes what kind of object is being allocated. That type ID is passed to the allocator, which uses it to select which zone / segment to serve the allocation. - Because of this, even if two allocations have the same size, they may go into entirely different zones if their types differ.
- In early iOS 17 versions, not all APIs (e.g. CFAllocator) were fully type-aware; Apple addressed some of those weaknesses in iOS 18.
Allocation & Freeing Workflow
Here is a high-level flow of how allocation and deallocation operate in xzone:
- malloc / calloc / realloc / typed alloc is invoked with a size and type ID.
- The allocator uses the type ID to pick the correct segment group / zone.
- Within that zone/segment, it seeks a chunk that has free blocks of the requested size.
- It may consult local caches / per-thread pools or free block lists from metadata.
- If no free block is available, it may allocate a new chunk in that zone.
- The metadata slab is updated (free bit cleared, bookkeeping).
- If memory tagging (EMTE) is in play, the returned block gets a tag assigned, and metadata is updated to reflect its “live” state.
- When
free()is called:
- The block is marked as freed in metadata (via OOL slab).
- The block may be placed into a free list or pooled for reuse.
- Optionally, block contents may be cleared or poisoned to reduce data leaks or use-after-free exploitation.
- The hardware tag associated with the block may be invalidated or re-tagged.
- If an entire chunk becomes free (all blocks freed), the allocator may reclaim that chunk (unmap it or return to OS) under memory pressure.
Security Features & Hardening
These are the defenses built into modern userland xzone:
| Feature | Purpose | Notes |
|---|---|---|
| Metadata decoupling | Prevent overflow from corrupting metadata | Metadata lives in separate VM region (metadata slab) |
| Guard pages / unmapped slices | Catch out-of-bounds writes | Helps detect buffer overflows rather than silently corrupting adjacent blocks |
| Type-based segregation | Prevent cross-type reuse & type confusion | Even same-size allocations from different types go to different zones |
| Memory Tagging (EMTE / MIE) | Detect invalid access, stale references, OOB, UAF | xzone works in concert with hardware EMTE in synchronous mode (“Memory Integrity Enforcement”) |
| Delayed reuse / poisoning / zap | Reduce chance of use-after-free exploitation | Freed blocks may be poisoned, zeroed, or quarantined before reuse |
| Chunk reclamation / dynamic unmapping | Reduce memory waste and fragmentation | Entire chunks may be unmapped when unused |
| Randomization / placement variation | Prevent deterministic adjacency | Blocks in a chunk and chunk selection may have randomized aspects |
| Segregation of “data-only” allocations | Separate allocations that don’t store pointers | Reduces attacker control over metadata or control fields |
Interaction with Memory Integrity Enforcement (MIE / EMTE)
- Apple’s MIE (Memory Integrity Enforcement) is the hardware + OS framework that brings Enhanced Memory Tagging Extension (EMTE) into always-on, synchronous mode across major attack surfaces.
- xzone allocator is a fundamental foundation of MIE in user space: allocations done via xzone get tags, and accesses are checked by hardware.
- In MIE, the allocator, tag assignment, metadata management, and tag confidentiality enforcement are integrated to ensure that memory errors (e.g. stale reads, OOB, UAF) are caught immediately, not exploited later.
- If you like, I can also generate a cheat-sheet or diagram of xzone internals for your book. Do you want me to do that next?
- :contentReference[oai:20]{index=20}
(Old) Physical Use-After-Free via IOSurface
Ghidra Install BinDiff
Download BinDiff DMG from https://www.zynamics.com/bindiff/manual and install it.
Open Ghidra with ghidraRun and go to File –> Install Extensions, press the add button and select the path /Applications/BinDiff/Extra/Ghidra/BinExport and click OK and isntall it even if there is a version mismatch.
Using BinDiff with Kernel versions
- Go to the page https://ipsw.me/ and download the iOS versions you want to diff. These will be
.ipswfiles. - Decompress until you get the bin format of the kernelcache of both
.ipswfiles. You have information on how to do this on:
macOS Kernel Extensions & Kernelcache
- Open Ghidra with
ghidraRun, create a new project and load the kernelcaches. - Open each kernelcache so they are automatically analyzed by Ghidra.
- Then, on the project Window of Ghidra, right click each kernelcache, select
Export, select formatBinary BinExport (v2) for BinDiffand export them. - Open BinDiff, create a new workspace and add a new diff indicating as primary file the kernelcache that contains the vulnerability and as secondary file the patched kernelcache.
Finding the right XNU version
If you want to check for vulnerabilities in a specific version of iOS, you can check which XNU release version the iOS version uses at [https://www.theiphonewiki.com/wiki/kernel]https://www.theiphonewiki.com/wiki/kernel).
For example, the versions 15.1 RC, 15.1 and 15.1.1 use the version Darwin Kernel Version 21.1.0: Wed Oct 13 19:14:48 PDT 2021; root:xnu-8019.43.1~1/RELEASE_ARM64_T8006.
JSKit-Based Safari Chains and PREYHUNTER Stagers
Renderer RCE abstraction with JSKit
- Reusable entry: Recent in-the-wild chains abused a WebKit JIT bug (patched as CVE-2023-41993) purely to gain JavaScript-level arbitrary read/write. The exploit immediately pivots into a purchased framework called JSKit, so any future Safari bug only needs to deliver the same primitive.
- Version abstraction & PAC bypasses: JSKit bundles support for a wide range of iOS releases together with multiple, selectable Pointer Authentication Code bypass modules. The framework fingerprints the target build, selects the appropriate PAC bypass logic, and verifies every step (primitive validation, shellcode launch) before progressing.
- Manual Mach-O mapping: JSKit parses Mach-O headers directly from memory, resolves the symbols it needs inside dyld-cached images, and can manually map additional Mach-O payloads without writing them to disk. This keeps the renderer process in-memory only and evades code-signature checks tied to filesystem artifacts.
- Portfolio model: Debug strings such as “exploit number 7” show that the suppliers maintain multiple interchangeable WebKit exploits. Once the JS primitive matches JSKit’s interface, the rest of the chain is unchanged across campaigns.
Kernel bridge: IPC UAF -> code-sign bypass pattern
- Kernel IPC UAF (CVE-2023-41992): The second stage, still running inside the Safari context, triggers a kernel use-after-free in IPC code, re-allocates the freed object from userland, and abuses the dangling pointers to pivot into arbitrary kernel read/write. The stage also reuses PAC bypass material previously computed by JSKit instead of re-deriving it.
- Code-signing bypass (CVE-2023-41991): With kernel R/W available, the exploit patches the trust cache / code-signing structures so unsigned payloads execute as
system. The stage then exposes a lightweight kernel R/W service to later payloads. - Composed pattern: This chain demonstrates a reusable recipe that defenders should expect going forward:
WebKit renderer RCE -> kernel IPC UAF -> kernel arbitrary R/W -> code-sign bypass -> unsigned system stager
PREYHUNTER helper & watcher modules
- Watcher anti-analysis: ’n toegewyde watcher binary profiliseer die toestel voortdurend en staak die kill-chain wanneer ’n research environment opgespoor word. Dit ondersoek
security.mac.amfi.developer_mode_status, die teenwoordigheid van ’ndiagnosticdconsole, localesUSofIL, jailbreak-spore soos Cydia, prosesse soosbash,tcpdump,frida,sshd, ofcheckrain, mobile AV apps (McAfee, AvastMobileSecurity, NortonMobileSecurity), aangepaste HTTP proxy-instellings, en aangepaste root CAs. Indien enige kontrole misluk, blokkeer dit verdere payload delivery. - Helper surveillance hooks: Die helper-komponent kommunikeer met ander fases deur
/tmp/helper.sock, en laai daarna hook-sets met die name DMHooker en UMHooker. Hierdie hooks tap VOIP audio paths (opnames beland onder/private/var/tmp/l/voip_%lu_%u_PART.m4a), implementeer ’n system-wide keylogger, neem fotos sonder UI, en hook SpringBoard om kennisgewings wat daardie aksies normaalweg sou veroorsaak te onderdruk. Die helper tree dus op as ’n sluipende validasie- + ligte-toesig-laag voordat swaarder implants soos Predator gelaai word.
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
Leer en oefen AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Leer en oefen Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Ondersteun HackTricks
- Kyk na die subskripsie planne!
- Sluit aan by die 💬 Discord groep of die telegram groep of volg ons op Twitter 🐦 @hacktricks_live.
- Deel hacking truuks deur PRs in te dien na die HackTricks en HackTricks Cloud github repos.


