Translated ['', 'src/binary-exploitation/ios-exploiting/ios-physical-uaf

This commit is contained in:
Translator 2025-09-29 09:00:30 +00:00
parent 6e36ce60f6
commit 3f9be3ec20

View File

@ -1,4 +1,4 @@
# iOS Physical Use-After-Free via IOSurface
# iOS Physical Use After Free via IOSurface
{{#include ../../banners/hacktricks-training.md}}
@ -7,91 +7,91 @@
This is a summary from the post from [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) moreover further information about exploit using this technique can be found in [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
### Memory management in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
### Διαχείριση μνήμης στο XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
Ο **χώρος εικονικών διευθύνσεων μνήμης** για τις διεργασίες χρήστη στο iOS εκτείνεται από **0x0 έως 0x8000000000**. Ωστόσο, αυτές οι διευθύνσεις δεν αντιστοιχούν άμεσα στη φυσική μνήμη. Αντίθετα, ο **kernel** χρησιμοποιεί **πίνακες σελίδων (page tables)** για να μεταφράσει εικονικές διευθύνσεις σε πραγματικές **φυσικές διευθύνσεις**.
Ο χώρος διευθύνσεων της εικονικής μνήμης για τις διεργασίες χρήστη στο iOS εκτείνεται από **0x0 έως 0x8000000000**. Ωστόσο, αυτές οι διευθύνσεις δεν αντιστοιχούν άμεσα στη φυσική μνήμη. Αντίθετα, ο kernel χρησιμοποιεί **page tables** για να μεταφράσει τις εικονικές διευθύνσεις σε πραγματικές **physical addresses**.
#### Levels of Page Tables in iOS
#### Επίπεδα των Page Tables στο iOS
Οι πίνακες σελίδων οργανώνονται ιεραρχικά σε τρία επίπεδα:
Τα page tables οργανώνονται ιεραρχικά σε τρία επίπεδα:
1. **L1 Page Table (Level 1)**:
* Κάθε καταχώριση εδώ αντιπροσωπεύει ένα μεγάλο εύρος εικονικής μνήμης.
* Κάθε εγγραφή εδώ αντιπροσωπεύει ένα μεγάλο εύρος εικονικής μνήμης.
* Καλύπτει **0x1000000000 bytes****256 GB**) εικονικής μνήμης.
2. **L2 Page Table (Level 2)**:
* Μια καταχώριση εδώ αντιπροσωπεύει μια μικρότερη περιοχή εικονικής μνήμης, συγκεκριμένα **0x2000000 bytes** (32 MB).
* Μια καταχώριση L1 μπορεί να δείχνει σε έναν πίνακα L2 αν δεν μπορεί να αντιστοιχίσει ολόκληρη την περιοχή από μόνη της.
* Μια εγγραφή εδώ αντιπροσωπεύει μια μικρότερη περιοχή εικονικής μνήμης, συγκεκριμένα **0x2000000 bytes** (32 MB).
* Μια εγγραφή L1 μπορεί να δείχνει σε έναν πίνακα L2 αν δεν μπορεί να χαρτογραφήσει ολόκληρη την περιοχή από μόνη της.
3. **L3 Page Table (Level 3)**:
* Αυτό είναι το πιο λεπτομερές επίπεδο, όπου κάθε καταχώριση αντιστοιχίζει μία μόνο σελίδα μνήμης **4 KB**.
* Μια καταχώριση L2 μπορεί να δείχνει σε έναν πίνακα L3 αν απαιτείται πιο λεπτομερής έλεγχος.
* Αυτό είναι το πιο λεπτομερές επίπεδο, όπου κάθε εγγραφή χαρτογραφεί μια μοναδική σελίδα μνήμης **4 KB**.
* Μια εγγραφή L2 μπορεί να δείχνει σε έναν πίνακα L3 αν απαιτείται πιο λεπτομερής έλεγχος.
#### Mapping Virtual to Physical Memory
#### Αντιστοίχιση εικονικής σε φυσική μνήμη
* **Direct Mapping (Block Mapping)**:
* Ορισμένες καταχωρίσεις σε έναν πίνακα σελίδων αντιστοιχίζουν απευθείας ένα εύρος εικονικών διευθύνσεων σε ένα συνεχή εύρος φυσικών διευθύνσεων (σαν συντόμευση).
* Ορισμένες εγγραφές στον page table αντιστοιχούν άμεσα ένα εύρος εικονικών διευθύνσεων σε ένα συνεχή εύρος φυσικών διευθύνσεων (σαν συντόμευση).
* **Pointer to Child Page Table**:
* Αν απαιτείται πιο λεπτομερής έλεγχος, μια καταχώριση σε ένα επίπεδο (π.χ. L1) μπορεί να δείχνει σε έναν **child page table** στο επόμενο επίπεδο (π.χ. L2).
* Αν απαιτείται πιο λεπτομερής έλεγχος, μια εγγραφή σε ένα επίπεδο (π.χ. L1) μπορεί να δείχνει σε έναν child page table στο επόμενο επίπεδο (π.χ. L2).
#### Example: Mapping a Virtual Address
#### Παράδειγμα: Χαρτογράφηση μιας εικονικής διεύθυνσης
Ας υποθέσουμε ότι προσπαθείτε να προσπελάσετε την εικονική διεύθυνση **0x1000000000**:
1. **L1 Table**:
* Ο kernel ελέγχει την καταχώριση L1 που αντιστοιχεί σε αυτή την εικονική διεύθυνση. Αν έχει έναν **pointer to an L2 page table**, πηγαίνει σε αυτόν τον πίνακα L2.
* Ο kernel ελέγχει την εγγραφή L1 που αντιστοιχεί στην εν λόγω εικονική διεύθυνση. Αν έχει **pointer to an L2 page table**, πηγαίνει σε εκείνο τον πίνακα L2.
2. **L2 Table**:
* Ο kernel ελέγχει τον πίνακα L2 για μια πιο λεπτομερή αντιστοίχιση. Αν αυτή η καταχώριση δείχνει σε έναν **L3 page table**, προχωράει εκεί.
* Ο kernel ελέγχει τον πίνακα L2 για μια πιο λεπτομερή αντιστοίχιση. Αν αυτή η εγγραφή δείχνει σε έναν **L3 page table**, προχωράει εκεί.
3. **L3 Table**:
* Ο kernel αναζητά την τελική καταχώριση L3, η οποία δείχνει στη **φυσική διεύθυνση** της πραγματικής σελίδας μνήμης.
* Ο kernel αναζητά την τελική εγγραφή L3, που δείχνει στη **physical address** της πραγματικής σελίδας μνήμης.
#### Example of Address Mapping
#### Παράδειγμα Χαρτογράφησης Διευθύνσεων
Αν γράψετε τη φυσική διεύθυνση **0x800004000** στην πρώτη θέση του πίνακα L2, τότε:
Αν γράψετε τη φυσική διεύθυνση **0x800004000** στον πρώτο δείκτη του πίνακα L2, τότε:
* Οι εικονικές διευθύνσεις από **0x1000000000** έως **0x1002000000** αντιστοιχίζονται στις φυσικές διευθύνσεις από **0x800004000** έως **0x802004000**.
* Οι εικονικές διευθύνσεις από **0x1000000000** έως **0x1002000000** αντιστοιχούν στις φυσικές διευθύνσεις από **0x800004000** έως **0x802004000**.
* Αυτή είναι μια **block mapping** στο επίπεδο L2.
Εναλλακτικά, αν η καταχώριση L2 δείχνει σε έναν πίνακα L3:
Εναλλακτικά, αν η εγγραφή L2 δείχνει σε έναν πίνακα L3:
* Κάθε σελίδα 4 KB στο εικονικό εύρος **0x1000000000 -> 0x1002000000** θα αντιστοιχίζεται από μεμονωμένες καταχωρίσεις στον L3 πίνακα.
* Κάθε σελίδα 4 KB στην εικονική περιοχή **0x1000000000 -> 0x1002000000** θα χαρτογραφείται από μεμονωμένες εγγραφές στον πίνακα L3.
### Physical use-after-free
Μια **physical use-after-free** (UAF) συμβαίνει όταν:
1. Μια διεργασία **εκχωρεί** κάποια μνήμη ως **readable and writable**.
2. Οι **page tables** ενημερώνονται για να αντιστοιχίσουν αυτή τη μνήμη σε μια συγκεκριμένη φυσική διεύθυνση που η διεργασία μπορεί να προσπελάσει.
3. Η διεργασία **αποδεσμεύει** (frees) τη μνήμη.
4. Ωστόσο, λόγω ενός **bug**, ο kernel **ξεχνά να αφαιρέσει την αντιστοίχιση** από τους πίνακες σελίδων, παρόλο που σημειώνει τη σχετική φυσική μνήμη ως ελεύθερη.
5. Ο kernel μπορεί στη συνέχεια να **επανα-εκχωρήσει αυτή τη "απελευθερωμένη" φυσική μνήμη** για άλλες χρήσεις, όπως **kernel data**.
6. Εφόσον η αντιστοίχιση δεν αφαιρέθηκε, η διεργασία μπορεί ακόμα να **διαβάσει και να γράψει** σε αυτή τη φυσική μνήμη.
1. Μια διεργασία **κατανεμηθεί** κάποια μνήμη ως **readable and writable**.
2. Οι **page tables** ενημερώνονται για να χαρτογραφήσουν αυτή τη μνήμη σε μια συγκεκριμένη φυσική διεύθυνση που η διεργασία μπορεί να προσπελάσει.
3. Η διεργασία **αποδεσμεύει** (free) τη μνήμη.
4. Όμως, λόγω ενός **bug**, ο kernel **ξεχνά να αφαιρέσει την αντιστοίχιση** από τους page tables, παρόλο που σημειώνει ότι η αντίστοιχη φυσική μνήμη είναι ελεύθερη.
5. Ο kernel μπορεί στη συνέχεια να **επανακατανείμει αυτή τη "free" φυσική μνήμη** για άλλους σκοπούς, όπως δεδομένα του kernel.
6. Εφόσον η αντιστοίχιση δεν αφαιρέθηκε, η διεργασία μπορεί ακόμα να **διαβάζει και να γράφει** σε αυτή τη φυσική μνήμη.
Αυτό σημαίνει ότι η διεργασία μπορεί να προσπελάσει **σελίδες kernel μνήμης**, οι οποίες μπορεί να περιέχουν ευαίσθητα δεδομένα ή δομές, επιτρέποντας ενδεχομένως σε έναν επιτιθέμενο να **χειραγωγήσει μνήμη του kernel**.
Αυτό σημαίνει ότι η διεργασία μπορεί να προσπελάσει **σελίδες μνήμης του kernel**, οι οποίες μπορεί να περιέχουν ευαίσθητα δεδομένα ή δομές, επιτρέποντας πιθανώς σε έναν επιτιθέμενο να **χειριστεί μνήμη του kernel**.
### IOSurface Heap Spray
Δεδομένου ότι ο επιτιθέμενος δεν μπορεί να ελέγξει ποιες συγκεκριμένες σελίδες του kernel θα εκχωρηθούν στη freed μνήμη, χρησιμοποιεί μια τεχνική που ονομάζεται **heap spray**:
Εφόσον ο επιτιθέμενος δεν μπορεί να ελέγξει ποιες συγκεκριμένες σελίδες του kernel θα εκχωρηθούν στη freed μνήμη, χρησιμοποιεί μια τεχνική που ονομάζεται **heap spray**:
1. Ο επιτιθέμενος **δημιουργεί μεγάλο αριθμό αντικειμένων IOSurface** στη μνήμη του kernel.
2. Κάθε αντικείμενο IOSurface περιέχει μια **magic value** σε ένα από τα πεδία του, που το καθιστά εύκολα αναγνωρίσιμο.
3. Σαρώνονται οι **freed pages** για να δουν αν κάποιο από αυτά τα αντικείμενα IOSurface "προσγειώθηκε" σε μια freed σελίδα.
4. Όταν βρουν ένα αντικείμενο IOSurface σε μια freed σελίδα, μπορούν να το χρησιμοποιήσουν για να **διαβάσουν και να γράψουν μνήμη του kernel**.
2. Κάθε αντικείμενο IOSurface περιέχει μια **magic value** σε ένα από τα πεδία του, καθιστώντας το εύκολα αναγνωρίσιμο.
3. Σαρώνει τις freed σελίδες για να δει αν οποιοδήποτε από αυτά τα αντικείμενα IOSurface κατέληξε σε μια freed σελίδα.
4. Όταν βρει ένα αντικείμενο IOSurface σε freed σελίδα, μπορεί να το χρησιμοποιήσει για να **διαβάσει και να γράψει μνήμη του kernel**.
More info about this in [https://github.com/felix-pb/kfd/tree/main/writeups](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 σελίδες μπλοκάρονται.
> Secure Page Table Monitor (SPTM) επεκτείνει το PPL σκληραίνοντας τις ενημερώσεις των page tables. Διασφαλίζει ότι ακόμη και privileged kernel code δεν μπορεί να επαναχαρτογραφήσει απρόσκοπτα freed σελίδες ή να τροποποιήσει mappings χωρίς να περάσει από secure checks.
> KTRR (Kernel Text Read-Only Region) κλειδώνει την περιοχή του κώδικα του kernel ως read-only μετά το boot. Αυτό αποτρέπει οποιεσδήποτε runtime τροποποιήσεις στον kernel code, κλείνοντας ένα σημαντικό attack vector που συχνά εκμεταλλεύονται τα physical UAF exploits.
> Επιπλέον, οι εκχωρήσεις `IOSurface` είναι πλέον λιγότερο προβλέψιμες και πιο δύσκολες να αντιστοιχιστούν σε user-accessible περιοχές, γεγονός που καθιστά το κόλπο του “magic value scanning” πολύ λιγότερο αξιόπιστο. Και `IOSurface` τώρα προστατεύεται από entitlements και sandbox περιορισμούς.
> Λάβετε υπόψη ότι συσκευές iOS 16+ (A12+) φέρουν hardware mitigations (όπως PPL ή SPTM) που κάνουν τις τεχνικές physical UAF πολύ λιγότερο ρεαλιστικές.
> PPL επιβάλει αυστηρές προστασίες MMU σε σελίδες σχετικές με code signing, entitlements και ευαίσθητα δεδομένα του kernel, έτσι ακόμα και αν μια σελίδα επαναχρησιμοποιηθεί, εγγραφές από userland ή compromised kernel code σε σελίδες προστατευμένες από PPL αποκλείονται.
> Secure Page Table Monitor (SPTM) επεκτείνει το PPL ενισχύοντας τις ενημερώσεις των page tables οι ίδιες. Διασφαλίζει ότι ακόμη και ο προνομιούχος kernel code δεν μπορεί να επαναχαρτογραφήσει σιωπηλά freed σελίδες ή να παραποιήσει αντιστοιχίσεις χωρίς να περάσει από secure checks.
> KTRR (Kernel Text Read-Only Region), το οποίο κλειδώνει το τμήμα κώδικα του kernel ως read-only μετά το boot. Αυτό αποτρέπει οποιεσδήποτε runtime τροποποιήσεις στον κώδικα του kernel, κλείνοντας ένα μεγάλο attack vector που συχνά εκμεταλλεύονται τα physical UAF exploits.
> Επιπλέον, οι κατανομές `IOSurface` είναι λιγότερο προβλέψιμες και πιο δύσκολο να χαρτογραφηθούν σε user-accessible περιοχές, κάτι που κάνει το κόλπο του “magic value scanning” πολύ λιγότερο αξιόπιστο. Και το `IOSurface` τώρα προστατεύεται από entitlements και sandbox περιορισμούς.
### Step-by-Step Heap Spray Process
### Διαδικασία Heap Spray βήμα-προς-βήμα
1. **Spray IOSurface Objects**: Ο επιτιθέμενος δημιουργεί πολλά αντικείμενα IOSurface με έναν ειδικό αναγνωριστικό ("magic value").
2. **Scan Freed Pages**: Ελέγχουν αν κάποιο από τα αντικείμενα έχει εκχωρηθεί σε μια freed σελίδα.
3. **Read/Write Kernel Memory**: Με τη χειραγώγηση πεδίων στο αντικείμενο IOSurface, αποκτούν τη δυνατότητα για **arbitrary reads and writes** στη μνήμη του kernel. Αυτό τους επιτρέπει να:
* Χρησιμοποιήσουν ένα πεδίο για να **διαβάσουν οποιαδήποτε 32-bit τιμή** στη μνήμη του kernel.
* Χρησιμοποιήσουν ένα άλλο πεδίο για να **γράψουν 64-bit τιμές**, επιτυγχάνοντας ένα σταθερό **kernel read/write primitive**.
2. **Scan Freed Pages**: Ελέγχει αν κάποια από τα αντικείμενα έχουν εκχωρηθεί σε μια freed σελίδα.
3. **Read/Write Kernel Memory**: Με τη χειραγώγηση πεδίων στο αντικείμενο IOSurface, αποκτά τη δυνατότητα για **arbitrary reads and writes** στη μνήμη του kernel. Αυτό του επιτρέπει να:
* Χρησιμοποιήσει ένα πεδίο για να **διαβάσει οποιαδήποτε 32-bit τιμή** στη μνήμη του kernel.
* Χρησιμοποιήσει άλλο πεδίο για να **γράψει 64-bit τιμές**, επιτυγχάνοντας ένα σταθερό **kernel read/write primitive**.
Generate IOSurface objects with the magic value IOSURFACE\_MAGIC to later search for:
```c
@ -114,7 +114,7 @@ io_connect_t id = result.surface_id;
}
}
```
Αναζήτηση για **`IOSurface`** αντικείμενα σε μία ελευθερωμένη φυσική σελίδα:
Αναζήτηση για **`IOSurface`** αντικείμενα σε μία απελευθερωμένη φυσική σελίδα:
```c
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);
@ -150,22 +150,22 @@ return 0;
```
### Επίτευξη Kernel Read/Write με IOSurface
Μετά την απόκτηση ελέγχου πάνω σε ένα αντικείμενο IOSurface στη kernel memory (mapped σε μια freed physical page προσβάσιμη από userspace), μπορούμε να το χρησιμοποιήσουμε για **αυθαίρετες λειτουργίες ανάγνωσης και εγγραφής στον kernel**.
Μετά την απόκτηση ελέγχου ενός IOSurface object στη kernel μνήμη (mapped σε μια freed φυσική σελίδα προσβάσιμη από userspace), μπορούμε να το χρησιμοποιήσουμε για **arbitrary kernel read and write operations**.
**Key Fields in IOSurface**
Το αντικείμενο IOSurface έχει δύο κρίσιμα πεδία:
Το IOSurface αντικείμενο έχει δύο κρίσιμα πεδία:
1. **Use Count Pointer**: Επιτρέπει μια **ανάγνωση 32-bit**.
2. **Indexed Timestamp Pointer**: Επιτρέπει μια **εγγραφή 64-bit**.
1. **Use Count Pointer**: Επιτρέπει ένα **32-bit read**.
2. **Indexed Timestamp Pointer**: Επιτρέπει ένα **64-bit write**.
Επανεγγράφοντας αυτούς τους δείκτες, τους αναδρομολογούμε σε αυθαίρετες διευθύνσεις στη kernel memory, επιτρέποντας δυνατότητες ανάγνωσης/εγγραφής.
Με την επαναγραφή αυτών των pointers, τα ανακατευθύνουμε σε αυθαίρετες διευθύνσεις στη kernel μνήμη, ενεργοποιώντας read/write δυνατότητες.
#### 32-Bit Kernel Read
Για να εκτελέσουμε ανάγνωση:
Για να εκτελέσετε ανάγνωση:
1. Επανεγγράψτε τον **use count pointer** ώστε να δείχνει στη διεύθυνση-στόχο μείον ένα offset 0x14 bytes.
1. Επαναγράψτε τον **use count pointer** ώστε να δείχνει στη διεύθυνση-στόχο μείον ένα offset 0x14 bytes.
2. Χρησιμοποιήστε τη μέθοδο `get_use_count` για να διαβάσετε την τιμή σε εκείνη τη διεύθυνση.
```c
uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) {
@ -184,12 +184,12 @@ iosurface_set_use_count_pointer(info.object, orig);
return value;
}
```
#### 64-Bit Kernel Write
#### Εγγραφή 64-Bit στον Kernel
Για να πραγματοποιήσετε μια εγγραφή:
1. Αντικαταστήστε τον δείκτη **indexed timestamp pointer** ώστε να δείχνει στη διεύθυνση-στόχο.
2. Χρησιμοποιήστε τη μέθοδο `set_indexed_timestamp` για να γράψετε μια 64-bit τιμή.
1. Υπεργράψτε τον **indexed timestamp pointer** με τη διεύθυνση-στόχο.
2. Χρησιμοποιήστε τη μέθοδο `set_indexed_timestamp` για να γράψετε μια τιμή 64-bit.
```c
void set_indexed_timestamp(io_connect_t client, uint32_t surfaceID, uint64_t value) {
uint64_t args[3] = {surfaceID, 0, value};
@ -206,10 +206,10 @@ iosurface_set_indexed_timestamp_pointer(info.object, orig);
#### Exploit Flow Recap
1. **Trigger Physical Use-After-Free**: Οι σελίδες που έχουν απελευθερωθεί είναι διαθέσιμες για επαναχρησιμοποίηση.
2. **Spray IOSurface Objects**: Κατανεμήστε πολλά αντικείμενα IOSurface με μια μοναδική "magic value" στη μνήμη του kernel.
3. **Identify Accessible IOSurface**: Εντοπίστε ένα IOSurface σε μια σελίδα που έχει απελευθερωθεί και την οποία ελέγχετε.
4. **Abuse Use-After-Free**: Τροποποιήστε δείκτες στο αντικείμενο IOSurface για να ενεργοποιήσετε αυθαίρετο **kernel read/write** μέσω των μεθόδων IOSurface.
2. **Spray IOSurface Objects**: Δημιουργήστε πολλά IOSurface objects με ένα μοναδικό "magic value" στο kernel memory.
3. **Identify Accessible IOSurface**: Εντοπίστε ένα IOSurface σε σελίδα που έχει απελευθερωθεί και που ελέγχετε.
4. **Abuse Use-After-Free**: Τροποποιήστε δείκτες στο αντικείμενο IOSurface για να επιτρέψετε αυθαίρετο **kernel read/write** μέσω μεθόδων IOSurface.
Με αυτά τα primitives, το exploit παρέχει ελεγχόμενα **32-bit reads** και **64-bit writes** στη μνήμη του kernel. Επιπλέον βήματα για jailbreak μπορεί να περιλαμβάνουν πιο σταθερά read/write primitives, τα οποία ενδέχεται να απαιτούν παράκαμψη πρόσθετων προστασιών (π.χ. PPL σε νεότερες συσκευές arm64e).
Με αυτά τα primitives, το exploit παρέχει ελεγχόμενα **32-bit reads** και **64-bit writes** σε kernel memory. Περαιτέρω βήματα για jailbreak ενδέχεται να περιλαμβάνουν πιο σταθερά read/write primitives, που μπορεί να απαιτήσουν παράκαμψη επιπλέον προστασιών (π.χ. PPL σε νεότερες συσκευές arm64e).
{{#include ../../banners/hacktricks-training.md}}