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

This commit is contained in:
Translator 2025-09-29 08:59:42 +00:00
parent ebb7a1b19e
commit b3abdaa2b7

View File

@ -1,99 +1,99 @@
# iOS Physical Use-After-Free via IOSurface
# iOS Physische use-after-free via IOSurface
{{#include ../../banners/hacktricks-training.md}}
## Physical use-after-free
## Physischer use-after-free
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)
Dies ist eine Zusammenfassung des Beitrags von [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html). Weitere Informationen zu Exploits, die diese Technik verwenden, finden sich 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>
### Speicherverwaltung in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
Der **virtuelle Adressraum** für User-Prozesse auf iOS reicht von **0x0 bis 0x8000000000**. Diese Adressen zeigen jedoch nicht direkt auf den physischen Speicher. Stattdessen übersetzt der **kernel** virtuelle Adressen über **page tables** in tatsächliche **physische Adressen**.
Der **virtuelle Adressraum** für Benutzerprozesse unter iOS reicht von **0x0 bis 0x8000000000**. Diese Adressen werden jedoch nicht direkt auf physischen Speicher abgebildet. Stattdessen verwendet der **Kernel** **Seitentabellen**, um virtuelle Adressen in tatsächliche **physische Adressen** zu übersetzen.
#### Levels of Page Tables in iOS
#### Ebenen der Seitentabellen in iOS
Page tables sind hierarchisch in drei Ebenen organisiert:
Seitentabellen sind hierarchisch in drei Ebenen organisiert:
1. **L1 Page Table (Level 1)**:
* Jeder Eintrag repräsentiert einen großen Bereich des virtuellen Speichers.
* Deckt **0x1000000000 bytes** (oder **256 GB**) virtuellen Speicher ab.
* Jeder Eintrag repräsentiert hier einen großen Bereich des virtuellen Speichers.
* Er deckt **0x1000000000 Bytes** (oder **256 GB**) virtuellen Speicher ab.
2. **L2 Page Table (Level 2)**:
* Ein Eintrag repräsentiert einen kleineren Bereich von **0x2000000 bytes** (32 MB).
* Ein L1-Eintrag kann auf eine L2-Tabelle zeigen, wenn er den gesamten Bereich nicht direkt mappt.
* Ein Eintrag repräsentiert hier einen kleineren Bereich des virtuellen Speichers, nämlich **0x2000000 Bytes** (32 MB).
* Ein L1-Eintrag kann auf eine L2-Tabelle zeigen, wenn er den gesamten Bereich nicht selbst abbilden kann.
3. **L3 Page Table (Level 3)**:
* Die feinste Ebene, bei der jeder Eintrag eine einzelne **4 KB**-Speicherseite abbildet.
* Ein L2-Eintrag kann auf eine L3-Tabelle zeigen, wenn detailliertere Kontrolle nötig ist.
* Dies ist die feinste Ebene, in der jeder Eintrag eine einzelne **4 KB** Speicherseite abbildet.
* Ein L2-Eintrag kann auf eine L3-Tabelle zeigen, wenn eine feinere Auflösung erforderlich ist.
#### Mapping Virtual to Physical Memory
#### Abbildung von virtuellem zu physischem Speicher
* **Direct Mapping (Block Mapping)**:
* Manche Einträge in einer page table mappen direkt einen Bereich virtueller Adressen auf einen zusammenhängenden Bereich physischer Adressen (wie eine Abkürzung).
* **Pointer to Child Page Table**:
* Wenn feinere Kontrolle benötigt wird, kann ein Eintrag auf einer Ebene (z. B. L1) auf eine **child page table** der nächsten Ebene (z. B. L2) zeigen.
* **Direkte Abbildung (Block Mapping)**:
* Einige Einträge in einer Seitentabelle **bilden direkt einen Bereich virtueller Adressen** auf einen zusammenhängenden Bereich physischer Adressen ab (wie eine Abkürzung).
* **Zeiger auf untergeordnete Seitentabelle**:
* Wenn eine feinere Steuerung notwendig ist, kann ein Eintrag einer Ebene (z. B. L1) auf eine **untergeordnete Seitentabelle** der nächsten Ebene (z. B. L2) zeigen.
#### Example: Mapping a Virtual Address
#### Beispiel: Abbildung einer virtuellen Adresse
Angenommen, du versuchst auf die virtuelle Adresse **0x1000000000** zuzugreifen:
Angenommen, Sie greifen auf die virtuelle Adresse **0x1000000000** zu:
1. **L1 Table**:
* Der kernel überprüft den L1-Page-Table-Eintrag, der dieser virtuellen Adresse entspricht. Zeigt er auf eine **L2 page table**, geht er zu dieser L2-Tabelle.
* Der Kernel prüft den L1-Seitentabelleneintrag, der dieser virtuellen Adresse entspricht. Wenn dieser einen **Zeiger auf eine L2-Seitentabelle** enthält, geht er zu dieser L2-Tabelle.
2. **L2 Table**:
* Der kernel prüft die L2-Page-Table für eine detailliertere Zuordnung. Zeigt dieser Eintrag auf eine **L3 page table**, geht es weiter dorthin.
* Der Kernel prüft die L2-Seitentabelle für eine detailliertere Abbildung. Wenn dieser Eintrag auf eine **L3-Seitentabelle** zeigt, geht er dorthin.
3. **L3 Table**:
* Der kernel liest den finalen L3-Eintrag aus, der auf die **physische Adresse** der tatsächlichen Speicherseite zeigt.
* Der Kernel sucht den finalen L3-Eintrag, der auf die **physische Adresse** der tatsächlichen Speicherseite zeigt.
#### Example of Address Mapping
#### Beispiel einer Adressabbildung
Wenn du die physische Adresse **0x800004000** in den ersten Index der L2-Tabelle schreibst, dann:
Wenn Sie die physische Adresse **0x800004000** in den ersten Index der L2-Tabelle schreiben, dann:
* Virtuelle Adressen von **0x1000000000** bis **0x1002000000** werden auf physische Adressen von **0x800004000** bis **0x802004000** abgebildet.
* Das ist ein **Block Mapping** auf L2-Ebene.
* Das ist eine **Block-Abbildung** auf L2-Ebene.
Alternativ, wenn der L2-Eintrag auf eine L3-Tabelle zeigt:
* Jede 4 KB-Seite im virtuellen Adressbereich **0x1000000000 -> 0x1002000000** würde durch einzelne Einträge in der L3-Tabelle gemappt werden.
* Jede 4 KB-Seite im virtuellen Adressbereich **0x1000000000 -> 0x1002000000** würde durch einzelne Einträge in der L3-Tabelle abgebildet werden.
### Physical use-after-free
### Physischer use-after-free
Ein **physical use-after-free** (UAF) tritt auf, wenn:
Ein **physischer use-after-free** (UAF) tritt auf, wenn:
1. Ein Prozess Speicher als **readable and writable** allokiert.
2. Die **page tables** aktualisiert werden, sodass dieser Speicher auf eine bestimmte physische Adresse gemappt wird, auf die der Prozess zugreifen kann.
3. Der Prozess den Speicher **dealloziert** (freigibt).
4. Aufgrund eines **Bugs** vergisst der kernel jedoch, die Zuordnung aus den page tables zu entfernen, obwohl der entsprechende physische Speicher als frei markiert wird.
5. Der kernel kann diesen "freigegebenen" physischen Speicher dann für andere Zwecke neu allozieren, z. B. für **kernel data**.
6. Da die Zuordnung nicht entfernt wurde, kann der Prozess weiterhin auf diesen physischen Speicher **lesen und schreiben**.
1. Ein Prozess etwas Speicher als **lesbar und beschreibbar** alloziert.
2. Die **Seitentabellen** werden aktualisiert, um diesen Speicher auf eine bestimmte physische Adresse abzubilden, auf die der Prozess zugreifen kann.
3. Der Prozess **deallokiert** (freigibt) den Speicher.
4. Aufgrund eines **Fehlers** vergisst der Kernel jedoch, die Abbildung aus den Seitentabellen zu entfernen, obwohl er den entsprechenden physischen Speicher als frei markiert.
5. Der Kernel kann diesen "freigegebenen" physischen Speicher dann für andere Zwecke neu verwenden, z. B. für **Kernel-Daten**.
6. Da die Abbildung nicht entfernt wurde, kann der Prozess weiterhin **auf diesen physischen Speicher lesen und schreiben**.
Das bedeutet, der Prozess kann auf **Seiten von kernel-Speicher** zugreifen, die sensible Daten oder Strukturen enthalten können, was einem Angreifer ermöglichen könnte, **kernel memory** zu manipulieren.
Das bedeutet, dass der Prozess auf **Seiten des Kernel-Speichers** zugreifen kann, die sensible Daten oder Strukturen enthalten könnten, und einem Angreifer möglicherweise erlauben, **Kernel-Speicher zu manipulieren**.
### IOSurface Heap Spray
Da der Angreifer nicht kontrollieren kann, welche spezifischen kernel-Seiten einer freigegebenen Seite zugewiesen werden, verwendet er die Technik **heap spray**:
Da der Angreifer nicht kontrollieren kann, welche spezifischen Kernel-Seiten für den freigegebenen Speicher verwendet werden, nutzt er eine Technik namens **Heap Spray**:
1. Der Angreifer **erstellt eine große Anzahl von IOSurface-Objekten** im kernel-Speicher.
2. Jedes IOSurface-Objekt enthält einen **magic value** in einem seiner Felder, der das Identifizieren erleichtert.
3. Sie **scannen die freigegebenen Seiten**, um zu sehen, ob eines dieser IOSurface-Objekte auf einer freigegebenen Seite gelandet ist.
4. Wenn sie ein IOSurface-Objekt auf einer freigegebenen Seite finden, können sie es verwenden, um **kernel memory** zu lesen und zu schreiben.
1. Der Angreifer **erstellt eine große Anzahl von IOSurface-Objekten** im Kernel-Speicher.
2. Jedes IOSurface-Objekt enthält einen **magischen Wert** in einem seiner Felder, wodurch es leicht zu identifizieren ist.
3. Sie **scannen die freigegebenen Seiten**, um zu sehen, ob sich eines dieser IOSurface-Objekte auf einer freigegebenen Seite niedergelassen hat.
4. Wenn sie ein IOSurface-Objekt auf einer freigegebenen Seite finden, können sie es nutzen, um **Kernel-Speicher zu lesen und zu schreiben**.
Mehr Infos dazu in [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
> [!TIP]
> Beachte, dass iOS 16+ (A12+) Geräte Hardware-Mitigationen (wie PPL oder SPTM) bringen, die physical UAF-Techniken deutlich weniger praktikabel machen.
> PPL erzwingt strikte MMU-Schutzmechanismen auf Seiten, die mit Code-Signing, Entitlements und sensiblen kernel-Daten zusammenhängen. Selbst wenn eine Seite wiederverwendet wird, werden Writes aus userland oder kompromittiertem kernel-Code auf PPL-geschützte Seiten blockiert.
> Secure Page Table Monitor (SPTM) erweitert PPL, indem es page table-Updates selbst härter absichert. Es stellt sicher, dass selbst privilegierter kernel-Code nicht stillschweigend freed pages remappen oder Mappings ohne sichere Checks manipulieren kann.
> KTRR (Kernel Text Read-Only Region) sperrt den Kernel-Code-Bereich nach dem Boot als read-only. Das verhindert Laufzeit-Modifikationen am Kernel-Code und schließt einen großen Angriffsvektor, auf den physical UAF-Exploits oft angewiesen sind.
> Außerdem sind IOSurface-Allocations weniger vorhersehbar und schwerer in benutzerzugängliche Regionen zu mappen, wodurch der „Magic-Value-Scanning“-Trick deutlich unzuverlässiger wird. Und IOSurface ist jetzt durch Entitlements und Sandbox-Restriktionen geschützt.
> Beachte, dass iOS 16+ (A12+) Geräte Hardware-Mitigationen (wie PPL oder SPTM) mitbringen, die physische UAF-Techniken deutlich weniger praktikabel machen.
> PPL erzwingt strikte MMU-Schutzmaßnahmen für Seiten, die mit code signing, entitlements und sensiblen Kernel-Daten zusammenhängen. Somit werden Schreibzugriffe aus userland oder von kompromittiertem Kernel-Code auf PPL-geschützte Seiten blockiert.
> Secure Page Table Monitor (SPTM) erweitert PPL, indem es die Seitentabellenupdates selbst härtert. Es stellt sicher, dass selbst privilegierter Kernel-Code nicht stillschweigend freigegebene Seiten remappen oder Abbildungen manipulieren kann, ohne sichere Prüfungen zu durchlaufen.
> KTRR (Kernel Text Read-Only Region) sperrt den Code-Bereich des Kernels nach dem Boot als read-only. Das verhindert Laufzeitänderungen am Kernel-Code und schließt damit einen großen Angriffsvektor, auf den physische UAF-Exploits oft angewiesen sind.
> Außerdem sind `IOSurface`-Allokationen weniger vorhersehbar und schwieriger in benutzerzugängliche Regionen zu mappen, was den „magischer Wert“-Scan wesentlich unzuverlässiger macht. Und `IOSurface` ist jetzt durch entitlements und sandbox-Beschränkungen geschützt.
### Step-by-Step Heap Spray Process
### Schritt-für-Schritt Heap Spray-Prozess
1. **Spray IOSurface Objects**: Der Angreifer erzeugt viele IOSurface-Objekte mit einem speziellen Identifier ("magic value").
2. **Scan Freed Pages**: Sie prüfen, ob eines der Objekte auf einer freigegebenen Seite zugewiesen wurde.
3. **Read/Write Kernel Memory**: Durch Manipulation von Feldern im IOSurface-Objekt erlangen sie die Fähigkeit zu **arbitrary reads and writes** im kernel-Speicher. Das erlaubt ihnen:
* Ein Feld zu verwenden, um **jeden 32-bit Wert** im kernel-Speicher zu lesen.
* Ein anderes Feld zu verwenden, um **64-bit Werte zu schreiben**, wodurch eine stabile **kernel read/write primitive** entsteht.
1. **Spray IOSurface Objects**: Der Angreifer erstellt viele IOSurface-Objekte mit einem speziellen Bezeichner ("magischer Wert").
2. **Scan Freed Pages**: Sie prüfen, ob eines der Objekte auf einer freigegebenen Seite alloziert wurde.
3. **Read/Write Kernel Memory**: Durch Manipulation von Feldern im IOSurface-Objekt erlangen sie die Fähigkeit zu **beliebigen Reads und Writes** im Kernel-Speicher. Das erlaubt ihnen:
* Ein Feld zu verwenden, um **jeden 32-Bit-Wert** im Kernel-Speicher zu lesen.
* Ein anderes Feld zu verwenden, um **64-Bit-Werte zu schreiben**, wodurch ein stabiler **Kernel read/write primitive** entsteht.
Generate IOSurface objects with the magic value IOSURFACE\_MAGIC to later search for:
Erzeuge IOSurface-Objekte mit dem magischen Wert IOSURFACE\_MAGIC, um später danach zu suchen:
```c
void spray_iosurface(io_connect_t client, int nSurfaces, io_connect_t **clients, int *nClients) {
if (*nClients >= 0x4000) return;
@ -148,25 +148,25 @@ free(surfaceIDs);
return 0;
}
```
### Erreichen von Kernel-Lese-/Schreibzugriff mit IOSurface
### Kernel-Lese-/Schreibzugriff mit IOSurface erreichen
Nachdem Kontrolle über ein IOSurface-Objekt im Kernel-Speicher erreicht wurde (auf eine freigegebene physische Seite abgebildet, die aus dem userspace zugänglich ist), können wir es für **beliebige Kernel-Lese- und Schreiboperationen** verwenden.
Nachdem Kontrolle über ein IOSurface-Objekt im Kernel-Speicher erreicht wurde (auf eine freigegebene physische Seite abgebildet, die vom userspace zugänglich ist), können wir es für **beliebige Kernel-Lese- und Schreiboperationen** verwenden.
**Wichtige Felder in IOSurface**
Das IOSurface-Objekt hat zwei entscheidende Felder:
Die IOSurface hat zwei entscheidende Felder:
1. **Use Count Pointer**: Ermöglicht ein **32-Bit read**.
2. **Indexed Timestamp Pointer**: Ermöglicht ein **64-Bit write**.
1. **Use Count Pointer**: Ermöglicht ein **32-bit read**.
2. **Indexed Timestamp Pointer**: Ermöglicht ein **64-bit write**.
Indem wir diese Zeiger überschreiben, leiten wir sie auf beliebige Adressen im Kernel-Speicher um und ermöglichen Lese-/Schreibzugriff.
Durch Überschreiben dieser Pointer leiten wir sie auf beliebige Adressen im Kernel-Speicher um und ermöglichen so Lese-/Schreibzugriff.
#### 32-Bit Kernel-Lesezugriff
Um einen Lesevorgang durchzuführen:
Um einen Lesezugriff durchzuführen:
1. Überschreibe den **use count pointer**, sodass er auf die Zieladresse minus einem 0x14-Byte-Offset zeigt.
2. Verwende die Methode `get_use_count`, um den Wert an dieser Adresse zu lesen.
1. Überschreibe den **use count pointer**, sodass er auf die Zieladresse minus einem 0x14-Byte-Offset zeigt.
2. Benutze die Methode `get_use_count`, um den Wert an dieser Adresse zu lesen.
```c
uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) {
uint64_t args[1] = {surfaceID};
@ -184,12 +184,12 @@ iosurface_set_use_count_pointer(info.object, orig);
return value;
}
```
#### 64-Bit Kernel-Schreiben
#### 64-Bit Kernel Write
Um einen Schreibvorgang auszuführen:
Um einen Schreibvorgang durchzuführen:
1. Überschreibe den **indexierten Zeitstempelzeiger** mit der Zieladresse.
2. Verwende die `set_indexed_timestamp`-Methode, um einen 64-Bit-Wert zu schreiben.
1. Überschreibe den **indexed timestamp pointer** auf die Zieladresse.
2. Verwende die Methode `set_indexed_timestamp`, um einen 64-Bit-Wert zu schreiben.
```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);
#### Zusammenfassung des Exploit-Ablaufs
1. **Trigger Physical Use-After-Free**: Freie Seiten stehen zur Wiederverwendung zur Verfügung.
2. **Spray IOSurface Objects**: Viele IOSurface-Objekte mit einem eindeutigen "magic value" im kernel memory allozieren.
3. **Identify Accessible IOSurface**: Eine IOSurface auf einer freigegebenen Seite finden, die Sie kontrollieren.
4. **Abuse Use-After-Free**: Pointer im IOSurface-Objekt manipulieren, um über IOSurface methods beliebiges **kernel read/write** zu ermöglichen.
2. **Spray IOSurface Objects**: Allokiere viele IOSurface-Objekte mit einem eindeutigen "magic value" im Kernel-Speicher.
3. **Identify Accessible IOSurface**: Finde ein IOSurface auf einer freigegebenen Seite, die du kontrollierst.
4. **Abuse Use-After-Free**: Ändere Pointer im IOSurface-Objekt, um über IOSurface-Methoden beliebige **kernel read/write** zu ermöglichen.
Mit diesen Primitiven stellt der Exploit kontrollierte **32-bit reads** und **64-bit writes** auf kernel memory bereit. Weitere Jailbreak-Schritte könnten stabilere read/write-Primitiven erfordern, die zusätzliche Schutzmechanismen umgehen müssen (z. B. PPL auf neueren arm64e-Geräten).
Mit diesen Primitiven ermöglicht der Exploit kontrollierte **32-bit reads** und **64-bit writes** auf den Kernel-Speicher. Weitere jailbreak-Schritte könnten stabilere read/write primitives erfordern, die das Umgehen zusätzlicher Schutzmechanismen nötig machen (z. B. PPL auf neueren arm64e-Geräten).
{{#include ../../banners/hacktricks-training.md}}