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 Mitigasies
1. Code Signing / Runtime Signature Verification
Wat vroeg ingestel is (iPhone OS → iOS) Dit is een van die fundamentele beskermings: alle uitvoerbare kode (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) moet kriptografies geteken wees deur ’n sertifikaatketting wat in Apple se vertroue gewortel is. Tydens runtime, voordat ’n binary in geheue gelaai word (of voordat spronge oor sekere grense gemaak word), kontroleer die stelsel die handtekening. As die kode gemodifiseer (bit-flipped, gepatch) of ongehandteken is, misluk die laai.
- Voorkom: die “classic payload drop + execute” fase in exploit chains; arbitrary code injection; die wysiging van ’n bestaande binary om kwaadwillige logika in te voeg.
- Meganisme besonderhede:
- 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 geteken of gevalideer word via spesiale APIs (bv.
mprotectmet code-sign checks). - Die handtekening sluit entitlements en identiteite in; die OS dwing af dat sekere APIs of bevoorregte vermoëns spesifieke entitlements vereis wat nie vervals kan word nie.
Example
Stel 'n exploit kry code execution in 'n proses en probeer shellcode in 'n heap skryf en daarna daarna spring. Op iOS sou daardie bladsy uitvoerbaar gemerk moet wees **en** code-signature vereistes moet voldoe. Omdat die shellcode nie deur Apple se sertifikaat geteken is nie, misluk die sprong of verwerp die stelsel om daardie geheuegebied uitvoerbaar te maak.2. CoreTrust
Ingevoer rond die iOS 14+ era (of geleidelik op nuwer toestelle / later iOS) CoreTrust is die subsisteem wat runtime signature validation van binaries uitvoer (insluitend stelsel- en gebruikersbinaries) teen Apple se root certificate in plaas van om op gecachte userland trust stores staat te maak.
- Voorkom: post-install tampering van binaries, jailbreaking-tegnieke wat probeer om system libraries of user apps te vervang of te patch; die bedrieg van die stelsel deur vertroude binaries met kwaadwillige eweknieë te vervang.
- Meganisme besonderhede:
- In plaas van ’n plaaslike trust database of sertifikaat-cache te vertrou, haal CoreTrust Apple se root direk of verifieer intermediate certificates in ’n veilige ketting.
- Dit verseker dat wysigings (bv. in die filesystem) aan bestaande binaries opgespoor en verwerp word.
- Dit koppel entitlements, team IDs, code signing flags, en ander metadata aan die binary by laai-tyd.
Example
'n Jailbreak kan probeer om `SpringBoard` of `libsystem` te vervang met 'n gepatchte weergawe om persistentie te kry. Maar wanneer die OS se loader of CoreTrust kontroleer, sien dit die handtekening-mismatch (of gemodifiseerde entitlements) en weier om dit uit te voer.3. Data Execution Prevention (DEP / NX / W^X)
Ingevoer in baie OS’e vroeër; iOS het NX-bit / w^x al lank DEP dwing dat pages wat as writable (vir data) gemerk is, nie-uitvoerbaar is, en pages wat as uitvoerbaar gemerk is, nie-skrifbaar is. Jy kan nie net shellcode in ’n heap of stack skryf en dit uitvoer nie.
- Voorkom: direkte shellcode-uitvoering; klassieke buffer-overflow → sprong na ingevoerde shellcode.
- Meganisme besonderhede:
- Die MMU / memory protection flags (via page tables) handhaaf die skeiding.
- Enige poging om ’n skryfbare bladsy uitvoerbaar te maak, trigger ’n stelselkontrole (en is óf verbode óf vereis code-sign approval).
- In baie gevalle vereis dit dat pages uitvoerbaar gemaak word deur OS APIs wat addisionele beperkings 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 bladsy moet slaag volgens code-sign constraints (wat die shellcode nie kan nie).4. Address Space Layout Randomization (ASLR)
Ingevoer in iOS ~4–5 era (ongeveer iOS 4–5 tydraam) ASLR randomiseer die basisadresse van sleutel geheuegebiede: libraries, heap, stack, ens., by elke proses-launch. Gadgets se adresse beweeg tussen uitvaardigings.
- Voorkom: hardcoding van gadget-adresse vir ROP/JOP; statiese exploit chains; blind spronge na bekende offsets.
- Meganisme besonderhede:
- Elke gelaaide library / dynamic module word by ’n randomiseerde offset herbasis.
- Stack en heap basispunte word gerandomiseer (binne sekere entropy perke).
- Soms word ander gebiede (bv. mmap allocations) ook gerandomiseer.
- Gecombineer met information-leak mitigations, dwing dit die aanvaller om eers ’n address of pointer te leak om basisadresse tydens runtime te ontdek.
Example
'n ROP-ketting verwag gadget by `0x….lib + offset`. Maar aangesien `lib` elke run anders hergebaseer word, misluk die hardcoded ketting. 'n Exploit moet eers die base address van die module leak voordat dit gadget-adresse bereken.5. Kernel Address Space Layout Randomization (KASLR)
Ingevoer in iOS ~ (iOS 5 / iOS 6 tydraam) Analoog aan user ASLR, KASLR randomiseer die basis van die kernel text en ander kernel strukture by boot-tyd.
- Voorkom: kernel-level exploits wat staatmaak op vaste lokasies van kernel-kode of data; statiese kernel exploits.
- Meganisme besonderhede:
- By elke boot word die kernel se basisadres gerandomiseer (binne ’n reeks).
- Kernel data strukture (soos
task_structs,vm_map, ens.) kan ook herlê of verskuif word. - Aanvallers moet eers kernel pointers leak of info disclosure vulnerabilities gebruik om offsets te bereken voordat hulle kernel strukture of kode kap.
Example
'n Lokale kwetsbaarheid mik om 'n kernel function pointer (bv. in `vtable`) te korrupteer by `KERN_BASE + offset`. Maar aangesien `KERN_BASE` onbekend is, moet die aanvaller eers dit leak (bv. via 'n read primitive) voordat hy die korrekte adres vir korrupsie kan bereken.6. Kernel Patch Protection (KPP / AMCC)
Ingevoer in nuwer iOS / A-series hardware (post ongeveer iOS 15–16 era of nuwer chips) KPP (ook bekend as AMCC) monitor kontinu die integriteit van kernel text pages (via hash of checksum). As dit tampering (patches, inline hooks, code modifications) buite toegelate vensters opspoor, trigger dit ’n kernel panic of herbegin.
- Voorkom: persistent kernel patching (wysiging van kernel instruksies), inline hooks, statiese funksie-overskrywings.
- Meganisme besonderhede:
- ’n Hardware of firmware module monitor die kernel text area.
- Dit re-hash periodies of op aanvraag die pages en vergelyk teen verwagte waardes.
- Indien mismatches plaasvind buite benign update-vensters, panikeer dit die toestel (om persistent kwaadwillige patch te voorkom).
- Aanvallers moet óf opsporingsvensters vermy óf legitieme patch-paaie gebruik.
Example
'n Exploit probeer 'n kernel funksie proloog patch (bv. `memcmp`) om oproepe te onderskep. Maar KPP opmerk dat die code page se hash nie meer met die verwagte waarde ooreenstem nie en trigger 'n kernel panic, wat die toestel laat crash voordat die patch kan stabiliseer.7. Kernel Text Read‐Only Region (KTRR)
Ingevoer in moderne SoCs (post ~A12 / nuwer hardware) KTRR is ’n hardware-afgedwonge meganisme: sodra die kernel text vroeg in die boot gesluit word, word dit read-only vanaf EL1 (die kernel), wat verdere skryf na code pages voorkom.
- Voorkom: enige wysigings aan kernel kode na boot (bv. patching, in-place code injection) op EL1 privilege vlak.
- Meganisme besonderhede:
- Tydens boot (in secure/bootloader fase) 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 voorregte kry, kan dit nie daardie pages skryf om instruksies te patch nie.
- Om hulle te wysig, moet die aanvaller eers die boot-ketting kompromitteer, of KTRR self subverteer.
Example
'n Privilege-escalation exploit spring na EL1 en probeer 'n trampoline skryf in 'n kernel funksie (bv. in `syscall` handler). Maar omdat die pages deur KTRR read-only geanker is, misluk die skryf (of trigger 'n fout), so patches word nie toegepas nie.8. Pointer Authentication Codes (PAC)
Ingevoer met ARMv8.3 (hardware), Apple begin met A12 / iOS ~12+
- PAC is ’n hardware-kenmerk wat in ARMv8.3-A ingestel is om die manipulasie van pointer waardes (return addresses, function pointers, sekere datapointers) op te spoor deur ’n klein kriptografiese handtekening (/’n “MAC”) in ongebruikte hoë-bitte van die pointer in te sluit.
- Die handtekening (“PAC”) word bereken oor die pointer waarde plus ’n modifier (/’n kontekstuele waarde, bv. stack pointer of ander onderskeidende data). Op daardie manier kry dieselfde pointer waarde in verskillende kontekste ’n verskillende PAC.
- Tydens gebruik, voor dereference of branching via daardie pointer, voer ’n authenticate instruksie ’n check op die PAC uit. As dit geldig is, word die PAC verwyder en die suiwer pointer verkry; as dit ongeldig is, word die pointer “poisoned” (of ’n fout opgegooi).
- Die sleutels wat vir die produksie/validasie van PAC’s gebruik word, woon in voorregte registers (EL1, kernel) en is nie direk leesbaar vanaf user mode nie.
- Omdat nie al 64 bite van ’n pointer in baie stelsels gebruik word nie (bv. 48-bit address space), is die boonste bite “oordadig” en kan die PAC hou sonder om die effektiewe adres te verander.
Arkitektoniese Basis & Sleutelsoorte
-
ARMv8.3 stel vyf 128-bit sleutels bekend (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 signing van nie-pointer data of ander generiese gebruike
-
Hierdie sleutels word in voorregte stelselregisters gestoor (toeganklik slegs by EL1/EL2 ens.), nie toeganklik vanaf user mode nie.
-
Die PAC word bereken via ’n kriptografiese funksie (ARM stel QARMA as die algoritme voor) wat gebruik:
- Die pointer waarde (kanoniese deel)
- ’n modifier (/’n kontekstuele waarde, soos ’n salt)
- Die geheime sleutel
- ’n Bietjie interne tweak-logika As die resulterende PAC ooreenstem met wat in die boonste bite van die pointer gestoor is, slaag authenticatie.
Instruksiefamilies
Die naamkonvensie is: PAC / AUT / XPAC, dan domeinletters.
PACxxinstruksies sign ’n pointer en voeg ’n PAC inAUTxxinstruksies authenticate + strip (valideer en verwyder die PAC)XPACxxinstruksies strip sonder validasie
Domeine / suffixes:
| Mnemonic | Meaning / Domein | Sleutel / Domein | Voorbeeld Gebruik in Assembly |
|---|---|---|---|
| PACIA | Sign instruction pointer met APIAKey | “I, A” | PACIA X0, X1 — sign pointer in X0 using APIAKey with modifier X1 |
| PACIB | Sign instruction pointer met APIBKey | “I, B” | PACIB X2, X3 |
| PACDA | Sign data pointer met APDAKey | “D, A” | PACDA X4, X5 |
| PACDB | Sign data pointer met APDBKey | “D, B” | PACDB X6, X7 |
| PACG / PACGA | Generic (nie-pointer) signing met APGAKey | “G” | PACGA X8, X9, X10 (sign X9 with modifier X10 into X8) |
| AUTIA | Authenticate APIA-gesignte instruction pointer & strip PAC | “I, A” | AUTIA X0, X1 — check PAC on X0 using modifier X1, then strip |
| AUTIB | Authenticate APIB domein | “I, B” | AUTIB X2, X3 |
| AUTDA | Authenticate APDA-gesignte data pointer | “D, A” | AUTDA X4, X5 |
| AUTDB | Authenticate APDB-gesignte data pointer | “D, B” | AUTDB X6, X7 |
| AUTGA | Authenticate generic / blob (APGA) | “G” | AUTGA X8, X9, X10 (validate generic) |
| XPACI | Strip PAC (instruction pointer, geen validasie) | “I” | XPACI X0 — verwyder PAC uit X0 (instruction domein) |
| XPACD | Strip PAC (data pointer, geen validasie) | “D” | XPACD X4 — verwyder PAC van data pointer in X4 |
Daar is gespesialiseerde / alias vorms:
PACIASPis afkorting virPACIA X30, SP(sign die link register met SP as modifier)AUTIASPisAUTIA X30, SP(authenticate link register met SP)- Gekombineerde vorms soos
RETAA,RETAB(authenticate-and-return) ofBLRAA(authenticate & branch) bestaan in ARM uitbreidinge / compiler ondersteuning. - Ook nul-modifier variante:
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 sal hê. Dit verhoed eenvoudige pointer reuse oor frames of objekte. Dit is soos om ’n salt by ’n hash te voeg.
Dus:
- Die modifier is ’n kontekstuele waarde (nog ’n register) wat in die PAC-berekening gemeng word. Tipiese keuses: die stack pointer (
SP), ’n frame pointer, of ’n objek-ID. - Om SP as modifier te gebruik is algemeen vir return address signing: die PAC word gesluit aan die spesifieke stack frame. As jy probeer om die LR in ’n ander frame te hergebruik, verander die modifier, dus fail PAC-validasie.
- Dieselfde pointer waarde gesign onder verskillende modifiers gee verskillende PACs.
- Die modifier hoef nie geheim te wees nie, maar idealiter is dit nie deur ’n aanvaller beheer nie.
- Vir instruksies wat pointers teken of verifieer waar geen sinvolle modifier bestaan nie, gebruik sommige vorms nul of ’n implisiete konstante.
Apple / iOS / XNU Aanpassings & Waarnemings
- Apple se PAC-implementasie sluit per-boot diversifiers in sodat sleutels of tweaks by elke boot verander, wat hergebruik oor boots voorkom.
- Hulle sluit ook cross-domain mitigations in sodat PACs wat in user mode gesign is nie maklik in kernel mode hergebruik kan word nie, ens.
- Op Apple M1 / Apple Silicon het reverse engineering getoon dat daar nege modifier tipes is en Apple-spesifieke stelselregisters vir sleutelbeheer.
- Apple gebruik PAC in baie kernel-substelsels: return address signing, pointer integriteit in kernel data, signed thread contexts, ens.
- Google Project Zero het getoon hoe onder ’n kragtige memory read/write primitive in die kernel, iemand kernel PACs (vir A sleutels) kon forgeer op A12-era toestelle, maar Apple het baie van daardie paaie gepatch.
- In Apple se stelsel is sommige sleutels globaal oor die kernel, terwyl user prosesse moontlik per-proses sleutel randomisering kry.
PAC Bypasses
- Kernel-mode PAC: teoreties vs werklike bypasses
- Omdat kernel PAC sleutels en logika styf gekontroleer word (voorregte registers, diversifiers, domein isolasie), is dit baie moeilik om arbitrêr gesigneerde kernel pointers te vervals.
- 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 gesigneerde state, onbeskermde indirect branches) maar geen volle generiese bypass nie. bazad.github.io
- Apple se “Dark Magic” aanpassings versmal verder die eksploiteerbare oppervlaktes (domein wissel, per-sleutel enabling 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 berus dikwels op baie spesifieke gadgets of implementasie-bugs; hulle is nie generiese bypasses nie.
Dus word kernel PAC as baie robuust beskou, al is dit nie perfek nie.
- User-mode / runtime PAC bypass tegnieke
Hierdie is meer algemeen, en misbruik 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 so wyd gedeel word, is funksie pointers binne die shared cache “pre-signed” en word dan deur baie prosesse gebruik. Aanvallers mik na hierdie reeds-gesigneerde pointers as “PAC oracles”.
-
Sommige bypass tegnieke probeer om A-key gesignte pointers wat in die shared cache voorkom te onttrek of hergebruik en dit met gadgets te kombineer.
-
Die “No Clicks Required” praatjie beskryf die bou van ’n orakel oor die shared cache om relatiewe adresse af te lei en dit te kombineer met gesigneerde pointers om PAC te omseil. saelo.github.io
-
Ook is invoere van funksie pointers uit gedeelde libraries in userspace gevind dat hulle onvoldoende beskerm is deur PAC, wat ’n aanvaller toelaat om funksie pointers te kry sonder om hul handtekening te verander. (Project Zero bug entry) bugs.chromium.org
2.2 dlsym(3) / dynamic symbol resolution
-
Een bekende bypass is om
dlsym()aan te roep om ’n alreeds gesigneerde funksie pointer (gesign met A-key, diversifier zero) te kry en dit dan te gebruik. Omdatdlsym’n wettige gesigneerde pointer teruggee, omseil dit die noodsaaklikheid om PAC te vervals. -
Epsilon se blog beskryf hoe sommige bypasses dit uitbuit: aanroep
dlsym("someSym")lewer ’n gesigneerde pointer op en kan vir indirect calls gebruik word. blog.epsilon-sec.com -
Synacktiv se “iOS 18.4 — dlsym considered harmful” beskryf ’n fout: sommige simboles wat via
dlsymop iOS 18.4 opgelos is, keer pointer terug wat verkeerd gesign is (of met buggy diversifiers), wat ongewenste 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 Ander DYLD / runtime relocations
-
Die DYLD loader en dynamic relocation logika is kompleks en kaartjieer soms tydelik pages as read/write om relocations uit te voer, en skakel hulle daarna terug na read-only. Aanvallers exploitation hierdie vensters. Synacktiv se praatjie beskryf “Operation Triangulation”, ’n timing-gebaseerde PAC bypass via dynamic relocations. Synacktiv
-
DYLD pages is nou beskerm met SPRR / VM_FLAGS_TPRO (sommige beskermingsvlae vir dyld). Maar vroeër weergawes het swakker beskerming gehad. Synacktiv
-
In WebKit exploit chains is die DYLD loader dikwels ’n teiken vir PAC bypass. Die slides noem dat baie PAC bypasses die DYLD loader geteiken het (via relocation, interposer hooks). Synacktiv
2.4 NSPredicate / NSExpression / ObjC / SLOP
-
In userland exploit chains word Objective-C runtime metodes soos
NSPredicate,NSExpressionofNSInvocationgebruik om beheer-oproepe te smokkel sonder voor die hand liggende pointer vervalsing. -
Op ouer iOS (voor PAC) het ’n exploit fake NSInvocation objects gebruik om arbitrary selectors op beheerste geheue aan te roep. Met PAC is wysigings nodig. Maar die tegniek SLOP (SeLector Oriented Programming) is uitgebreide onder PAC ook. Project Zero
-
Die oorspronklike SLOP-tegniek het toegelaat om kettings van ObjC-oproepe te maak deur fake invocations; die bypass berus op die feit dat ISA of selector pointers soms nie volledig deur PAC beskerm is nie. Project Zero
-
In omgewings waar pointer authentication slegs gedeeltelik toegepas word, mag metodes / selectors / target pointers nie altyd PAC-beskerming hê 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>Voorbeeld</summary>
'n buffer overflow oorskryf 'n return-adres op die stack. Die aanvaller skryf die teiken-gadget-adres maar kan nie die korrekte PAC bereken nie. Wanneer die funksie terugkeer, faal die CPU se `AUTIA` instruksie as gevolg van die PAC-mismatch. Die ketting misluk.
Project Zero se analise van A12 (iPhone XS) het getoon hoe Apple se PAC gebruik word en metodes om PACs te vervals indien 'n aanvaller 'n memory read/write primitive het.
</details>
### 9. **Branch Target Identification (BTI)**
**Ingevoer met ARMv8.5 (later hardeware)**
BTI is 'n hardeware-eienskap wat **indirekte branch-teikens** kontroleer: wanneer `blr` of indirekte calls/jumps uitgevoer word, moet die teiken begin met 'n **BTI landing pad** (`BTI j` of `BTI c`). Spring na gadget-adresse wat nie die landing pad het nie, veroorsaak 'n uitsondering.
LLVM se implementasie noem drie variante van BTI-instruksies en hoe hulle na branch-tipes map.
| BTI Variant | Wat dit toelaat (watter branch-tipes) | Tipiese plasing / gebruiksgeval |
|-------------|----------------------------------------|-------------------------------|
| **BTI C** | Teikens van *call*-styl indirekte takke (bv. `BLR`, of `BR` wat X16/X17 gebruik) | Plaas by die ingang van funksies wat indirek aangeroep kan word |
| **BTI J** | Teikens van *jump*-styl takke (bv. `BR` wat vir tail calls gebruik word) | Geplaas aan die begin van blokke wat deur jump-tabelle of tail-calls bereikbaar is |
| **BTI JC** | Handhaaf beide C en J | Kan deur of 'n call of 'n jump getiketteer word |
- In kode saamgestel met branch target enforcement voeg compilers 'n BTI-instruksie (C, J, of JC) by elke geldige indirekte-branch teiken (funksie-beginne of blokke bereikbaar deur jumps) sodat indirekte takke net daar slaag.
- **Direkte branches / calls** (d.w.s. vaste-adres `B`, `BL`) word deur BTI **nie beperk** nie. Die aanname is dat kodebladsye vertrouelik is en 'n aanvaller dit nie kan verander nie (dus is direkte branches veilig).
- Ook, **RET / return** instruksies is oor die algemeen nie deur BTI beperk nie omdat return-adresse beskerm word via PAC of return-signing meganismes.
#### Meganisme en afdwinging
- Wanneer die CPU 'n **indirekte 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, ontstaan 'n **Branch Target Exception**.
- Die BTI-instruksie-encoding is ontwerp om opcode-ruil te maak met voorheen vir NOPs gereserveerde opcodes (in vroeëre ARM-weergawes). Dus bly BTI-geskikte binaries agtertoe-kompatibel: op hardeware sonder BTI-ondersteuning tree daardie instruksies op as NOPs.
- Die compiler-passe wat BTIs byvoeg, plaas dit slegs waar nodig: funksies wat indirek aangeroep kan word, of basic blocks wat deur jumps geteiken word.
- Sommige patches en LLVM-kode wys dat BTI nie in *alle* basic blocks ingevoeg word nie — slegs daardie wat potensiële branch-teikens is (bv. vanaf switch / jump-tabelle).
#### BTI + PAC sinergie
PAC beskerm die pointer-waarde (die bron) — dit verseker dat die ketting van indirekte oproepe / returns nie gemanipuleer is nie.
BTI verseker dat selfs 'n geldige pointer slegs na behoorlik gemerkte ingangspunte mag teiken.
Gecombineerd benodig 'n aanvaller beide 'n geldige pointer met korrekte PAC en dat die teiken 'n BTI daar het. Dit maak dit moeiliker om bruikbare gadget-kettinge te konstrueer.
#### Voorbeeld
<details>
<summary>Voorbeeld</summary>
'n exploit probeer om na 'n gadget by `0xABCDEF` te pivot wat nie met `BTI c` begin nie. Die CPU, by die uitvoering van `blr x0`, kontroleer die teiken en faal omdat die instruksie-uitlijn nie 'n geldige landing pad bevat nie. Dus raak baie gadgets onbruikbaar tensy hulle 'n BTI-voorvoegsel het.
</details>
### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Ingevoer in nuwer ARMv8-uitbreidings / iOS-ondersteuning (vir geharde kernel)**
#### PAN (Privileged Access Never)
- **PAN** is 'n funksie wat in **ARMv8.1-A** bekendgestel is wat verhoed 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 mislei of gekompromitteer word, kan dit nie willekeurig user-space pointers dereference sonder om eers PAN te *skakel uit* nie, wat die risiko's van **`ret2usr`** styl exploits of verkeerde gebruik van user-gekontroleerde buffers verminder.
- Wanneer PAN aangeskakel is (PSTATE.PAN = 1), veroorsaak enige privileged load/store instruksie wat 'n virtuele adres aanspreek wat “accessible at EL0” is 'n **permission fault**.
- Die kernel, wanneer dit regmatig user-space geheue moet toegang (bv. kopieer data na/van user buffers), moet **tydelik PAN deaktiveer** (of na “unprivileged load/store” instruksies wissel) om daardie toegang toe te laat.
- In Linux op ARM64 is PAN-ondersteuning ingestel rond 2015: kernel-patches het deteksie van die funksie bygevoeg, en `get_user` / `put_user` ens. vervang met variante wat PAN rondom user memory accesses leegmaak.
**Belangrike nuans / beperking / bug**
- Soos opgemerk deur Siguza en ander, beteken 'n spesifikasie-bug (of ambigue gedrag) in ARM se ontwerp dat **execute-only user mappings** (`--x`) moontlik **nie PAN veroorsaak nie**. Met ander woorde, as 'n user-bladsy uitvoerbaar maar sonder lees-permissie is, kan die kernel se leespoging PAN omseil omdat die argitektuur “accessible at EL0” as leesbare toestemming beskou, nie net uitvoerbare nie. Dit lei tot 'n PAN-omseiling in sekere konfigurasies.
- As gevolg hiervan, as iOS / XNU execute-only user pages toelaat (soos sommige JIT of code-cache opstellings dalk doen), mag die kernel per ongeluk van hulle lees selfs met PAN aangeskakel. Dit is 'n bekende subtiele uitbuitbare 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 uitgevoer word nie** (d.w.s. wanneer EL1 dit uitvoer).
- PXN verhoed dat die kernel (of enige privileged code) na of instruksies vanaf user-space bladsye uitvoer, selfs as beheer gediversieer word. In werklikheid stop dit kernel-vlak control-flow redireksie na user geheue.
- Gecombineer 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 attribuut-bits.
Dus, selfs as die kernel 'n gekorrupte funksie-pointer na user geheue het en daarheen probeer branch, sal die PXN-bit 'n fout veroorsaak.
#### Geheue-toestemmingsmodel & hoe PAN en PXN na bladsy-tabel-bits map
Om te verstaan hoe PAN / PXN werk, moet jy sien hoe ARM se vertaling en toestemmingsmodel werk (vereenvoudig):
- Elke bladsy of blok entry het attribuutvelde insluitend **AP[2:1]** vir toegangspermissies (lees/skryf, privileged vs unprivileged) en **UXN / PXN** bits vir execute-never beperkings.
- Wanneer PSTATE.PAN 1 is (aangeskakel), handhaaf die hardeware gewysigde semantiek: privileged toegang tot bladsye gemerk as “accessible by EL0” (d.w.s. user-accessible) word geweier (fault).
- As gevolg van die genoemde bug tel bladsye wat slegs uitvoerbaar gemerk is (geen lees-perm nie) moontlik nie as “accessible by EL0” onder sekere implementasies nie, wat PAN se blokkering kan omseil.
- Wanneer 'n bladsy se PXN-bit gestel is, is selfs indien die instruksie-fetch van 'n hoër privilege-vlak kom, uitvoering verbode.
#### Kernel gebruik van PAN / PXN in 'n geharde OS (bv. iOS / XNU)
In 'n geharde kernel-ontwerp (soos wat Apple moontlik gebruik):
- Die kernel skakel PAN standaard aan (sodat privileged code beperk word).
- In paaie wat regmatig user-buffers 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 oorheers.
- Nadat user data-toegang voltooi is, moet dit PAN weer aktiveer.
- PXN word afgedwing via page tables: user pages het PXN = 1 (sodat kernel dit nie kan uitvoer nie), kernel pages het nie PXN nie (sodat kernel-kode kan uitvoer).
- Die kernel moet verseker dat geen kodepad uitvoering na user geheuegebiede veroorsaak nie (wat PXN sou omseil) — sodat exploit-kettinge wat staatmaak op “jump into user-controlled shellcode” geblokkeer word.
As gevolg van die genoemde PAN-omseiling via execute-only bladsye, kan Apple in 'n werklike stelsel execute-only user pages deaktiveer of teenwerk, of die spesifikasie-kwetsbaarheid omseil.
#### Aanvalsvlakke, omseilings, en mitigasies
- **PAN omseiling via execute-only bladsye**: soos bespreek, laat die spesifikasie 'n gaping toe: user pages met execute-only (geen lees-perm) mag nie as “accessible at EL0” beskou word nie onder sekere implementasies, so PAN sal kernel-lesse na sulke bladsye nie blokkeer nie. Dit gee die aanvaller 'n ongewone pad om data via “execute-only” afdelings te voer.
- **Temporele-venster-uitbuiting**: as die kernel PAN vir 'n venster langer as nodig deaktiveer, kan 'n race of kwaadwillige pad daardie venster uitbuit om bedoelde user memory toegang te kry.
- **Vergeet om weer te aktiveer**: as kodepade misluk om PAN weer aan te sit, kan opvolgende kernel-bewerkings per ongeluk user-geheue benader.
- **Verkeerde konfigurasie van PXN**: as page tables nie PXN op user pages stel nie of user code pages verkeerdelik map, kan die kernel mislei word om user-space kode uit te voer.
- **Spekulatie / side-channels**: analoog aan spekulatiewe omseilings, kan daar mikroargitektoniese nieuwighede wees wat transiente skending van PAN / PXN-toetse veroorsaak (alhoewel sulke aanvalle sterk CPU-ontwerp-afhanklik is).
- **Komplekse interaksies**: In meer gevorderde funksies (bv. JIT, gedeelde geheue, just-in-time kode-streke), benodig die kernel fynbeheer om sekere geheue-toegange of uitvoering in user-gemapte gebiede toe te laat; om dit veilig te ontwerp onder PAN/PXN- beperkings is nie eenvoudig nie.
#### Voorbeeld
<details>
<summary>Kodevoorbeeld</summary>
Hier is illustratiewe pseudo-assembly-volgordes wat wys hoe PAN aangeskakel/deaktiveer word rondom user memory toegang, en hoe 'n fout kan voorkom.
</details>
// 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 PXN **nie** op daardie gebruikersbladsy gestel het nie, sou die tak dalk slaag — wat onveilig sou wees.
As die kernel vergeet om PAN weer aan te skakel ná toegang tot gebruikersgeheue, skep dit 'n venster waar verdere kernel-logika per ongeluk willekeurige gebruikersgeheue kan lees/skryf.
As die gebruiker-aanwyser na 'n execute-only page wys (gebruikersbladsy met slegs execute-permissie, geen lees/skryf), kan onder die PAN spec bug `ldr W2, [X1]` **nie** fault nie selfs met PAN aangeskakel, wat afhangend van implementasie 'n bypass exploit moontlik maak.
</details>
<details>
<summary>Voorbeeld</summary>
'n kernel-kwetsbaarheid probeer 'n gebruiker-voorsiene funksie-aanwyser neem en dit in kernel-konteks aanroep (bv. `call user_buffer`). Onder PAN/PXN is daardie operasie verbode of veroorsaak dit 'n fout.
</details>
---
### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Ingevoer in ARMv8.5 / nuwer (of opsionele uitbreiding)**
TBI beteken die top byte (mees-belangrike byte) van 'n 64-bit pointer word deur adresvertaling geïgnoreer. Dit laat OS of hardware toe om **tag bits** in die pointer se top byte in te sluit sonder om die werklike adres te verander.
- TBI staan vir **Top Byte Ignore** (soms genoem *Address Tagging*). Dit is 'n hardware-funksie (beskikbaar in baie ARMv8+ implementasies) wat **die top 8 bits** (bits 63:56) van 'n 64-bit pointer ignoreer wanneer dit **adresvertaling / load/store / instruction fetch** uitvoer.
- In werklikheid behandel die CPU 'n pointer `0xTTxxxx_xxxx_xxxx` (waar `TT` = top byte) as `0x00xxxx_xxxx_xxxx` vir doeleindes van adresvertaling, deur die top byte te mask. Die top byte kan deur sagteware gebruik word om **metadata / tag bits** te stoor.
- Dit gee sagteware “gratis” in-band ruimte om 'n byte tag in elke pointer te plaas sonder om te verander watter geheue-lokasie dit aanspreek.
- Die argitektuur verseker dat loads, stores en instruction fetch die pointer met die top byte gemaak (d.w.s. tag verwyder) hanteer voordat die werklike geheue-toegang plaasvind.
Dus skei TBI die **logiese pointer** (pointer + tag) van die **fisiese adres** wat vir geheue-operasies gebruik word.
#### Waarom TBI: Gebruiksgvalle en motivering
- **Pointer tagging / metadata**: Jy kan ekstra metadata (bv. objektipe, weergawe, grense, integriteitstags) in daardie top byte stoor. Wanneer jy later die pointer gebruik, word die tag op hardwarevlak geïgnoreer, sodat jy dit nie handmatig hoef te verwyder vir die geheue-toegang nie.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI is die basishardware-meganisme waarop MTE bou. In ARMv8.5 gebruik die **Memory Tagging Extension** die bits 59:56 van die pointer as 'n **logiese tag** en vergelyk dit met 'n **allocation tag** wat in geheue gestoor is.
- **Verbeterde sekuriteit & integriteit**: Deur TBI te kombineer met pointer authentication (PAC) of runtime- kontroles, kan jy nie net die pointerwaarde nie, maar ook die tag afdwing. 'n Aanvaller wat 'n pointer oor-skryf sonder die korrekte tag sal 'n onversoenbare tag veroorsaak.
- **Kompatibiliteit**: Omdat TBI opsioneel is en tag bits deur hardware geïgnoreer word, funksioneer bestaande ongetagde kode normaal. Die tag bits word effektief “moet-nie-sorg” bits vir ouer kode.
#### Voorbeeld
<details>
<summary>Voorbeeld</summary>
'n funksie-aanwyser het 'n tag in sy top byte gehad (bv. `0xAA`). 'n Exploit oor-skryf die laer bits van die pointer maar vergeet die tag, so wanneer die kernel verifieer of sanitiseer, faal die pointer of word dit verwerp.
</details>
---
### 12. **Page Protection Layer (PPL)**
**Ingevoer in moderne iOS / nuwe hardware (iOS ~17 / Apple silicon / hoë-end modelle)** (sommige berigte wys PPL in macOS / Apple silicon, maar Apple bring analoog-beskerming na iOS)
- PPL is ontwerp as 'n **intra-kernel beskermingsgrens**: selfs as die kernel (EL1) gekompromitteer is en lees/skryf-vaardighede het, **moet dit nie vrylik sekere sensitiewe bladsye kan wysig nie** (veral page tables, code-signing metadata, kernel code pages, entitlements, trust caches, ens.).
- Dit skep effektief 'n **“kernel binne die kernel”** — 'n kleiner vertroude komponent (PPL) met **verhoogde bevoegdhede** wat alleen beskermde bladsye kan wysig. Ander kernel-kode moet in PPL-roetines aanroep om veranderinge te maak.
- Dit verminder die aanval-oppervlak vir kernel-exploits: selfs met volledige arbitraire R/W/execute in kernel-modus, moet exploit-kode ook op een of ander manier die PPL-domein binnedring (of PPL omseil) om kritieke strukture te wysig.
- Op nuwer Apple silicon (A15+ / M2+), skuif Apple na **SPTM (Secure Page Table Monitor)**, wat in baie gevalle PPL vir page-table beskerming op daardie platform vervang.
Hier is hoe PPL vermoedelik werk, gebaseer op publieke ontleding:
#### Gebruik van APRR / permission routing (APRR = Access Permission ReRouting)
- Apple-hardware gebruik 'n meganisme genaamd **APRR (Access Permission ReRouting)**, wat page table entries (PTEs) toelaat om klein indeksies te bevat, eerder as volle permissiebits. Daardie indeksies word via APRR-registers na effektiewe permissies gemap. Dit laat dinamiese remapping van permissies per domein toe.
- PPL benut APRR om bevoegdheid binne kernel-konteks te segregeer: slegs die PPL-domein mag die mapping tussen indeksies en effektiewe permissies opdateer. Dit beteken dat wanneer nie-PPL kernel-kode 'n PTE skryf of probeer permissiebits omskakel, die APRR-logika dit ontwrig of lees-alleen mapping afdwing.
- PPL-kode self draai in 'n beperkte streek (bv. `__PPLTEXT`) wat normaalweg nie-uitvoerbaar of nie-skryfbaar is tot toetspoorte tydelik toegang verleen. Die kernel roep PPL entry points (“PPL routines”) aan om sensitiewe operasies uit te voer.
#### Poort / In- en Uitgang
- Wanneer die kernel 'n beskermde bladsy moet wysig (bv. om permissies van 'n kernel-codebladsy te verander, of page tables te wysig), roep dit 'n **PPL wrapper**-routine aan, wat validasie doen en dan in die PPL-domein oor- skakel. Buite daardie domein is die beskermde bladsye effektief lees-alleen of nie-wysigbaar deur die hoofkernel.
- Tydens PPL-entry word die APRR-mappings aangepas sodat geheue-bladsye in die PPL-streek binne PPL as **uitvoerbaar & skryfbaar** gestel word. By uitgang word hulle teruggestel na lees-alleen / nie-skryfbaar. Dit verseker dat slegs goed-oorsigte PPL-roetines beskermde bladsye kan skryf.
- Buite PPL sal pogings deur kernel-kode om daardie beskermde bladsye te skryf 'n fault veroorsaak (permission denied) omdat die APRR-mapping vir daardie kode-domein nie skryf toelaat nie.
#### Kategorieë beskermde bladsye
Bladsye wat PPL tipies beskerm sluit in:
- Page table strukture (translation table entries, mapping metadata)
- Kernel code pages, veral dié wat kritieke logika bevat
- Code-sign metadata (trust caches, signature blobs)
- Entitlement-tabelle, signature enforcement-tabelle
- Ander hoë-waarde kernel-strukture waar 'n patch sou toelaat om signature checks of credential-manipulasie te omseil
Die idee is dat selfs as kernel-geheue lankal beheer word, kan die aanvaller nie sommer hierdie bladsye patch of herskryf nie, tensy hulle ook PPL-roetines kompromitteer of PPL omseil.
#### Bekende Omseilings & Kwetsbaarhede
1. **Project Zero’s PPL bypass (stale TLB trick)**
- 'n Publieke uiteensetting deur Project Zero beskryf 'n omseiling wat stale TLB entries betrek.
- Die idee:
1. Allokeer twee fisiese bladsye A en B, merk hulle as PPL-bladsye (sodat hulle beskerm is).
2. Map twee virtuele adresse P en Q waarvan die L3 translation table bladsye uit A en B kom.
3. Spin 'n thread wat voortdurend Q benader, en hou sodoende sy TLB-entry lewendig.
4. Roep `pmap_remove_options()` aan om mappings wat by P begin te verwyder; as gevolg van 'n fout 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 (bladsy van Q se tabel) om arbitraire geheue te map (bv. PPL-beskermde bladsye). Omdat die stale TLB-entry steeds Q se ou mapping kaart, bly daardie mapping geldig vir daardie konteks.
6. Deur dit kan die aanvaller 'n skryfbare mapping van PPL-beskermde bladsye in plek sit sonder om deur die PPL-koppelvlak te gaan.
- Hierdie exploit vereis fyn beheer oor fisiese mapping en TLB-gedrag. Dit demonstreer dat 'n sekuriteitsgrens wat op TLB / mapping-korrektheid staatmaak uiters sorgvuldig moet wees oor TLB-invaliderings en mapping-konsistentheid.
- Project Zero se kommentaar is dat omseilings soos hierdie subtiel en skaars is, maar moontlik in komplekse stelsels. Hulle beskou steeds PPL as 'n stewige mitigasie.
2. **Ander potensiële gevare & beperkings**
- As 'n kernel-exploit direk PPL-roetines kan binnentree (deur die PPL-wrappers aan te roep), kan dit beperkings omseil. Daarom is argumentvalidasie kritiek.
- Foute in die PPL-kode self (bv. aritmetiese overflow, grenskontroles) kan uit-tot-grens wysigings binne PPL toelaat. Project Zero het opgemerk dat so 'n fout in `pmap_remove_options_internal()` in hul omseiling benut is.
- Die PPL-grens is onherroeplik verbonde aan hardware-dwinging (APRR, memory controller), so dit is slegs so sterk soos die hardware-implementasie.
#### Voorbeeld
<details>
<summary>Kodevoorbeeld</summary>
Hier is 'n vereenvoudigde pseudokode / logika wat wys hoe 'n kernel na PPL kan aanroep om beskermde bladsye 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
Die kernel kan baie normale operasies uitvoer, maar slegs deur ppl_call_*-roetines kan dit beskermde mappings verander of kode patch.
Example
'n kernel exploit probeer die entitlement table oorskryf, of code-sign enforcement deaktiveer deur 'n kernel signature blob te wysig. Omdat daardie page PPL-protected is, word die skryf geblokkeer tensy dit deur die PPL interface gaan. Dus selfs met kernel code execution kan jy nie code-sign constraints omseil of credential data arbitrarily wysig nie. Op iOS 17+ gebruik sekere devices SPTM om PPL-managed pages verder te isoleer.PPL → SPTM / Replacements / Future
- Op Apple’s moderne SoCs (A15 of later, M2 of later), ondersteun Apple SPTM (Secure Page Table Monitor), wat PPL vervang vir page table protections.
- Apple vermeld in dokumentasie: “Page Protection Layer (PPL) and Secure Page Table Monitor (SPTM) enforce execution of signed and trusted code … PPL manages the page table permission overrides … Secure Page Table Monitor replaces PPL on supported platforms.”
- Die SPTM-argitektuur skuif waarskynlik meer beleidsafdwinging na ’n hoër-geprivilegieerde monitor buite kernel-kontrole, wat verder die vertrouensgrens verminder.
MTE | EMTE | MIE
Hier is ’n hoërvlak-beskrywing van hoe EMTE onder Apple se MIE-opstelling werk:
- Tag assignment
- Wanneer memory toegeken word (bv. in 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 (gebruik TBI / top byte ignore mechanisms).
- 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). Indien dit nie ooreenstem nie, faal dit onmiddellik (aangesien dit synchronous).
- Omdat dit synchronous is, bestaan daar geen “delayed detection” venster nie.
- Retagging on free / reuse
- Wanneer memory vrygestel 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 ’n mismatch hê wanneer dit aangespreek word.
- Neighbor-tag differentiation to catch overflows
- Aanliggende allocations kry onderskeibare tags. As ’n buffer overflow in ’n buurman se memory spuit, veroorsaak tag mismatch ’n fout.
- Dit is veral kragtig om klein overflows wat ’n grens oorsteek op te vang.
- Tag confidentiality enforcement
- Apple moet verhoed dat tag values being leaked (want as ’n attacker die tag leer, kan hulle pointers met korrekte tags maak).
- Hulle sluit beskerming in (microarchitectural / speculative controls) om side-channel leakage van tag bits te vermy.
- 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 memory corruption te beskerm).
- Die hardware/OS verseker dat tag-reëls geld selfs wanneer die kernel uitvoer namens 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 & uitdagings
- **Intrablock overflows**: As an overflow binne dieselfde toekenning bly (dit steek nie oor die grens nie) en die tag dieselfde bly, sal tag mismatch dit nie opspoor nie.
- **Tag width limitation**: Net 'n paar bits (bv. 4 bits, of 'n klein domein) is beskikbaar vir die tag — beperkte namespace.
- **Side-channel leaks**: As tag bits kan be leaked (via cache / speculative execution), kan 'n aanvaller geldige tags leer en omseil. Apple’s tag confidentiality enforcement is bedoel om dit te mitigate.
- **Performance overhead**: Tag checks by elke load/store voeg koste by; Apple moet die hardware optimaliseer om die overhead laag te hou.
- **Compatibility & fallback**: Op ouer hardware of dele wat EMTE nie ondersteun nie, moet daar '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 die allocator-logika kan kwesbaarhede inbring.
- **Mixed memory / hybrid areas**: Sommige geheue mag ongetagd bly (legacy), wat interoperabiliteit moeiliker maak.
- **Speculative / transient attacks**: Soos met baie microarchitectural 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-areas (kernel, security-critical subsystems), nie universeel nie.
---
## Key enhancements / differences compared to standard MTE
Hier is die verbeterings en veranderinge waarop Apple klem lê:
| 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 leak 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.|
Omdat Apple beide die hardware en sagteware-stapel beheer, kan dit EMTE streng afdwing, prestasie-valstrikke vermy, en side-channel gapings toemaak.
---
## How EMTE works in practice (Apple / MIE)
Hier is 'n hoërvlak beskrywing van hoe EMTE onder Apple se MIE-opstelling werk:
1. **Tag assignment**
- Wanneer geheue gealloceer word (bv. in 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 mechanisms te gebruik).
2. **Tag checking on access**
- Wanneer 'n load of store uitgevoer word met 'n pointer, kontroleer die hardware of die pointer se tag ooreenstem met die geheueblok se tag (allocation tag). As dit nie ooreenstem nie, gooi dit onmiddellik 'n fout (aangesien dit synchronous is).
- Omdat dit synchronous is, is daar geen "delayed detection" venster nie.
3. **Retagging on free / reuse**
- Wanneer geheue vrygestel 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 stale tag hê en 'n mismatch veroorsaak wanneer dit aangespreek word.
4. **Neighbor-tag differentiation to catch overflows**
- Aanliggende allocations kry verskillende tags. As 'n buffer overflow in 'n buur se geheue beland, veroorsaak tag mismatch 'n fout.
- Dit is veral kragtig om klein overflows wat grense oorsteek te vang.
5. **Tag confidentiality enforcement**
- Apple moet voorkom dat tag-waardes geleak word (want as 'n aanvaller die tag leer, kan hulle pointers met korrekte tags saamstel).
- Hulle sluit beskermings in (microarchitectural / speculative controls) 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 memory corruption te beskerm).
- Die hardware/OS verseker dat tag-reëls toegepas word 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 'n opt-in of debugging mode nie.
---
## Exception handling in XNU
Wanneer 'n **exception** voorkom (bv. `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, ens.), is die **Mach layer** van die XNU kernel verantwoordelik om dit te onderskep voordat dit 'n UNIX-styl **signal** word (soos `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).
Hierdie proses behels verskeie lae van exception-propagasie en hantering voordat dit by user space kom of na 'n BSD signal omgeskakel word.
### Exception Flow (High-Level)
1. **CPU triggers a synchronous exception** (bv. ongeldig pointer dereference, PAC failure, illegal instruction, ens.).
2. **Low-level 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 roeteer:
- 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 kernel:
- **Dit omskakel na 'n BSD signal** (vir user-space prosesse).
- **Paniekeer** (vir kernel-space exceptions).
### Kernfunksie: `exception_triage()`
Die funksie `exception_triage()` roeteer Mach exceptions op 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, waar exception messages heen gestuur word.
Hulle word deur die API gedefinieer:
task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()
Elke exception-poort het:
- ’n mask (watter exceptions dit wil ontvang)
- ’n port name (Mach port om boodskappe te ontvang)
- ’n behavior (hoe die kernel die boodskap stuur)
- ’n flavor (watter thread state ingesluit word)
Debuggers and Exception Handling
’n debugger (bv. LLDB) stel ’n exception port op die teiken task of thread, gewoonlik deur task_set_exception_ports() te gebruik.
Wanneer ’n exception plaasvind:
- Die Mach boodskap word na die debugger-proses gestuur.
- Die debugger kan besluit om die exception te handhaaf (resume, registers wysig, instruksie oorskryf) of nie te handhaaf nie.
- As die debugger dit nie hanteer nie, propageteer die exception na die volgende vlak (task → host).
Flow of EXC_BAD_ACCESS
-
Thread dereference ’n ongeldige pointer → CPU gooi Data Abort.
-
Kernel trap handler roep
exception_triage(EXC_BAD_ACCESS, ...)aan. -
Boodskap gestuur na:
-
Thread port → (debugger kan breakpoint onderskep).
-
As debugger ignoreer → Task port → (process-level handler).
-
As dit geignoreer word → Host port (gewoonlik ReportCrash).
- As niemand dit hanteer nie →
bsd_exception()vertaal dit naSIGSEGV.
PAC Exceptions
Wanneer Pointer Authentication (PAC) faal (signature mismatch), word ’n spesiale Mach exception opgeworpen:
EXC_ARM_PAC(tipe)- Codes mag besonderhede insluit (bv. sleuteltipe, pointertipe).
As die binêre die vlag TFRO_PAC_EXC_FATAL het, behandel die kernel PAC-foute as fatal, en omseil dit debugger-onderskep. Dit is om te verhoed dat aanvalleerders debuggers gebruik om PAC-checks te omseil en dit is geaktiveer vir platform binaries.
Software Breakpoints
’n software breakpoint (int3 op x86, brk op ARM64) word geïmplementeer deur ’n opsetlike fout te veroorsaak.
Die debugger vang dit op via die exception port:
- Wysig die instruction pointer of geheue.
- Herstel die oorspronklike instruksie.
- Hervat uitvoering.
Dieselfde meganisme laat jou toe om ’n PAC exception te “vang” — tensy TFRO_PAC_EXC_FATAL gestel is, in welke 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()aan. -
Dit karteer Mach exceptions na seine:
| 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 seine-lewering. -
osfmk/arm64/trap.c→ Laevlak trap handlers. -
osfmk/mach/exc.h→ Exception codes 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 sone stoor slegs toewysings van ’n enkele size class.
Van die skermkiekie:
| 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. |
Hoe dit gewerk het:
- Elke toewysingsversoek word na bo afgerond na die naaste sone-grootte.
(Bv. ’n 50-byte versoek beland in die
kalloc.64sone). - Geheue in elke sone is bewaar in ’n freelist — stukkies wat deur die kernel vrygestel is het teruggegaan na daardie sone.
- As jy ’n 64-byte buffer oorloop, sou jy die volgende objek in daardie sone oorskryf.
Dit is waarom heap spraying / feng shui so effektief was: jy kon objek-naaste bure voorspel deur toewysings van dieselfde size class te spray.
The freelist
Binne elke kalloc sone is vrygestelde objekke nie direk na die stelsel teruggegee nie — hulle het in ’n freelist gegaan, ’n gelinkte lys van beskikbare stukke.
-
Wanneer ’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 sone.
-
Die sone het ’n HEAD pointer na die eerste vrye stuk gehou.
-
Toewysing het altyd die huidige HEAD gebruik:
-
Pop HEAD (gee daardie geheue terug aan die roeper).
-
Werk HEAD by = HEAD->next (gestoor in die vrygestelde stukkie se header).
-
Vrygee het stukkies teruggedruk:
-
freed_chunk->next = HEAD -
HEAD = freed_chunk
So was die freelist net ’n gelinkte 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)
Uitbuiting van die 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 → oorskryf sy “next” pointer.
-
Use-after-free skryf in ’n freed object → oorskryf 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 vorm die 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
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.
HackTricks

