iOS Exploiting
Tip
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
iOS Exploit Mitigations
1. Code Signing / Runtime Signature Verification
Introduced early (iPhone OS → iOS) Αυτή είναι μία από τις θεμελιώδεις προστασίες: όλος ο εκτελέσιμος κώδικας (apps, dynamic libraries, JIT-ed code, extensions, frameworks, caches) πρέπει να είναι κρυπτογραφικά υπογεγραμμένος από μια certificate chain που έχει ρίζα την εμπιστοσύνη της Apple. Σε runtime, πριν φορτωθεί ένα binary στη μνήμη (ή πριν πραγματοποιηθούν jumps πέρα από ορισμένα όρια), το σύστημα ελέγχει την υπογραφή του. Εάν ο κώδικας τροποποιηθεί (bit-flipped, patched) ή είναι unsigned, το load αποτυγχάνει.
- Thwarts: το “classic payload drop + execute” στάδιο σε exploit chains; arbitrary code injection; τροποποίηση ενός υπάρχοντος binary για εισαγωγή κακόβουλης λογικής.
- Mechanism detail:
- Ο Mach-O loader (και dynamic linker) ελέγχει code pages, segments, entitlements, team IDs, και ότι η signature καλύπτει το περιεχόμενο του αρχείου.
- Για περιοχές μνήμης όπως JIT caches ή dynamically generated code, η Apple επιβάλλει ότι οι σελίδες πρέπει να είναι signed ή να επικυρώνονται μέσω ειδικών APIs (π.χ.
mprotectμε code-sign checks). - Η signature περιλαμβάνει entitlements και identifiers· το OS επιβάλλει ότι ορισμένα APIs ή privileged capabilities απαιτούν συγκεκριμένα entitlements που δεν μπορούν να πλαστογραφηθούν.
Example
Αν ένα exploit αποκτήσει code execution σε μια διεργασία και προσπαθήσει να γράψει shellcode στο heap και να κάνει jump σε αυτό, σε iOS η σελίδα αυτή θα πρέπει να είναι flagged executable **και** να ικανοποιεί τα code-signature constraints. Δεδομένου ότι το shellcode δεν είναι signed με το πιστοποιητικό της Apple, το jump αποτυγχάνει ή το σύστημα απορρίπτει το να γίνει αυτή η περιοχή μνήμης executable.2. CoreTrust
Introduced around iOS 14+ era (or gradually in newer devices / later iOS) Το CoreTrust είναι το subsystem που εκτελεί runtime signature validation των binaries (συμπεριλαμβανομένων system και user binaries) έναντι της Apple’s root certificate αντί να βασίζεται σε cached userland trust stores.
- Thwarts: post-install tampering των binaries, jailbreaking τεχνικές που προσπαθούν να αντικαταστήσουν ή να patch-άρουν system libraries ή user apps; εξαπάτηση του συστήματος με αντικατάσταση trusted binaries με κακόβουλα αντίγραφα.
- Mechanism detail:
- Αντί να εμπιστεύεται μια τοπική trust database ή certificate cache, το CoreTrust αναφέρεται απευθείας στην Apple’s root ή επαληθεύει intermediate certificates σε μια ασφαλή αλυσίδα.
- Εξασφαλίζει ότι τροποποιήσεις (π.χ. στο filesystem) σε υπάρχοντα binaries εντοπίζονται και απορρίπτονται.
- Δένει entitlements, team IDs, code signing flags και άλλα metadata στο binary κατά το load time.
Example
Ένα jailbreak μπορεί να προσπαθήσει να αντικαταστήσει το `SpringBoard` ή `libsystem` με μια patched έκδοση για να αποκτήσει persistence. Όμως όταν ο loader του OS ή το CoreTrust ελέγξει, θα παρατηρήσει mismatch στην υπογραφή (ή τροποποιημένα entitlements) και θα αρνηθεί να εκτελέσει.3. Data Execution Prevention (DEP / NX / W^X)
Introduced in many OSes earlier; iOS had NX-bit / w^x for a long time Το DEP επιβάλλει ότι σελίδες που είναι marked writable (για δεδομένα) είναι non-executable, και σελίδες που είναι marked executable είναι non-writable. Δεν μπορείτε απλά να γράψετε shellcode στο heap ή στο stack και να το εκτελέσετε.
- Thwarts: άμεση εκτέλεση shellcode; κλασικό buffer-overflow → jump σε injected shellcode.
- Mechanism detail:
- Οι MMU / memory protection flags (μέσω page tables) επιβάλλουν τον διαχωρισμό.
- Οποιαδήποτε προσπάθεια να σημειωθεί μια writable σελίδα ως executable ενεργοποιεί έναν συστημικό έλεγχο (και είτε απαγορεύεται είτε απαιτεί code-sign approval).
- Σε πολλές περιπτώσεις, το να γίνουν σελίδες executable απαιτεί χρήση OS APIs που επιβάλλουν επιπλέον constraints ή checks.
Example
Μια overflow εγγράφει shellcode στο heap. Ο επιτιθέμενος επιχειρεί `mprotect(heap_addr, size, PROT_EXEC)` για να το κάνει executable. Αλλά το σύστημα αρνείται ή επικυρώνει ότι η νέα σελίδα πρέπει να περάσει code-sign constraints (τα οποία το shellcode δεν μπορεί).4. Address Space Layout Randomization (ASLR)
Introduced in iOS ~4–5 era (roughly iOS 4–5 timeframe) Το ASLR τυχαίος-ποιεί τις βάσεις διευθύνσεων κρίσιμων περιοχών μνήμης: libraries, heap, stack κ.λπ., σε κάθε εκκίνηση διαδικασίας. Τα gadget addresses μετακινούνται μεταξύ runs.
- Thwarts: hardcoding gadget addresses για ROP/JOP; στατικές exploit chains; blind jumping σε γνωστά offsets.
- Mechanism detail:
- Κάθε loaded library / dynamic module rebased σε ένα randomized offset.
- Οι βάσεις του stack και του heap είναι randomized (εντός ορισμένων ορίων entropy).
- Μερικές φορές και άλλες περιοχές (π.χ. mmap allocations) επίσης randomized.
- Σε συνδυασμό με information-leak mitigations, αναγκάζει τον επιτιθέμενο να κάνει πρώτα leak μιας διεύθυνσης ή pointer για να ανακαλύψει τις βάσεις σε runtime.
Example
Μια ROP chain περιμένει gadget στο `0x….lib + offset`. Αλλά επειδή το `lib` rebased διαφορετικά κάθε run, η hardcoded chain αποτυγχάνει. Ένα exploit πρέπει πρώτα να leak-άρει τη base address του module πριν υπολογίσει gadget addresses.5. Kernel Address Space Layout Randomization (KASLR)
Introduced in iOS ~ (iOS 5 / iOS 6 timeframe) Αναλογικά με το user ASLR, το KASLR τυχαίος-ποιεί τη βάση του kernel text και άλλων kernel structures κατά την εκκίνηση.
- Thwarts: kernel-level exploits που στηρίζονται σε σταθερές τοποθεσίες kernel code ή data; στατικά kernel exploits.
- Mechanism detail:
- Σε κάθε boot, η base address του kernel randomized (εντός ενός εύρους).
- Kernel data structures (όπως
task_structs,vm_map, κ.λπ.) μπορεί επίσης να μετατοπίζονται ή να έχουν offsets. - Οι επιτιθέμενοι πρέπει πρώτα να leak-άρουν kernel pointers ή να χρησιμοποιήσουν information disclosure vulnerabilities για να υπολογίσουν offsets πριν hijackάρουν kernel structures ή κώδικα.
Example
Ένα local vulnerability στοχεύει να καταστρέψει ένα kernel function pointer (π.χ. σε `vtable`) στο `KERN_BASE + offset`. Αλλά επειδή το `KERN_BASE` είναι άγνωστο, ο επιτιθέμενος πρέπει πρώτα να το leak-άρει (π.χ. μέσω ενός read primitive) πριν υπολογίσει τη σωστή διεύθυνση για corruption.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) παρακολουθεί διαρκώς την ακεραιότητα των kernel text pages (μέσω hash ή checksum). Αν ανιχνεύσει tampering (patches, inline hooks, code modifications) εκτός των επιτρεπτών παραθύρων, προκαλεί kernel panic ή reboot.
- Thwarts: persistent kernel patching (τροποποίηση kernel instructions), inline hooks, static function overwrites.
- Mechanism detail:
- Ένα hardware ή firmware module παρακολουθεί την περιοχή kernel text.
- Περίοδο-κατά-περίοδο ή κατ’ απαίτηση επανα-υπολογίζει hashes των pages και συγκρίνει με τις αναμενόμενες τιμές.
- Αν υπάρξουν mismatches εκτός benign update windows, προκαλεί panic συσκευής (για να αποφευχθεί persistent malicious patch).
- Οι επιτιθέμενοι πρέπει είτε να αποφύγουν τα detection windows είτε να χρησιμοποιήσουν legitimate patch paths.
Example
Ένα exploit προσπαθεί να patch-άρει το prologue μιας kernel function (π.χ. `memcmp`) για να παρεμβαίνει στις κλήσεις. Αλλά το KPP παρατηρεί ότι το code page’s hash δεν ταιριάζει με την αναμενόμενη τιμή και προκαλεί kernel panic, κρασάροντας τη συσκευή πριν το patch σταθεροποιηθεί.7. Kernel Text Read‐Only Region (KTRR)
Introduced in modern SoCs (post ~A12 / newer hardware) Το KTRR είναι ένας hardware-enforced μηχανισμός: μόλις ο kernel text κλειδωθεί νωρίς κατά το boot, γίνεται read-only από EL1 (τον kernel), αποτρέποντας περαιτέρω εγγραφές σε code pages.
- Thwarts: οποιεσδήποτε τροποποιήσεις στον kernel code μετά το boot (π.χ. patching, in-place code injection) σε επίπεδο προνομίων EL1.
- Mechanism detail:
- Κατά το boot (σε secure/bootloader στάδιο), ο memory controller (ή ένα secure hardware unit) σημειώνει τις physical pages που περιέχουν kernel text ως read-only.
- Ακόμα και αν ένα exploit αποκτήσει πλήρη kernel privileges, δεν μπορεί να γράψει σε αυτές τις σελίδες για να αλλάξει instructions.
- Για να τις τροποποιήσει, ο επιτιθέμενος πρέπει πρώτα να kompromatάρει την boot chain, ή να υπονομεύσει το ίδιο το KTRR.
Example
Ένα privilege-escalation exploit πηδάει σε EL1 και γράφει ένα trampoline σε μια kernel function (π.χ. στο `syscall` handler). Αλλά επειδή οι σελίδες έχουν κλειδωθεί ως read-only από το KTRR, η εγγραφή αποτυγχάνει (ή προκαλεί fault), οπότε τα patches δεν εφαρμόζονται.8. Pointer Authentication Codes (PAC)
Introduced with ARMv8.3 (hardware), Apple beginning with A12 / iOS ~12+
- Το PAC είναι μια hardware δυνατότητα εισαγόμενη στο ARMv8.3-A για να ανιχνεύει την τροποποίηση pointer values (return addresses, function pointers, ορισμένα data pointers) ενσωματώνοντας μια μικρή κρυπτογραφική υπογραφή (ένα “MAC”) στα αχρησιμοποίητα high bits του pointer.
- Η υπογραφή (“PAC”) υπολογίζεται πάνω στο pointer value συν έναν modifier (μια context τιμή, π.χ. stack pointer ή κάποιον διακριτικό αριθμό). Έτσι το ίδιο pointer value σε διαφορετικά contexts έχει διαφορετικό PAC.
- Κατά τη χρήση, πριν γίνει dereference ή branch μέσω αυτού του pointer, μια authenticate εντολή ελέγχει το PAC. Αν είναι έγκυρο, το PAC αφαιρείται και λαμβάνεται το καθαρό pointer· αν είναι άκυρο, το pointer γίνεται “poisoned” (ή προκαλείται fault).
- Τα keys που χρησιμοποιούνται για παραγωγή/επικύρωση PAC ζουν σε privileged registers (EL1, kernel) και δεν είναι απευθείας αναγνώσιμα από user mode.
- Επειδή δεν χρησιμοποιούνται όλα τα 64 bits ενός pointer σε πολλά συστήματα (π.χ. 48-bit address space), τα upper bits είναι “ελεύθερα” και μπορούν να φιλοξενήσουν το PAC χωρίς να αλλάξουν την effective address.
Architectural Basis & Key Types
-
Το ARMv8.3 εισάγει πέντε 128-bit keys (το καθένα υλοποιημένο μέσω δύο 64-bit system registers) για pointer authentication.
-
APIAKey — για instruction pointers (domain “I”, key A)
-
APIBKey — δεύτερο key για instruction pointers (domain “I”, key B)
-
APDAKey — για data pointers (domain “D”, key A)
-
APDBKey — για data pointers (domain “D”, key B)
-
APGAKey — “generic” key, για signing μη-pointer δεδομένων ή άλλων γενικών χρήσεων
-
Αυτά τα keys αποθηκεύονται σε privileged system registers (προσβάσιμα μόνο σε EL1/EL2 κ.λπ.), όχι σε user mode.
-
Το PAC υπολογίζεται μέσω μιας κρυπτογραφικής function (το ARM προτείνει QARMA ως αλγόριθμο) χρησιμοποιώντας:
- Το pointer value (canonical portion)
- Έναν modifier (μια τιμή context, όπως ένα salt)
- Το secret key
- Κάποια εσωτερική tweak λογική Εάν το προκύπτον PAC ταιριάζει με αυτό που είναι αποθηκευμένο στα upper bits του pointer, η authentication πετυχαίνει.
Instruction Families
Η συμβατική ονομασία είναι: PAC / AUT / XPAC, έπειτα domain letters.
PACxxinstructions sign ένα pointer και εισάγουν ένα PACAUTxxinstructions authenticate + strip (επικυρώνουν και αφαιρούν το PAC)XPACxxinstructions strip χωρίς validation
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 |
There are specialized / alias forms:
PACIASPis shorthand forPACIA X30, SP(sign the link register using SP as modifier)AUTIASPisAUTIA X30, SP(authenticate link register with SP)- Combined forms like
RETAA,RETAB(authenticate-and-return) orBLRAA(authenticate & branch) exist in ARM extensions / compiler support. - Also zero-modifier variants:
PACIZA/PACIZBwhere the modifier is implicitly zero, etc.
Modifiers
Ο κύριος σκοπός του modifier είναι να δέσει το PAC σε ένα συγκεκριμένο context έτσι ώστε η ίδια διεύθυνση που υπογράφεται σε διαφορετικά contexts να δίνει διαφορετικά PACs. Είναι σαν να προσθέτεις ένα salt σε ένα hash.
Επομένως:
- Ο modifier είναι μια τιμή context (ένας άλλος register) που εμπλέκεται στον υπολογισμό του PAC. Τυπικές επιλογές: το stack pointer (
SP), ένας frame pointer, ή κάποιο object ID. - Η χρήση του SP ως modifier είναι κοινή για signing του return address: το PAC δένεται στο συγκεκριμένο stack frame. Αν επιχειρήσετε να επαναχρησιμοποιήσετε το LR σε διαφορετικό frame, ο modifier αλλάζει και το PAC validation αποτυγχάνει.
- Το ίδιο pointer value υπογεγραμμένο υπό διαφορετικούς modifiers παράγει διαφορετικά PACs.
- Ο modifier δεν χρειάζεται να είναι μυστικός, αλλά ιδανικά να μην ελέγχεται από τον επιτιθέμενο.
- Για εντολές που υπογράφουν ή επαληθεύουν pointers όπου δεν υπάρχει χρήσιμος modifier, μερικές μορφές χρησιμοποιούν zero ή ένα implicit constant.
Apple / iOS / XNU Customizations & Observations
- Η υλοποίηση PAC της Apple περιλαμβάνει per-boot diversifiers ώστε τα keys ή τα tweaks να αλλάζουν κάθε boot, εμποδίζοντας την επαναχρησιμοποίηση μεταξύ boots.
- Επίσης περιλαμβάνουν cross-domain mitigations ώστε τα PACs που υπογράφονται σε user mode να μην μπορούν εύκολα να επαναχρησιμοποιηθούν σε kernel mode, κ.λπ.
- Στο Apple M1 / Apple Silicon, το reverse engineering έδειξε ότι υπάρχουν nine modifier types και Apple-specific system registers για τον έλεγχο των keys.
- Η Apple χρησιμοποιεί PAC σε πολλά kernel subsystems: return address signing, pointer integrity σε kernel data, signed thread contexts, κ.λπ.
- Το Google Project Zero έδειξε πώς υπό ένα powerful memory read/write primitive σε kernel, κάποιος θα μπορούσε να forge kernel PACs (για A keys) σε A12-era συσκευές, αλλά η Apple έφτιαξε πολλές από αυτές τις διαδρομές.
- Στο σύστημα της Apple, κάποια keys είναι global across kernel, ενώ οι user processes μπορεί να έχουν per-process key randomness.
PAC Bypasses
- Kernel-mode PAC: theoretical vs real bypasses
- Επειδή τα kernel PAC keys και η λογική είναι στενά ελεγχόμενα (privileged registers, diversifiers, domain isolation), το να κατασκευάσει κανείς πλαστά signed kernel pointers είναι πολύ δύσκολο.
- Ο Azad στο 2020 “iOS Kernel PAC, One Year Later” αναφέρει ότι σε iOS 12-13 βρήκε μερικά partial bypasses (signing gadgets, reuse of signed states, unprotected indirect branches) αλλά όχι πλήρες generic bypass. bazad.github.io
- Οι Apple “Dark Magic” customizations περιορίζουν ακόμα περισσότερο τις εκμεταλλεύσιμες επιφάνειες (domain switching, per-key enabling bits). i.blackhat.com
- Υπάρχει γνωστό kernel PAC bypass CVE-2023-32424 σε Apple silicon (M1/M2) αναφερθέν από Zecao Cai et al. i.blackhat.com
- Αλλά αυτά τα bypasses συχνά βασίζονται σε πολύ συγκεκριμένα gadgets ή implementation bugs· δεν είναι γενικής χρήσης bypass.
Επομένως το kernel PAC θεωρείται πολύ ισχυρό, αν και όχι τέλειο.
- User-mode / runtime PAC bypass techniques
Αυτά είναι πιο συνηθισμένα, και εκμεταλλεύονται ατέλειες στον τρόπο που το PAC εφαρμόζεται ή χρησιμοποιείται στο dynamic linking / runtime frameworks. Παρακάτω είναι κατηγορίες με παραδείγματα.
2.1 Shared Cache / A key issues
- Το dyld shared cache είναι ένα μεγάλο pre-linked blob system frameworks και libraries. Επειδή χρησιμοποιείται ευρέως, function pointers μέσα στο shared cache είναι “pre-signed” και χρησιμοποιούνται από πολλές διεργασίες. Οι επιτιθέμενοι στοχεύουν αυτά τα ήδη-signed pointers ως “PAC oracles”.
- Κάποιες bypass τεχνικές προσπαθούν να εξάγουν ή να επαναχρησιμοποιήσουν A-key signed pointers που υπάρχουν στο shared cache και να τα επαναχρησιμοποιήσουν σε gadgets.
- Το talk “No Clicks Required” περιγράφει την κατασκευή ενός oracle πάνω στο shared cache για να συμπεράνει relative addresses και να το συνδυάσει με signed pointers για να παρακάμψει PAC. saelo.github.io
- Επίσης, οι imports function pointers από shared libraries σε userspace βρέθηκαν ότι δεν προστατεύονται επαρκώς από PAC, επιτρέποντας σε έναν επιτιθέμενο να αποκτήσει function pointers χωρίς να αλλάξει την υπογραφή τους. (Project Zero bug entry) bugs.chromium.org
2.2 dlsym(3) / dynamic symbol resolution
- Ένα γνωστό bypass είναι να καλέσει κανείς
dlsym()για να πάρει έναν ήδη signed function pointer (signed με A-key, diversifier zero) και μετά να τον χρησιμοποιήσει. Επειδή τοdlsymεπιστρέφει έναν νόμιμα signed pointer, η χρήση του παρακάμπτει την ανάγκη να κατασκευαστεί PAC. - Το blog του Epsilon εξηγεί πώς κάποια bypasses εκμεταλλεύονται αυτό: κλήση
dlsym("someSym")επιστρέφει έναν signed pointer και μπορεί να χρησιμοποιηθεί για indirect calls. blog.epsilon-sec.com - Το Synacktiv “iOS 18.4 — dlsym considered harmful” περιγράφει ένα bug: κάποια symbols που επιλύονται μέσω
dlsymσε iOS 18.4 επιστρέφουν pointers που είναι λάθος signed (ή με buggy diversifiers), επιτρέποντας ανεπιθύμητο PAC bypass. Synacktiv - Η λογική στο dyld για dlsym περιλαμβάνει: όταν
result->isCode, υπογράφουν τον επιστρεφόμενο pointer με__builtin_ptrauth_sign_unauthenticated(..., key_asia, 0), δηλαδή context zero. blog.epsilon-sec.com
Έτσι, το dlsym είναι συχνός φορέας σε user-mode PAC bypasses.
2.3 Other DYLD / runtime relocations
- Ο DYLD loader και η dynamic relocation λογική είναι περίπλοκες και μερικές φορές προσωρινά map-άρουν σελίδες ως read/write για να κάνουν relocations, και μετά τις επιστρέφουν σε read-only. Οι επιτιθέμενοι εκμεταλλεύονται αυτά τα παράθυρα. Η ομιλία του Synacktiv περιγράφει την “Operation Triangulation”, ένα timing-based bypass του PAC μέσω dynamic relocations. Synacktiv
- Οι DYLD σελίδες τώρα προστατεύονται με SPRR / VM_FLAGS_TPRO (κάποια protection flags για dyld). Αλλά παλαιότερες εκδόσεις είχαν ασθενέστερους φραγμούς. Synacktiv
- Σε WebKit exploit chains, ο DYLD loader είναι συχνά στόχος για PAC bypass. Τα slides αναφέρουν ότι πολλά PAC bypasses έχουν στοχεύσει τον DYLD loader (μέσω relocation, interposer hooks). Synacktiv
2.4 NSPredicate / NSExpression / ObjC / SLOP
- Σε userland exploit chains, Objective-C runtime methods όπως
NSPredicate,NSExpressionήNSInvocationχρησιμοποιούνται για να μεταφέρουν control calls χωρίς προφανή pointer forging. - Σε παλαιότερο iOS (πριν PAC), ένα exploit χρησιμοποίησε fake NSInvocation objects για να καλέσει arbitrary selectors σε controlled μνήμη. Με PAC, η τεχνική χρειάζεται προσαρμογές. Αλλά η τεχνική SLOP (SeLector Oriented Programming) επεκτάθηκε και υπό PAC. Project Zero
- Η αρχική SLOP τεχνική επέτρεπε chaining ObjC κλήσεων δημιουργώντας fake invocations· το bypass βασίζεται στο ότι ISA ή selector pointers μερικές φορές δεν προστατεύονται πλήρως από PAC. Project Zero
- Σε περιβάλλοντα όπου η pointer authentication εφαρμόζεται μερικώς, methods / selectors / target pointers ενδέχεται να μην έχουν πάντα PAC προστασία, δίνοντας περιθώριο για bypass.
Example Flow
Example Signing & Authenticating
``` ; Example: function prologue / return address protection my_func: stp x29, x30, [sp, #-0x20]! ; push frame pointer + LR mov x29, sp PACIASP ; sign LR (x30) using SP as modifier ; … body … mov sp, x29 ldp x29, x30, [sp], #0x20 ; restore AUTIASP ; authenticate & strip PAC ret; Example: indirect function pointer stored in a struct ; suppose X1 contains a function pointer PACDA X1, X2 ; sign data pointer X1 with context X2 STR X1, [X0] ; store signed pointer
; later retrieval: LDR X1, [X0] AUTDA X1, X2 ; authenticate & strip BLR X1 ; branch to valid target
; Example: stripping for comparison (unsafe) LDR X1, [X0] XPACI X1 ; strip PAC (instruction domain) CMP X1, #some_label_address BEQ matched_label
</details>
<details>
<summary>Παράδειγμα</summary>
A buffer overflow overwrites a return address on the stack. The attacker writes the target gadget address but cannot compute the correct PAC. When the function returns, the CPU’s `AUTIA` instruction faults because the PAC mismatch. The chain fails.
Project Zero’s analysis on A12 (iPhone XS) showed how Apple’s PAC is used and methods of forging PACs if an attacker has a memory read/write primitive.
</details>
### 9. **Branch Target Identification (BTI)**
**Εισήχθη με ARMv8.5 (νεότερο hardware)**
BTI είναι ένα hardware χαρακτηριστικό που ελέγχει τα **indirect branch targets**: όταν εκτελείται `blr` ή indirect calls/jumps, ο στόχος πρέπει να ξεκινά με ένα **BTI landing pad** (`BTI j` ή `BTI c`). Το άλμα σε gadget addresses που δεν έχουν το landing pad προκαλεί εξαίρεση.
LLVM’s implementation notes three variants of BTI instructions and how they map to branch types.
| 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 |
- Σε κώδικα που έχει μεταγλωττιστεί με branch target enforcement, οι compilers εισάγουν μια εντολή BTI (C, J, ή JC) σε κάθε έγκυρο indirect-branch target (αρχές συναρτήσεων ή blocks προσβάσιμα από jumps) έτσι ώστε τα indirect branches να επιτυγχάνουν μόνο σε αυτά τα σημεία.
- **Direct branches / calls** (δηλαδή σταθερές διευθύνσεις `B`, `BL`) **δεν περιορίζονται** από το BTI. Η υπόθεση είναι ότι οι σελίδες κώδικα είναι αξιόπιστες και ο attacker δεν μπορεί να τις αλλάξει (οπότε τα direct branches θεωρούνται ασφαλή).
- Επίσης, **RET / return** εντολές γενικά δεν περιορίζονται από το BTI επειδή οι διευθύνσεις επιστροφής προστατεύονται μέσω PAC ή return signing μηχανισμών.
#### Mechanism and enforcement
- Όταν η CPU αποκωδικοποιεί ένα **indirect branch (BLR / BR)** σε μια σελίδα που είναι σημειωμένη ως “guarded / BTI-enabled,” ελέγχει αν η πρώτη εντολή στη διεύθυνση-στόχο είναι έγκυρο BTI (C, J, ή JC όπως επιτρέπεται). Αν όχι, συμβαίνει **Branch Target Exception**.
- Η κωδικοποίηση της εντολής BTI έχει σχεδιαστεί να ξαναχρησιμοποιεί opcodes που προηγουμένως ήταν reserved για NOPs (σε παλαιότερες εκδόσεις ARM). Έτσι τα BTI-enabled binaries είναι backward-compatible: σε hardware χωρίς υποστήριξη BTI, αυτές οι εντολές συμπεριφέρονται ως NOP.
- Τα compiler passes που προσθέτουν BTIs τα εισάγουν μόνο όπου χρειάζεται: σε συναρτήσεις που μπορεί να κληθούν έμμεσα, ή σε basic blocks που στοχεύονται από jumps.
- Κάποιες patches και κώδικας LLVM δείχνουν ότι το BTI δεν εισάγεται για *όλα* τα basic blocks — μόνο για αυτά που είναι potential branch targets (π.χ. από switch / jump tables).
#### BTI + PAC synergy
Το PAC προστατεύει την τιμή του pointer (την πηγή) — εξασφαλίζει ότι η αλυσίδα των indirect calls / returns δεν έχει παραποιηθεί.
Το BTI εξασφαλίζει ότι ακόμα και ένας έγκυρος pointer πρέπει να στοχεύει μόνο σωστά σημειωμένα entry points.
Σε συνδυασμό, ο attacker χρειάζεται τόσο έναν έγκυρο pointer με σωστό PAC όσο και το ότι ο στόχος έχει BTI. Αυτό αυξάνει την δυσκολία κατασκευής exploit gadgets.
#### Example
<details>
<summary>Παράδειγμα</summary>
Ένα exploit προσπαθεί να κάνει pivot σε gadget στη διεύθυνση `0xABCDEF` που δεν ξεκινά με `BTI c`. Η CPU, κατά την εκτέλεση του `blr x0`, ελέγχει τον στόχο και faultάρει επειδή η ευθυγράμμιση της εντολής δεν περιλαμβάνει έγκυρο landing pad. Έτσι πολλά gadgets γίνονται μη χρησιμοποιήσιμα εκτός αν έχουν το BTI prefix.
</details>
### 10. **Privileged Access Never (PAN) & Privileged Execute Never (PXN)**
**Εισήχθη σε νεότερες επεκτάσεις ARMv8 / υποστήριξη iOS (για hardened kernel)**
#### PAN (Privileged Access Never)
- **PAN** είναι ένα χαρακτηριστικό που εισήχθη στο **ARMv8.1-A** και αποτρέπει τον **privileged code** (EL1 ή EL2) από το να **διαβάζει ή να γράφει** μνήμη που έχει χαρακτηριστεί ως **user-accessible (EL0)**, εκτός αν το PAN απενεργοποιηθεί ρητώς.
- Η ιδέα: ακόμα κι αν ο kernel παραπλανηθεί ή συμβιβαστεί, δεν μπορεί αυθαίρετα να dereference-άρει user-space pointers χωρίς πρώτα να *καθαρίσει* το PAN, μειώνοντας τον κίνδυνο επιθέσεων τύπου **`ret2usr`** ή κακής χρήσης user-controlled buffers.
- Όταν το PAN είναι ενεργοποιημένο (PSTATE.PAN = 1), οποιαδήποτε privileged load/store εντολή που προσπελαύνει μια virtual address που είναι “accessible at EL0” προκαλεί **permission fault**.
- Ο kernel, όταν πρέπει νόμιμα να προσπελάσει user-space μνήμη (π.χ. copy data to/from user buffers), πρέπει **προσωρινά να απενεργοποιήσει το PAN** (ή να χρησιμοποιήσει “unprivileged load/store” εντολές) για να επιτρέψει την πρόσβαση.
- Στο Linux σε ARM64, η υποστήριξη PAN εισήχθη περίπου το 2015: patches στον kernel πρόσθεσαν detection του feature και αντικατέστησαν `get_user` / `put_user` κ.λπ. με παραλλαγές που καθαρίζουν το PAN γύρω από user memory accesses.
**Κύρια λεπτομέρεια / περιορισμός / bug**
- Όπως έχει επισημάνει ο Siguza και άλλοι, ένα specification bug (ή ασαφής συμπεριφορά) στο σχεδιασμό του ARM σημαίνει ότι **execute-only user mappings** (`--x`) ενδέχεται **να μην ενεργοποιούν το PAN**. Με άλλα λόγια, αν μια user σελίδα είναι markαρισμένη ως executable αλλά χωρίς read permission, η προσπάθεια του kernel να τη διαβάσει μπορεί να παρακάμψει το PAN επειδή η αρχιτεκτονική θεωρεί ότι “accessible at EL0” απαιτεί readable permission, όχι απαραιτήτως executable. Αυτό οδηγεί σε PAN bypass σε ορισμένες υλοποιήσεις.
- Εξαιτίας αυτού, αν iOS / XNU επιτρέπει execute-only user pages (όπως σε κάποια JIT ή code-cache setups), ο kernel μπορεί κατά λάθος να διαβάσει από αυτές ακόμη και με PAN ενεργό. Αυτό είναι μια γνώριμη, λεπτή, εκμεταλλεύσιμη περιοχή σε κάποια ARMv8+ συστήματα.
#### PXN (Privileged eXecute Never)
- **PXN** είναι ένα flag στον page table (σε leaf ή block entries) που δείχνει ότι η σελίδα **δεν είναι εκτελέσιμη όταν τρέχει σε privileged mode** (δηλαδή όταν το EL1 εκτελεί).
- Το PXN αποτρέπει τον kernel (ή οποιοδήποτε privileged code) από το να κάνει jump ή να εκτελέσει εντολές από user-space σελίδες ακόμα και αν ο έλεγχος εκτρέπεται εκεί. Ουσιαστικά, σταματά την εκτέλεση κώδικα σε user memory από privileged επίπεδο.
- Σε συνδυασμό με το PAN, αυτό διασφαλίζει ότι:
1. Ο kernel δεν μπορεί (κατά προεπιλογή) να διαβάσει ή να γράψει user-space δεδομένα (PAN)
2. Ο kernel δεν μπορεί να εκτελέσει user-space κώδικα (PXN)
- Στο ARMv8 page table format, οι leaf entries έχουν ένα `PXN` bit (και επίσης `UXN` για unprivileged execute-never) στα attribute bits τους.
Έτσι, ακόμα κι αν ο kernel έχει έναν κατεστραμμένο function pointer που δείχνει σε user μνήμη και επιχειρήσει να κάνει branch εκεί, το PXN bit θα προκαλέσει fault.
#### Memory-permission model & how PAN and PXN map to page table bits
Για να κατανοήσετε πώς δουλεύουν το PAN / PXN, χρειάζεται να δείτε το μοντέλο μετάφρασης και τα permission του ARM (απλοποιημένο):
- Κάθε page ή block entry έχει πεδία attributes, μεταξύ των οποίων **AP[2:1]** για access permissions (read/write, privileged vs unprivileged) και bits **UXN / PXN** για execute-never περιορισμούς.
- Όταν PSTATE.PAN είναι 1 (enabled), το hardware εφαρμόζει τροποποιημένη σημασιολογία: privileged προσβάσεις σε σελίδες που είναι σημειωμένες ως “accessible by EL0” (δηλαδή user-accessible) απαγορεύονται (fault).
- Εξαιτίας του αναφερθέντος bug, σελίδες που έχουν μόνο execute permission (χωρίς read) ενδέχεται να μην θεωρούνται “accessible by EL0” σε κάποιες υλοποιήσεις, άρα να παρακάμπτεται το PAN.
- Όταν το PXN bit μιας σελίδας είναι set, ακόμα κι αν το instruction fetch προέρχεται από πιο υψηλό privilege level, η εκτέλεση απαγορεύεται.
#### Kernel usage of PAN / PXN in a hardened OS (e.g. iOS / XNU)
Σε ένα hardened kernel design (όπως πιθανώς αυτό που χρησιμοποιεί η Apple):
- Ο kernel ενεργοποιεί το PAN από προεπιλογή (ώστε ο privileged κώδικας να είναι περιορισμένος).
- Σε μονοπάτια που νόμιμα χρειάζεται να διαβάσει ή να γράψει user buffers (π.χ. syscall buffer copy, I/O, read/write user pointer), ο kernel προσωρινά **απενεργοποιεί το PAN** ή χρησιμοποιεί ειδικές εντολές για override.
- Μετά την πρόσβαση στα user δεδομένα, πρέπει να ενεργοποιήσει ξανά το PAN.
- Το PXN εφαρμόζεται μέσω των page tables: οι user σελίδες έχουν PXN = 1 (ώστε ο kernel να μην μπορεί να τις εκτελέσει), οι kernel σελίδες δεν έχουν PXN (ώστε να εκτελείται kernel κώδικας).
- Ο kernel πρέπει να διασφαλίσει ότι κανένα code path δεν οδηγεί σε εκτέλεση μέσα σε user memory regions (που θα μπορούσε να παρακάμψει το PXN) — ώστε αλυσίδες exploits που βασίζονται στο “jump into user-controlled shellcode” να μπλοκάρονται.
Εξαιτίας της προαναφερθείσας PAN bypass μέσω execute-only σελίδων, σε ένα πραγματικό σύστημα η Apple μπορεί να απενεργοποιεί ή να απαγορεύει τις execute-only user pages, ή να πατεντάρει γύρω από την αδυναμία της προδιαγραφής.
#### Attack surfaces, bypasses, and mitigations
- **PAN bypass via execute-only pages**: όπως συζητήθηκε, η προδιαγραφή αφήνει ένα κενό: user σελίδες με execute-only (χωρίς read perm) μπορεί να μην μετρούν ως “accessible at EL0,” οπότε το PAN δεν μπλοκάρει kernel reads από αυτές σε κάποιες υλοποιήσεις. Αυτό δίνει στον attacker ένα ασυνήθιστο μονοπάτι να τροφοδοτήσει δεδομένα μέσω “execute-only” τμημάτων.
- **Temporal window exploit**: αν ο kernel απενεργοποιεί το PAN για παράθυρο μεγαλύτερο από το απαραίτητο, μια race ή κακόβουλη ροή μπορεί να εκμεταλλευτεί αυτό το διάστημα για ανεπιθύμητη πρόσβαση user memory.
- **Forgotten re-enable**: αν κάποιο μονοπάτι στον κώδικα ξεχάσει να ενεργοποιήσει ξανά το PAN, επακόλουθες kernel λειτουργίες μπορεί να προσπελάσουν λανθασμένα user μνήμη.
- **Misconfiguration of PXN**: αν οι page tables δεν βάλουν PXN στις user σελίδες ή γίνεται λάθος mapping των user code σελίδων, ο kernel μπορεί να παραπλανηθεί να εκτελέσει user-space κώδικα.
- **Speculation / side-channels**: αναλογικά με speculative bypasses, μπορεί να υπάρχουν μικροαρχιτεκτονικές παρενέργειες που προκαλούν παροδική παραβίαση των PAN / PXN ελέγχων (παρόλο που τέτοιες επιθέσεις εξαρτώνται σε μεγάλο βαθμό από το design της CPU).
- **Complex interactions**: Σε πιο σύνθετες λειτουργίες (π.χ. JIT, shared memory, code regions που δημιουργούνται δυναμικά), ο kernel μπορεί να χρειαστεί λεπτομερή έλεγχο για να επιτρέψει ορισμένες memory accesses ή εκτέλεση σε user-mapped περιοχές· το να σχεδιαστεί αυτό με ασφάλεια υπό PAN/PXN περιορισμούς δεν είναι απλό.
#### Παράδειγμα
<details>
<summary>Παράδειγμα κώδικα</summary>
Εδώ είναι ενδεικτικές pseudo-assembly ακολουθίες που δείχνουν την ενεργοποίηση/απενεργοποίηση του PAN γύρω από πρόσβαση σε user μνήμη, και πώς μπορεί να συμβεί ένα fault.
</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
If the kernel had **not** set PXN on that user page, then the branch might succeed — which would be insecure.
If the kernel forgets to re-enable PAN after user memory access, it opens a window where further kernel logic might accidentally read/write arbitrary user memory.
If the user pointer is into an execute-only page (user page with only execute permission, no read/write), under the PAN spec bug, `ldr W2, [X1]` might **not** fault even with PAN enabled, enabling a bypass exploit, depending on implementation.
</details>
<details>
<summary>Παράδειγμα</summary>
A kernel vulnerability tries to take a user-provided function pointer and call it in kernel context (i.e. `call user_buffer`). Under PAN/PXN, that operation is disallowed or faults.
</details>
---
### 11. **Top Byte Ignore (TBI) / Pointer Tagging**
**Εισήχθη στο ARMv8.5 / νεότερο (ή προαιρετική επέκταση)**
TBI means the top byte (most-significant byte) of a 64-bit pointer is ignored by address translation. This lets OS or hardware embed **tag bits** in the pointer’s top byte without affecting the actual address.
- TBI stands for **Top Byte Ignore** (sometimes called *Address Tagging*). It is a hardware feature (available in many ARMv8+ implementations) that **ignores the top 8 bits** (bits 63:56) of a 64-bit pointer when performing **address translation / load/store / instruction fetch**.
- In effect, the CPU treats a pointer `0xTTxxxx_xxxx_xxxx` (where `TT` = top byte) as `0x00xxxx_xxxx_xxxx` for the purposes of address translation, ignoring (masking off) the top byte. The top byte can be used by software to store **metadata / tag bits**.
- This gives software “free” in-band space to embed a byte of tag in each pointer without altering which memory location it refers to.
- The architecture ensures that loads, stores, and instruction fetch treat the pointer with its top byte masked (i.e. tag stripped off) before performing the actual memory access.
Thus TBI decouples the **logical pointer** (pointer + tag) from the **physical address** used for memory operations.
#### Γιατί TBI: Χρήσεις και κίνητρα
- **Pointer tagging / metadata**: You can store extra metadata (e.g. object type, version, bounds, integrity tags) in that top byte. When you later use the pointer, the tag is ignored at hardware level, so you don’t need to strip manually for the memory access.
- **Memory tagging / MTE (Memory Tagging Extension)**: TBI is the base hardware mechanism that MTE builds on. In ARMv8.5, the **Memory Tagging Extension** uses bits 59:56 of the pointer as a **logical tag** and checks it against an **allocation tag** stored in memory.
- **Enhanced security & integrity**: By combining TBI with pointer authentication (PAC) or runtime checks, you can force not just the pointer value but also the tag to be correct. An attacker overwriting a pointer without the correct tag will produce a mismatched tag.
- **Compatibility**: Because TBI is optional and tag bits are ignored by hardware, existing untagged code continues to operate normally. The tag bits effectively become “don’t care” bits for legacy code.
#### Παράδειγμα
<details>
<summary>Παράδειγμα</summary>
A function pointer included a tag in its top byte (say `0xAA`). An exploit overwrites the pointer low bits but neglects the tag, so when the kernel verifies or sanitizes, the pointer fails or is rejected.
</details>
---
### 12. **Page Protection Layer (PPL)**
**Εισήχθη στα τέλη του iOS / σύγχρονο hardware (iOS ~17 / Apple silicon / high-end models)** (μερικές αναφορές δείχνουν PPL circa macOS / Apple silicon, αλλά Apple φέρνει ανάλογες προστασίες στο iOS)
- PPL is designed as an **intra-kernel protection boundary**: even if the kernel (EL1) is compromised and has read/write capabilities, **it should not be able to freely modify** certain **sensitive pages** (especially page tables, code-signing metadata, kernel code pages, entitlements, trust caches, etc.).
- It effectively creates a **“kernel within the kernel”** — a smaller trusted component (PPL) with **elevated privileges** that alone can modify protected pages. Other kernel code must call into PPL routines to effect changes.
- This reduces the attack surface for kernel exploits: even with full arbitrary R/W/execute in kernel mode, exploit code must also somehow get into the PPL domain (or bypass PPL) to modify critical structures.
- On newer Apple silicon (A15+ / M2+), Apple is transitioning to **SPTM (Secure Page Table Monitor)**, which in many cases replaces PPL for page-table protection on those platforms.
Here’s how PPL is believed to operate, based on public analysis:
#### Use of APRR / permission routing (APRR = Access Permission ReRouting)
- Apple hardware uses a mechanism called **APRR (Access Permission ReRouting)**, which allows page table entries (PTEs) to contain small indices, rather than full permission bits. Those indices are mapped via APRR registers to actual permissions. This allows dynamic remapping of permissions per domain.
- PPL leverages APRR to segregate privilege within kernel context: only the PPL domain is permitted to update the mapping between indices and effective permissions. That is, when non-PPL kernel code writes a PTE or tries to flip permission bits, the APRR logic disallows it (or enforces read-only mapping).
- PPL code itself runs in a restricted region (e.g. `__PPLTEXT`) which is normally non-executable or non-writable until entry gates temporarily allow it. The kernel calls PPL entry points (“PPL routines”) to perform sensitive operations.
#### Gate / Entry & Exit
- When the kernel needs to modify a protected page (e.g. change permissions of a kernel code page, or modify page tables), it calls into a **PPL wrapper** routine, which does validation and then transitions into the PPL domain. Outside that domain, the protected pages are effectively read-only or non-modifiable by the main kernel.
- During PPL entry, the APRR mappings are adjusted so that memory pages in the PPL region are set to **executable & writable** within PPL. Upon exit, they are returned to read-only / non-writable. This ensures that only well-audited PPL routines can write to protected pages.
- Outside PPL, attempts by kernel code to write to those protected pages will fault (permission denied) because the APRR mapping for that code domain doesn’t permit writing.
#### Protected page categories
The pages that PPL typically protects include:
- Page table structures (translation table entries, mapping metadata)
- Kernel code pages, especially those containing critical logic
- Code-sign metadata (trust caches, signature blobs)
- Entitlement tables, signature enforcement tables
- Other high-value kernel structures where a patch would allow bypassing signature checks or credentials manipulation
The idea is that even if the kernel memory is fully controlled, the attacker cannot simply patch or rewrite these pages, unless they also compromise PPL routines or bypass PPL.
#### Known Bypasses & Vulnerabilities
1. **Project Zero’s PPL bypass (stale TLB trick)**
- A public writeup by Project Zero describes a bypass involving **stale TLB entries**.
- The idea:
1. Allocate two physical pages A and B, mark them as PPL pages (so they are protected).
2. Map two virtual addresses P and Q whose L3 translation table pages come from A and B.
3. Spin a thread to continuously access Q, keeping its TLB entry alive.
4. Call `pmap_remove_options()` to remove mappings starting at P; due to a bug, the code mistakenly removes the TTEs for both P and Q, but only invalidates the TLB entry for P, leaving Q’s stale entry live.
5. Reuse B (page Q’s table) to map arbitrary memory (e.g. PPL-protected pages). Because the stale TLB entry still maps Q’s old mapping, that mapping remains valid for that context.
6. Through this, the attacker can put writable mapping of PPL-protected pages in place without going through PPL interface.
- This exploit required fine control of physical mapping and TLB behavior. It demonstrates that a security boundary relying on TLB / mapping correctness must be extremely careful about TLB invalidations and mapping consistency.
- Project Zero commented that bypasses like this are subtle and rare, but possible in complex systems. Still, they regard PPL as a solid mitigation.
2. **Other potential hazards & constraints**
- If a kernel exploit can directly enter PPL routines (via calling the PPL wrappers), it might bypass restrictions. Thus argument validation is critical.
- Bugs in the PPL code itself (e.g. arithmetic overflow, boundary checks) can allow out-of-bounds modifications inside PPL. Project Zero observed that such a bug in `pmap_remove_options_internal()` was exploited in their bypass.
- The PPL boundary is irrevocably tied to hardware enforcement (APRR, memory controller), so it's only as strong as the hardware implementation.
#### Παράδειγμα
<details>
<summary>Παράδειγμα Κώδικα</summary>
Here’s a simplified pseudocode / logic showing how a kernel might call into PPL to modify protected pages:
```c
// In kernel (outside PPL domain)
function kernel_modify_pptable(pt_addr, new_entry) {
// validate arguments, etc.
return ppl_call_modify(pt_addr, new_entry) // call PPL wrapper
}
// In PPL (trusted domain)
function ppl_call_modify(pt_addr, new_entry) {
// temporarily enable write access to protected pages (via APRR adjustments)
aprr_set_index_for_write(PPL_INDEX)
// perform the modification
*pt_addr = new_entry
// restore permissions (make pages read-only again)
aprr_restore_default()
return success
}
// If kernel code outside PPL does:
*pt_addr = new_entry // a direct write
// It will fault because APRR mapping for non-PPL domain disallows write to that page
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 / Αντικαταστάσεις / Μέλλον
- Σε ορισμένα σύγχρονα SoCs της Apple (A15 ή νεότερα, M2 ή νεότερα), η Apple υποστηρίζει το SPTM (Secure Page Table Monitor), που αντικαθιστά το PPL για τις προστασίες των page tables.
- Η Apple αναφέρει στη τεκμηρίωση: “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.”
- Η αρχιτεκτονική SPTM πιθανώς μεταφέρει περισσότερη επιβολή πολιτικών σε έναν monitor με υψηλότερα προνόμια εκτός του ελέγχου του kernel, μειώνοντας περαιτέρω το όριο εμπιστοσύνης.
MTE | EMTE | MIE
Ακολουθεί μια πιο γενική περιγραφή του πώς λειτουργεί το EMTE στο πλαίσιο της ρύθμισης MIE της Apple:
- Tag assignment
- Όταν γίνεται allocation μνήμης (π.χ. στο kernel ή στο user space μέσω secure allocators), σε αυτό το block ανατίθεται ένα secret tag.
- Ο pointer που επιστρέφεται στον χρήστη ή το kernel περιλαμβάνει αυτό το tag στα υψηλά του bit (χρησιμοποιώντας TBI / top byte ignore μηχανισμούς).
- Tag checking on access
- Όταν εκτελείται ένα load ή store χρησιμοποιώντας έναν pointer, το hardware ελέγχει ότι το tag του pointer ταιριάζει με το tag του memory block (allocation tag). Σε περίπτωση mismatch, προκαλεί fault αμέσως (εφόσον είναι synchronous).
- Επειδή είναι synchronous, δεν υπάρχει παράθυρο «delayed detection».
- Retagging on free / reuse
- Όταν η μνήμη απελευθερώνεται, ο allocator αλλάζει το tag του block (έτσι παλαιότεροι pointers με τα παλιά tags δεν ταιριάζουν πλέον).
- Ένας use-after-free pointer θα έχει επομένως ένα stale tag και mismatch κατά την πρόσβαση.
- Neighbor-tag differentiation to catch overflows
- Γειτονικές allocations λαμβάνουν διαφορετικά tags. Αν ένα buffer overflow μεταφερθεί στη μνήμη του γείτονα, το tag mismatch προκαλεί fault.
- Αυτό είναι ιδιαίτερα ισχυρό στο να εντοπίζει μικρά overflows που διασχίζουν τα όρια.
- Tag confidentiality enforcement
- Η Apple πρέπει να αποτρέψει τα tag values από being leaked (επειδή αν ο attacker μάθει το tag, μπορεί να φτιάξει pointers με τα σωστά tags).
- Συμπεριλαμβάνουν προστασίες (microarchitectural / speculative controls) για να αποφευχθεί side-channel leakage των bit του tag.
- Kernel and user-space integration
- Η Apple χρησιμοποιεί το EMTE όχι μόνο στο user-space αλλά και σε kernel / OS-critical components (για να προστατέψει το kernel από memory corruption).
- Το hardware/OS διασφαλίζει ότι οι κανόνες των tags εφαρμόζονται ακόμα και όταν το kernel εκτελείται εκ μέρους του 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>
#### Περιορισμοί & προκλήσεις
- **Intrablock overflows**: Εάν το overflow παραμένει στην ίδια allocation (δεν διασχίζει boundary) και το tag παραμένει το ίδιο, το tag mismatch δεν το ανιχνεύει.
- **Tag width limitation**: Μόνο λίγα bits (π.χ. 4 bits, ή μικρό domain) είναι διαθέσιμα για tag — περιορισμένο namespace.
- **Side-channel leaks**: Εάν τα tag bits μπορούν να be leaked (via cache / speculative execution), ο attacker μπορεί να μάθει έγκυρα tags και να παρακάμψει. Η εφαρμογή tag confidentiality enforcement από Apple στοχεύει στο να το μετριάσει.
- **Performance overhead**: Έλεγχοι tag σε κάθε load/store προσθέτουν κόστος; η Apple πρέπει να βελτιστοποιήσει το hardware για να μειώσει το overhead.
- **Compatibility & fallback**: Σε παλαιότερο hardware ή σε μέρη που δεν υποστηρίζουν EMTE, πρέπει να υπάρχει fallback. Η Apple δηλώνει ότι MIE ενεργοποιείται μόνο σε συσκευές με υποστήριξη.
- **Complex allocator logic**: Ο allocator πρέπει να διαχειρίζεται tags, retagging, aligning boundaries, και να αποφεύγει mis-tag collisions. Bugs στη λογική του allocator μπορεί να εισάγουν ευπάθειες.
- **Mixed memory / hybrid areas**: Μερική μνήμη μπορεί να παραμείνει untagged (legacy), καθιστώντας την interoperability πιο περίπλοκη.
- **Speculative / transient attacks**: Όπως πολλές μικροαρχιτεκτονικές προστασίες, speculative execution ή micro-op fusions μπορεί να παρακάμψουν checks transiently ή να leak tag bits.
- **Limited to supported regions**: Η Apple μπορεί να εφαρμόζει EMTE μόνο σε επιλεγμένες, high-risk περιοχές (kernel, security-critical subsystems), όχι καθολικά.
---
## Key enhancements / differences compared to standard MTE
Εδώ είναι οι βελτιώσεις και οι αλλαγές που τονίζει η Apple:
| 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 απαιτεί ότι accesses από tagged region προς non-tagged memory επίσης validate tag knowledge, καθιστώντας πιο δύσκολο να παρακαμφθεί με mixing allocations.|
| **Tag confidentiality / secrecy** | Tags might be observable or leaked via side channels | Η Apple προσθέτει **Tag Confidentiality Enforcement**, που στοχεύει στο να αποτρέψει το leakage των τιμών tag (via speculative side-channels κ.λπ.).|
| **Allocator integration & retagging** | MTE leaves much of allocator logic to software | Οι secure typed allocators της Apple (kalloc_type, xzone malloc, κ.λπ.) ενσωματώνονται με EMTE: όταν γίνεται allocation ή free, τα tags διαχειρίζονται σε λεπτό βαθμό (fine granularity).|
| **Always-on by default** | In many platforms, MTE is optional or off by default | Η Apple ενεργοποιεί EMTE / MIE by default σε υποστηριζόμενο hardware (π.χ. iPhone 17 / A19) για kernel και πολλούς user processes.|
Εφόσον η Apple ελέγχει τόσο το hardware όσο και το software stack, μπορεί να επιβλέπει στενά το EMTE, να αποφεύγει performance προβλήματα και να κλείνει side-channel τρύπες.
---
## How EMTE works in practice (Apple / MIE)
Εδώ είναι μια υψηλού επιπέδου περιγραφή του πώς λειτουργεί το EMTE στο setup της Apple / MIE:
1. **Tag assignment**
- Όταν γίνεται allocation μνήμης (π.χ. στο kernel ή user space μέσω secure allocators), ανατίθεται ένα **secret tag** στο block.
- Το pointer που επιστρέφεται στον χρήστη ή στον kernel περιλαμβάνει αυτό το tag στα high bits (χρησιμοποιώντας TBI / top byte ignore mechanisms).
2. **Tag checking on access**
- Όποτε εκτελείται ένα load ή store χρησιμοποιώντας pointer, το hardware ελέγχει ότι το tag του pointer ταιριάζει με το tag του memory block (allocation tag). Αν mismatch, προκαλεί fault άμεσα (εφόσον synchronous).
- Επειδή είναι synchronous, δεν υπάρχει παράθυρο “delayed detection”.
3. **Retagging on free / reuse**
- Όταν η μνήμη free-άρεται, ο allocator αλλάζει το tag του block (ώστε οι παλαιότεροι pointers με παλιά tags να μην ταιριάζουν).
- Ένα use-after-free pointer θα έχει επομένως stale tag και θα κάνει mismatch όταν γίνει access.
4. **Neighbor-tag differentiation to catch overflows**
- Διπλανά allocations λαμβάνουν διαφορετικά tags. Αν ένα buffer overflow ξεπεράσει σε γειτονική μνήμη, το tag mismatch προκαλεί fault.
- Αυτό είναι ιδιαίτερα ισχυρό για την ανίχνευση μικρών overflows που διασχίζουν boundary.
5. **Tag confidentiality enforcement**
- Η Apple πρέπει να εμποδίσει το να leak-ούν οι τιμές των tag (επειδή αν ο attacker μάθει το tag, θα μπορούσε να κατασκευάσει pointers με σωστά tags).
- Περιλαμβάνουν προστασίες (microarchitectural / speculative controls) για να αποφευχθεί το side-channel leakage των tag bits.
6. **Kernel and user-space integration**
- Η Apple χρησιμοποιεί EMTE όχι μόνο στο user-space αλλά και σε kernel / OS-critical components (για να προστατεύσει το kernel από memory corruption).
- Το hardware/OS διασφαλίζει ότι οι κανόνες tag ισχύουν ακόμα και όταν το kernel εκτελεί εκ μέρους του user space.
Επειδή το EMTE είναι ενσωματωμένο στο MIE, η Apple χρησιμοποιεί EMTE σε synchronous mode σε κρίσιμες επιφάνειες επίθεσης, όχι ως opt-in ή debugging mode.
---
## Exception handling in XNU
Όταν συμβαίνει ένα **exception** (π.χ., `EXC_BAD_ACCESS`, `EXC_BAD_INSTRUCTION`, `EXC_CRASH`, `EXC_ARM_PAC`, κ.λπ.), το **Mach layer** του XNU kernel είναι υπεύθυνο για την abύπνιση του πριν μετατραπεί σε UNIX-style **signal** (όπως `SIGSEGV`, `SIGBUS`, `SIGILL`, ...).
Αυτή η διαδικασία περιλαμβάνει πολλαπλά στρώματα exception propagation και handling πριν φτάσει στο user space ή μετατραπεί σε BSD signal.
### Exception Flow (High-Level)
1. **CPU triggers a synchronous exception** (π.χ., invalid pointer dereference, PAC failure, illegal instruction, κ.λπ.).
2. **Low-level trap handler** τρέχει (`trap.c`, `exception.c` στο XNU source).
3. Ο trap handler καλεί **`exception_triage()`**, τον πυρήνα του Mach exception handling.
4. Το `exception_triage()` αποφασίζει πώς θα δρομολογήσει το exception:
- Πρώτα στο **thread's exception port**.
- Έπειτα στο **task's exception port**.
- Έπειτα στο **host's exception port** (συχνά `launchd` ή `ReportCrash`).
Εάν κανένα από αυτά τα ports δεν χειριστεί το exception, το kernel μπορεί να:
- **Convert it into a BSD signal** (για user-space processes).
- **Panic** (για kernel-space exceptions).
### Core Function: `exception_triage()`
Η συνάρτηση `exception_triage()` δρομολογεί τα Mach exceptions πάνω στην αλυσίδα πιθανών handlers μέχρι κάποιος να το χειριστεί ή μέχρι να γίνει τελικά fatal. Ορίζεται σε `osfmk/kern/exception.c`.
```c
void exception_triage(exception_type_t exception, mach_exception_data_t code, mach_msg_type_number_t codeCnt);
Τυπική Ροή Κλήσεων:
exception_triage() └── exception_deliver() ├── exception_deliver_thread() ├── exception_deliver_task() └── exception_deliver_host()
Εάν όλα αποτύχουν → διαχειρίζεται από bsd_exception() → μεταφράζεται σε ένα σήμα όπως SIGSEGV.
Θύρες Εξαίρεσης
Κάθε αντικείμενο Mach (thread, task, host) μπορεί να καταχωρήσει θύρες εξαίρεσης, στις οποίες αποστέλλονται τα μηνύματα εξαίρεσης.
Ορίζονται από το API:
task_set_exception_ports()
thread_set_exception_ports()
host_set_exception_ports()
Each exception port has:
- A mask (ποιες εξαιρέσεις θέλει να λαμβάνει)
- A port name (Mach port για να λαμβάνει μηνύματα)
- A behavior (πώς ο kernel στέλνει το μήνυμα)
- A flavor (ποιο thread state να συμπεριλάβει)
Debuggers and Exception Handling
A debugger (π.χ. LLDB) sets an exception port στο target task ή thread, συνήθως χρησιμοποιώντας task_set_exception_ports().
When an exception occurs:
- Το Mach message στέλνεται στη διαδικασία του debugger.
- Ο debugger μπορεί να αποφασίσει να handle (resume, modify registers, skip instruction) ή να not handle την εξαίρεση.
- Αν ο debugger δεν την χειριστεί, η εξαίρεση προωθείται στο επόμενο επίπεδο (task → host).
Flow of EXC_BAD_ACCESS
-
Το thread dereferences μη έγκυρη pointer → CPU προκαλεί Data Abort.
-
Ο kernel trap handler καλεί
exception_triage(EXC_BAD_ACCESS, ...). -
Μήνυμα αποστέλλεται σε:
-
Thread port → (o debugger μπορεί να παρακολουθήσει breakpoint).
-
Αν ο debugger αγνοήσει → Task port → (process-level handler).
-
Αν αγνοηθεί → Host port (συνήθως ReportCrash).
- Αν κανείς δεν το χειριστεί →
bsd_exception()μεταφράζει σεSIGSEGV.
PAC Exceptions
Όταν η Pointer Authentication (PAC) αποτυγχάνει (mismatch υπογραφής), εγείρεται μια ειδική Mach εξαίρεση:
EXC_ARM_PAC(type)- Codes μπορεί να περιλαμβάνουν λεπτομέρειες (π.χ., key type, pointer type).
Αν το binary έχει το flag TFRO_PAC_EXC_FATAL, ο kernel αντιμετωπίζει τις αποτυχίες PAC ως fatal, παρακάμπτοντας την παρεμπόδιση από τον debugger. Αυτό γίνεται για να αποτραπεί η χρήση debugger από επιτιθέμενους για να παρακάμψουν τους PAC ελέγχους και είναι ενεργό για platform binaries.
Software Breakpoints
Ένα software breakpoint (int3 σε x86, brk σε ARM64) υλοποιείται προκαλώντας ένα σκόπιμο fault.
Ο debugger το πιάνει μέσω του exception port:
- Τροποποιεί το instruction pointer ή τη μνήμη.
- Επαναφέρει την αρχική εντολή.
- Επαναλαμβάνει την εκτέλεση.
Αυτός ο ίδιος μηχανισμός είναι που επιτρέπει να “πιάσεις” μια PAC exception — εκτός αν το TFRO_PAC_EXC_FATAL είναι σε ισχύ, οπότε ποτέ δεν φτάνει στον debugger.
Conversion to BSD Signals
Αν κανένας handler δεν αποδεχτεί την εξαίρεση:
-
Ο kernel καλεί
task_exception_notify() → bsd_exception(). -
Αυτό χαρτογραφεί Mach exceptions σε 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→ Core ofexception_triage(),exception_deliver_*(). -
bsd/kern/kern_sig.c→ Signal delivery logic. -
osfmk/arm64/trap.c→ Low-level trap handlers. -
osfmk/mach/exc.h→ Exception codes and structures. -
osfmk/kern/task.c→ Task exception port setup.
Old Kernel Heap (Pre-iOS 15 / Pre-A12 era)
Ο kernel χρησιμοποιούσε έναν zone allocator (kalloc) χωρισμένο σε fixed-size “zones.”
Κάθε zone αποθηκεύει allocations μιας μόνο size class.
Από το screenshot:
| Zone Name | Element Size | Example Use |
|---|---|---|
default.kalloc.16 | 16 bytes | Πολύ μικρές kernel structs, pointers. |
default.kalloc.32 | 32 bytes | Μικρές δομές, object headers. |
default.kalloc.64 | 64 bytes | IPC messages, μικροί kernel buffers. |
default.kalloc.128 | 128 bytes | Μεσαία αντικείμενα όπως μέρη του OSObject. |
| … | … | … |
default.kalloc.1280 | 1280 bytes | Μεγάλες δομές, IOSurface/graphics metadata. |
Πώς λειτουργούσε:
- Κάθε αίτημα allocation στρογγυλοποιούνταν πάνω στο κοντινότερο zone size.
(π.χ., ένα αίτημα 50 bytes πάει στην
kalloc.64zone). - Η μνήμη σε κάθε zone κρατούνταν σε μια freelist — chunks που απελευθερώνονταν από τον kernel επέστρεφαν σε εκείνη τη zone.
- Αν υπερβαίνατε ένα 64-byte buffer, θα αντικαθιστούσατε το επόμενο αντικείμενο στην ίδια zone.
Για αυτό το λόγο το heap spraying / feng shui ήταν τόσο αποτελεσματικό: μπορούσες να προβλέψεις γείτονες αντικειμένων ψεκάζοντας allocations της ίδιας size class.
The freelist
Μέσα σε κάθε kalloc zone, τα freed objects δεν επέστρεφαν άμεσα στο σύστημα — πήγαιναν σε μια freelist, μια linked list διαθέσιμων chunks.
-
Όταν ένα chunk ήταν freed, ο kernel έγραφε έναν pointer στην αρχή αυτού του chunk → τη διεύθυνση του επόμενου free chunk στην ίδια zone.
-
Η zone κρατούσε έναν HEAD pointer στο πρώτο free chunk.
-
Το allocation πάντα χρησιμοποιούσε το τρέχον HEAD:
-
Pop HEAD (επιστρέφει αυτή τη μνήμη στον caller).
-
Ενημέρωση HEAD = HEAD->next (αποθηκευμένο στο header του freed chunk).
-
Το freeing ωθούσε τα chunks πίσω:
-
freed_chunk->next = HEAD -
HEAD = freed_chunk
Έτσι η freelist ήταν απλά μια linked list χτισμένη μέσα στη μνήμη των freed chunks.
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)
Εκμετάλλευση του freelist
Επειδή τα πρώτα 8 bytes ενός free chunk = freelist pointer, ένας επιτιθέμενος θα μπορούσε να το αλλοιώσει:
-
Heap overflow σε έναν διπλανό freed chunk → overwrite its “next” pointer.
-
Use-after-free write into a freed object → overwrite its “next” pointer.
Τότε, στην επόμενη allocation αυτού του μεγέθους:
-
Ο allocator pops το corrupted chunk.
-
Ακολουθεί τον από τον επιτιθέμενο παρεχόμενο “next” pointer.
-
Επιστρέφει έναν pointer σε αυθαίρετη μνήμη, επιτρέποντας fake object primitives ή targeted overwrite.
Οπτικό παράδειγμα 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.
Αυτός ο σχεδιασμός του freelist έκανε την εκμετάλλευση ιδιαίτερα αποτελεσματική πριν το hardening: προβλέψιμοι γείτονες από heap sprays, raw pointer freelist links, και η απουσία διαχωρισμού τύπων επέτρεπαν στους επιτιθέμενους να αναβαθμίσουν σφάλματα UAF/overflow σε αυθαίρετο έλεγχο μνήμης του kernel.
Heap Grooming / Feng Shui
Ο σκοπός του heap grooming είναι να διαμορφώσει τη διάταξη του heap έτσι ώστε όταν ένας επιτιθέμενος προκαλέσει overflow ή use-after-free, το αντικείμενο-θύμα να βρίσκεται ακριβώς δίπλα σε ένα αντικείμενο που ελέγχεται από τον επιτιθέμενο.
Με αυτόν τον τρόπο, όταν συμβεί διαφθορά μνήμης, ο επιτιθέμενος μπορεί αξιόπιστα να αντικαταστήσει το αντικείμενο-θύμα με ελεγχόμενα δεδομένα.
Βήματα:
- Spray allocations (fill the holes)
- Με το πέρασμα του χρόνου, το kernel heap κατακερματίζεται: κάποιες ζώνες έχουν κενά όπου παλιά αντικείμενα απελευθερώθηκαν.
- Ο επιτιθέμενος αρχικά κάνει πολλές ψεύτικες allocations για να γεμίσει αυτά τα κενά, έτσι ώστε το heap να γίνει «συμπιεσμένο» και προβλέψιμο.
- Force new pages
- Μόλις τα κενά γεμίσουν, οι επόμενες allocations πρέπει να προέλθουν από νέες σελίδες που προστίθενται στη ζώνη.
- Νέες σελίδες σημαίνουν ότι τα αντικείμενα θα ομαδοποιηθούν μαζί, όχι διασκορπισμένα πάνω σε παλιά κατακερματισμένη μνήμη.
- Αυτό δίνει στον επιτιθέμενο πολύ καλύτερο έλεγχο των γειτόνων.
- Place attacker objects
- Ο επιτιθέμενος τώρα κάνει ξανά spray, δημιουργώντας πολλά αντικείμενα που ελέγχει στις νέες αυτές σελίδες.
- Αυτά τα αντικείμενα είναι προβλέψιμα σε μέγεθος και θέση (εφόσον ανήκουν στην ίδια ζώνη).
- Free a controlled object (make a gap)
- Ο επιτιθέμενος επιτηδευμένα απελευθερώνει ένα από τα δικά του αντικείμενα.
- Αυτό δημιουργεί μια «τρύπα» στο heap, την οποία ο allocator θα ξαναχρησιμοποιήσει αργότερα για την επόμενη allocation του ίδιου μεγέθους.
- Victim object lands in the hole
- Ο επιτιθέμενος προκαλεί τον kernel να κάνει allocation του αντικειμένου-θύματος (αυτό που θέλει να διαφθείρει).
- Δεδομένου ότι η τρύπα είναι το πρώτο διαθέσιμο slot στη freelist, το θύμα τοποθετείται ακριβώς εκεί που ο επιτιθέμενος είχε απελευθερώσει το αντικείμενό του.
- Overflow / UAF into victim
- Τώρα ο επιτιθέμενος έχει ελεγχόμενα αντικείμενα γύρω από το θύμα.
- Με overflow από ένα από τα δικά του αντικείμενα (ή επαναχρησιμοποίηση ενός freed), μπορεί αξιόπιστα να αντικαταστήσει τα πεδία μνήμης του θύματος με επιλεγμένες τιμές.
Γιατί δουλεύει:
- Προβλεψιμότητα του zone allocator: allocations του ίδιου μεγέθους πάντα προέρχονται από την ίδια ζώνη.
- Συμπεριφορά freelist: οι νέες allocations επαναχρησιμοποιούν πρώτα το πιο πρόσφατα freed chunk.
- Heap sprays: ο επιτιθέμενος γεμίζει τη μνήμη με προβλέψιμο περιεχόμενο και ελέγχει τη διάταξη.
- Τελικό αποτέλεσμα: ο επιτιθέμενος ελέγχει πού προσγειώνεται το αντικείμενο-θύμα και ποια δεδομένα κάθονται δίπλα του.
Modern Kernel Heap (iOS 15+/A12+ SoCs)
Η Apple έκανε hardening του allocator και έκανε το heap grooming πολύ πιο δύσκολο:
1. From Classic kalloc to kalloc_type
- Πριν: υπήρχε μία ενιαία ζώνη
kalloc.<size>για κάθε κλάση μεγέθους (16, 32, 64, … 1280, κ.λπ.). Οποιοδήποτε αντικείμενο αυτού του μεγέθους τοποθετούνταν εκεί → τα αντικείμενα του επιτιθέμενου μπορούσαν να βρεθούν δίπλα σε προνομιακά kernel αντικείμενα. - Τώρα:
- Τα kernel αντικείμενα γίνονται allocations από typed zones (
kalloc_type). - Κάθε τύπος αντικειμένου (π.χ.
ipc_port_t,task_t,OSString,OSData) έχει τη δική του αφιερωμένη ζώνη, ακόμα κι αν έχουν το ίδιο μέγεθος. - Η αντιστοίχιση μεταξύ τύπου αντικειμένου ↔ ζώνης παράγεται από το kalloc_type system κατά τη διάρκεια της compile time.
Ένας επιτιθέμενος δεν μπορεί πια να εγγυηθεί ότι ελεγχόμενα δεδομένα (OSData) θα καταλήξουν δίπλα σε ευαίσθητα kernel αντικείμενα (task_t) ίδιου μεγέθους.
2. Slabs and Per-CPU Caches
- Το heap χωρίζεται σε slabs (σελίδες μνήμης κομμένες σε chunks σταθερού μεγέθους για εκείνη τη ζώνη).
- Κάθε ζώνη έχει μία per-CPU cache για να μειώσει την contention.
- Διαδρομή allocation:
- Δοκίμασε την per-CPU cache.
- Αν είναι άδεια, τράβηξε από τη global freelist.
- Αν η freelist είναι άδεια, κάνε allocation νέου slab (μία ή περισσότερες σελίδες).
- Όφελος: αυτή η αποκέντρωση κάνει τα heap sprays λιγότερο ντετερμινιστικά, καθώς οι allocations μπορεί να ικανοποιηθούν από caches διαφορετικών CPUs.
3. Randomization inside zones
- Μέσα σε μια ζώνη, τα freed στοιχεία δεν επιστρέφονται σε απλό FIFO/LIFO order.
- Το σύγχρονο XNU χρησιμοποιεί encoded freelist pointers (safe-linking όπως στο Linux, εισήχθη ~iOS 14).
- Κάθε freelist pointer κωδικοποιείται με XOR με ένα per-zone secret cookie.
- Αυτό εμποδίζει τους επιτιθέμενους να πλαστογραφήσουν ένα ψεύτικο freelist pointer εάν αποκτήσουν write primitive.
- Κάποιες allocations είναι τυχαίες στην τοποθέτηση εντός ενός slab, οπότε το spraying δεν εγγυάται γειτνίαση.
4. Guarded Allocations
- Ορισμένα κρίσιμα kernel αντικείμενα (π.χ., credentials, task structures) allocated σε guarded zones.
- Αυτές οι ζώνες εισάγουν guard pages (μη mapped μνήμη) μεταξύ slabs ή χρησιμοποιούν redzones γύρω από αντικείμενα.
- Οποιοδήποτε overflow στην guard page προκαλεί fault → άμεσο panic αντί για σιωπηλή διαφθορά.
5. Page Protection Layer (PPL) and SPTM
- Ακόμα κι αν ελέγχεις ένα freed object, δεν μπορείς να τροποποιήσεις όλη τη μνήμη του kernel:
- Το PPL (Page Protection Layer) επιβάλλει ότι ορισμένες περιοχές (π.χ. δεδομένα code signing, entitlements) είναι read-only ακόμα και για τον ίδιο τον kernel.
- Σε A15/M2+ devices, αυτός ο ρόλος αντικαθίσταται/ενισχύεται από SPTM (Secure Page Table Monitor) + TXM (Trusted Execution Monitor).
- Αυτά τα hardware-enforced layers σημαίνουν ότι οι επιτιθέμενοι δεν μπορούν να ανέβουν από μια μεμονωμένη heap corruption σε αυθαίρετο patching κρίσιμων security δομών.
- (Προστέθηκε / Ενισχύθηκε): επίσης, PAC (Pointer Authentication Codes) χρησιμοποιείται στον kernel για να προστατεύει pointers (ειδικά function pointers, vtables) ώστε η πλαστογράφηση ή η διαφθορά τους να γίνεται πιο δύσκολη.
- (Προστέθηκε / Ενισχύθηκε): οι ζώνες μπορεί να επιβάλλουν zone_require / zone enforcement, δηλ. ότι ένα αντικείμενο που απελευθερώθηκε μπορεί μόνο να επιστραφεί μέσω της σωστής typed zone· μη έγκυρες cross-zone frees μπορεί να οδηγήσουν σε panic ή να απορριφθούν. (Η Apple υπαινίσσεται αυτό σε posts σχετικά με memory safety)
6. Large Allocations
- Όχι όλες οι allocations περνούν από το
kalloc_type. - Πολύ μεγάλες αιτήσεις (πάνω από ~16 KB) παρακάμπτουν τις typed zones και εξυπηρετούνται απευθείας από το kernel VM (kmem) μέσω page allocations.
- Αυτές είναι λιγότερο προβλέψιμες, αλλά επίσης λιγότερο εκμεταλλεύσιμες, καθώς δεν μοιράζονται slabs με άλλα αντικείμενα.
7. Allocation Patterns Attackers Target
Ακόμα και με αυτές τις προστασίες, οι επιτιθέμενοι ψάχνουν για:
- Reference count objects: αν μπορείς να αλλοιώσεις retain/release counters, μπορείς να προκαλέσεις use-after-free.
- Objects with function pointers (vtables): η διαφθορά ενός τέτοιου αντικειμένου μπορεί ακόμα να δώσει control flow.
- Shared memory objects (IOSurface, Mach ports): αυτά παραμένουν στόχοι γιατί γεφυρώνουν user ↔ kernel.
Αλλά — σε αντίθεση με πριν — δεν μπορείς απλά να κάνεις spray OSData και να περιμένεις να γειτνιάσει με task_t. Χρειάζεσαι bugs ειδικών τύπων ή info leaks για να πετύχεις.
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)
Σε πρόσφατες εκδόσεις των Apple OS (ειδικά iOS 17+), η Apple εισήγαγε έναν πιο ασφαλή userland allocator, το xzone malloc (XZM). Αυτό είναι το user-space ανάλογο του kernel kalloc_type, εφαρμόζοντας awareness τύπων, απομόνωση metadata, και μηχανισμούς memory tagging.
Goals & Design Principles
- Type segregation / type awareness: ομαδοποίηση allocations ανά τύπο ή χρήση (pointer vs data) για να αποτραπεί type confusion και cross-type reuse.
- Metadata isolation: διαχωρισμός των heap metadata (π.χ. free lists, size/state bits) από τα payloads των αντικειμένων ώστε out-of-bounds writes να είναι λιγότερο πιθανό να αλλοιώσουν metadata.
- Guard pages / redzones: εισαγωγή μη mapped σελίδων ή padding γύρω από allocations για να πιαστούν overflows.
- Memory tagging (EMTE / MIE): συνεργασία με το hardware tagging για να ανιχνεύονται use-after-free, out-of-bounds και μη έγκυρες προσβάσεις.
- Scalable performance: διατήρηση χαμηλού overhead, αποφυγή υπερβολικού fragmentation, και υποστήριξη πολλών allocations ανά δευτερόλεπτο με χαμηλή καθυστέρηση.
Architecture & Components
Παρακάτω είναι τα κύρια στοιχεία του xzone allocator:
Segment Groups & Zones
- Segment groups διαμερίζουν το address space ανά κατηγορίες χρήσης: π.χ.
data,pointer_xzones,data_large,pointer_large. - Κάθε segment group περιέχει segments (VM ranges) που φιλοξενούν allocations για εκείνη την κατηγορία.
- Σχετικά με κάθε segment υπάρχει ένας metadata slab (ξεχωριστό VM area) που αποθηκεύει metadata (π.χ. free/used bits, size classes) για εκείνο το segment. Αυτή η out-of-line (OOL) metadata διασφαλίζει ότι τα metadata δεν αναμειγνύονται με τα payloads των αντικειμένων, μειώνοντας τη διαφθορά από overflows.
- Τα segments χωρίζονται σε chunks (slices) που με τη σειρά τους υποδιαιρούνται σε blocks (μονάδες allocation). Ένα chunk συνδέεται με μια συγκεκριμένη size class και segment group (δηλ. όλα τα blocks σε ένα chunk μοιράζονται το ίδιο μέγεθος & κατηγορία).
- Για μικρές / μεσαίες allocations, θα χρησιμοποιεί fixed-size chunks; για μεγάλες/τεράστιες, μπορεί να κάνει ξεχωριστό mapping.
Chunks & Blocks
- Ένα chunk είναι μια περιοχή (συνήθως πολλές σελίδες) αφιερωμένη σε allocations μιας size class εντός μιας group.
- Μέσα σε ένα chunk, τα blocks είναι slots διαθέσιμα για allocations. Τα freed blocks παρακολουθούνται μέσω του metadata slab — π.χ. μέσω bitmaps ή free lists αποθηκευμένων out-of-line.
- Μεταξύ chunks (ή εσωτερικά), μπορεί να εισαχθούν guard slices / guard pages (π.χ. unmapped slices) για να πιαστούν out-of-bounds writes.
Type / Type ID
- Κάθε σημείο allocation (ή κλήση σε malloc, calloc, κλπ.) συνδέεται με ένα type identifier (ένα
malloc_type_id_t) που κωδικοποιεί τι είδους αντικείμενο γίνεται allocation. Αυτό το type ID περνάει στον allocator, ο οποίος το χρησιμοποιεί για να επιλέξει ποια ζώνη / segment θα εξυπηρετήσει την allocation. - Εξαιτίας αυτού, ακόμα κι αν δύο allocations έχουν το ίδιο μέγεθος, μπορεί να πάνε σε εντελώς διαφορετικές ζώνες αν οι τύποι τους διαφέρουν.
- Σε πρώιμες iOS 17 εκδόσεις, όχι όλα τα APIs (π.χ. CFAllocator) ήταν πλήρως type-aware; η Apple διόρθωσε κάποιες από αυτές τις αδυναμίες στο iOS 18.
Allocation & Freeing Workflow
Εδώ είναι μια υψηλού επιπέδου ροή για το πώς λειτουργούν allocation και deallocation στο xzone:
- malloc / calloc / realloc / typed alloc καλούνται με μέγεθος και type ID.
- Ο allocator χρησιμοποιεί το type ID για να επιλέξει το σωστό segment group / zone.
- Εντός εκείνης της zone/segment, ψάχνει ένα chunk που έχει δωρεάν blocks του αιτούμενου μεγέθους.
- Μπορεί να συμβουλευτεί local caches / per-thread pools ή free block lists από το metadata.
- Αν δεν υπάρχει διαθέσιμο block, μπορεί να κάνει allocation νέου chunk σε εκείνη τη ζώνη.
- Το metadata slab ενημερώνεται (το free bit καθαρίζεται, bookkeeping).
- Αν το memory tagging (EMTE) είναι ενεργό, το επιστρεφόμενο block παίρνει ένα tag και το metadata ενημερώνεται για την «ζωντανή» κατάστασή του.
- Όταν καλείται
free():
- Το block σημαίνεται ως freed στο metadata (μέσω OOL slab).
- Το block μπορεί να μπει σε free list ή να μπει σε pool για επαναχρησιμοποίηση.
- Προαιρετικά, τα περιεχόμενα του block μπορεί να καθαριστούν ή να τοξινωθούν για να μειωθούν data leaks ή εκμεταλλεύσεις use-after-free.
- Το hardware tag που σχετίζεται με το block μπορεί να ακυρωθεί ή να επανα-ετικετοποιηθεί.
- Αν ένα ολόκληρο chunk γίνει ελεύθερο (όλα τα blocks freed), ο allocator μπορεί να ανακτήσει εκείνο το chunk (unmap ή επιστροφή στο OS) υπό πίεση μνήμης.
Security Features & Hardening
Αυτές είναι οι άμυνες ενσωματωμένες στο σύγχρονο 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)
- Το MIE (Memory Integrity Enforcement) της Apple είναι το hardware + OS πλαίσιο που φέρνει το Enhanced Memory Tagging Extension (EMTE) σε always-on, synchronous mode σε μεγάλες επιφάνειες επίθεσης.
- Ο allocator xzone είναι θεμελιώδες κομμάτι του MIE στο user space: allocations μέσω xzone παίρνουν tags, και οι προσβάσεις ελέγχονται από το hardware.
- Στο MIE, ο allocator, η ανάθεση tags, η διαχείριση metadata, και η επιβολή εμπιστευτικότητας των tags είναι ενσωματωμένα για να διασφαλιστεί ότι τα σφάλματα μνήμης (π.χ. stale reads, OOB, UAF) ανιχνεύονται άμεσα και δεν εκμεταλλεύονται αργότερα.
- Αν θέλεις, μπορώ επίσης να δημιουργήσω ένα cheat-sheet ή διάγραμμα των εσωτερικών του xzone για το βιβλίο σου. Θες να το κάνω επόμενο;
- :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
Μάθετε & εξασκηθείτε στο AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Υποστηρίξτε το HackTricks
- Ελέγξτε τα σχέδια συνδρομής!
- Εγγραφείτε στην 💬 ομάδα Discord ή στην ομάδα telegram ή ακολουθήστε μας στο Twitter 🐦 @hacktricks_live.
- Μοιραστείτε κόλπα hacking υποβάλλοντας PRs στα HackTricks και HackTricks Cloud github repos.
HackTricks

