Translated ['', 'src/binary-exploitation/ios-exploiting/ios-physical-uaf

This commit is contained in:
Translator 2025-09-29 08:59:30 +00:00
parent baaa93b004
commit dccd9d8649

View File

@ -1,99 +1,99 @@
# iOS Physical Use-After-Free via IOSurface
# iOS Physical Use After Free via IOSurface
{{#include ../../banners/hacktricks-training.md}}
## Physical use-after-free
This is a summary from the post from [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](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](https://github.com/felix-pb/kfd)
Questo è un riassunto del post [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html); inoltre ulteriori informazioni su exploit che usano questa tecnica si trovano in [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
### Memory management in XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
Lo spazio di indirizzamento della memoria virtuale per i processi utente su iOS va da **0x0 a 0x8000000000**. Tuttavia, questi indirizzi non corrispondono direttamente alla memoria fisica. Il **kernel** utilizza invece le **page table** per tradurre gli indirizzi virtuali negli effettivi **indirizzi fisici**.
Lo spazio di indirizzamento della virtual memory per i processi utente su iOS va da **0x0 a 0x8000000000**. Tuttavia, questi indirizzi non mappano direttamente sulla memoria fisica. Invece, il kernel usa i page tables per tradurre gli virtual addresses in effettivi physical addresses.
#### Levels of Page Tables in iOS
Le page table sono organizzate gerarchicamente in tre livelli:
I page tables sono organizzati gerarchicamente in tre livelli:
1. **L1 Page Table (Level 1)**:
* Ogni entry qui rappresenta un ampio intervallo di memoria virtuale.
* Copre **0x1000000000 bytes** (o **256 GB**) di memoria virtuale.
* Ogni entry qui rappresenta un ampio intervallo di virtual memory.
* Copre **0x1000000000 bytes** (o **256 GB**) di virtual memory.
2. **L2 Page Table (Level 2)**:
* Un'entry qui rappresenta una regione più piccola di memoria virtuale, specificamente **0x2000000 bytes** (32 MB).
* Un'entry L1 può puntare a una page table L2 se non riesce a mappare l'intera regione da sola.
* Unentry qui rappresenta una regione più piccola di virtual memory, specificamente **0x2000000 bytes** (32 MB).
* Un entry L1 può puntare a una tabella L2 se non può mappare lintera regione da sola.
3. **L3 Page Table (Level 3)**:
* Questo è il livello più fine, dove ogni entry mappa una singola pagina di memoria di **4 KB**.
* Un'entry L2 può puntare a una page table L3 se è necessaria una granularità maggiore.
* Questo è il livello più fine, dove ogni entry mappa una singola pagina di memoria da **4 KB**.
* Un entry L2 può puntare a una tabella L3 se è necessario un controllo più granulare.
#### Mapping Virtual to Physical Memory
* **Direct Mapping (Block Mapping)**:
* Alcune entry in una page table mappano direttamente un intervallo di indirizzi virtuali a un intervallo contiguo di indirizzi fisici (come una scorciatoia).
* Alcune entry in un page table mappano direttamente un intervallo di virtual addresses a un intervallo contiguo di physical addresses (come una scorciatoia).
* **Pointer to Child Page Table**:
* Se è necessario un controllo più fine, un'entry a un livello (es. L1) può puntare a una **child page table** al livello successivo (es. L2).
* Se serve un controllo più fine, un entry in un livello (es. L1) può puntare a un child page table al livello successivo (es. L2).
#### Example: Mapping a Virtual Address
Prendiamo l'esempio di accesso all'indirizzo virtuale **0x1000000000**:
Supponiamo di accedere allindirizzo virtuale **0x1000000000**:
1. **L1 Table**:
* Il kernel controlla l'entry della L1 page table corrispondente a questo indirizzo virtuale. Se contiene un **pointer to an L2 page table**, passa a quella L2.
* Il kernel controlla lentry della L1 page table corrispondente a questo virtual address. Se contiene un pointer a una L2 page table, passa a quella L2.
2. **L2 Table**:
* Il kernel controlla la L2 page table per una mappatura più dettagliata. Se questa entry punta a una **L3 page table**, procede lì.
* Il kernel controlla la L2 page table per una mappatura più dettagliata. Se questa entry punta a una L3 page table, procede lì.
3. **L3 Table**:
* Il kernel consulta la entry finale L3, che punta all'**indirizzo fisico** della pagina di memoria reale.
* Il kernel consulta lentry finale L3, che punta al physical address della pagina di memoria effettiva.
#### Example of Address Mapping
Se scrivi l'indirizzo fisico **0x800004000** nel primo indice della L2 table, allora:
Se scrivi il physical address **0x800004000** nel primo indice della L2 table, allora:
* Gli indirizzi virtuali da **0x1000000000** a **0x1002000000** vengono mappati agli indirizzi fisici da **0x800004000** a **0x802004000**.
* Questo è un **block mapping** a livello L2.
* Gli virtual addresses da **0x1000000000** a **0x1002000000** sono mappati ai physical addresses da **0x800004000** a **0x802004000**.
* Questa è una block mapping al livello L2.
In alternativa, se l'entry L2 punta a una L3 table:
In alternativa, se lentry L2 punta a una L3 table:
* Ogni pagina da 4 KB nell'intervallo virtuale **0x1000000000 -> 0x1002000000** sarebbe mappata da singole entry nella L3 table.
* Ogni pagina da 4 KB nellintervallo virtuale **0x1000000000 -> 0x1002000000** sarebbe mappata da entry individuali nella L3 table.
### Physical use-after-free
Un **physical use-after-free** (UAF) si verifica quando:
Un **physical use-after-free** (UAF) avviene quando:
1. Un processo **alloca** della memoria come **readable and writable**.
2. Le **page table** vengono aggiornate per mappare quella memoria a uno specifico indirizzo fisico accessibile al processo.
3. Il processo **dealloca** (libera) la memoria.
4. Tuttavia, a causa di un **bug**, il kernel **dimentica di rimuovere la mappatura** dalle page table, anche se segna la corrispondente memoria fisica come libera.
1. Un processo **alloca** della memoria come **readable e writable**.
2. I page tables vengono aggiornati per mappare questa memoria a un specifico physical address che il processo può accedere.
3. Il processo **dealloca** (free) la memoria.
4. Tuttavia, a causa di un **bug**, il kernel **dimentica di rimuovere la mapping** dai page tables, anche se marca la corrispondente memoria fisica come libera.
5. Il kernel può quindi **riallocare questa memoria fisica "liberata"** per altri scopi, come dati del kernel.
6. Poiché la mappatura non è stata rimossa, il processo può ancora **leggere e scrivere** su quella memoria fisica.
6. Poiché la mapping non è stata rimossa, il processo può ancora **leggere e scrivere** su quella memoria fisica.
Questo significa che il processo può accedere a **pagine di memoria del kernel**, che potrebbero contenere dati o strutture sensibili, permettendo potenzialmente a un attaccante di **manipolare la memoria del kernel**.
Questo significa che il processo può accedere a **pagine di kernel memory**, che potrebbero contenere dati sensibili o strutture, permettendo potenzialmente a un attaccante di **manipolare la kernel memory**.
### IOSurface Heap Spray
Poiché l'attaccante non può controllare quali pagine del kernel saranno assegnate alla memoria liberata, usa una tecnica chiamata **heap spray**:
Poiché lattaccante non può controllare quali specifiche kernel pages verranno allocate sulla memoria liberata, usa una tecnica chiamata **heap spray**:
1. L'attaccante **crea un gran numero di oggetti IOSurface** nella memoria del kernel.
2. Ogni oggetto IOSurface contiene un **magic value** in uno dei suoi campi, rendendolo facile da identificare.
3. Scansionano le pagine liberate per vedere se uno di questi oggetti IOSurface è stato allocato su una pagina liberata.
4. Quando trovano un oggetto IOSurface su una pagina liberata, possono usarlo per **leggere e scrivere la memoria del kernel**.
1. Lattaccante **crea un gran numero di IOSurface objects** nella kernel memory.
2. Ogni IOSurface object contiene un **valore magico** in uno dei suoi campi, rendendolo facile da identificare.
3. Scansionano le pagine liberate per vedere se uno di questi IOSurface objects è stato allocato su una pagina liberata.
4. Quando trovano un IOSurface object su una pagina liberata, possono usarlo per **leggere e scrivere kernel memory**.
More info about this in [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
Ulteriori informazioni su questo in [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
> [!TIP]
> Sii consapevole che i dispositivi iOS 16+ (A12+) introducono mitigazioni hardware (come PPL o SPTM) che rendono le tecniche di physical UAF molto meno praticabili.
> PPL impone protezioni MMU rigide su pagine relative a code signing, entitlements e dati sensibili del kernel: quindi, anche se una pagina viene riutilizzata, le scritture da userland o da codice kernel compromesso verso pagine protette da PPL vengono bloccate.
> Secure Page Table Monitor (SPTM) estende PPL rafforzando gli aggiornamenti delle page table stesse. Garantisce che anche codice kernel privilegiato non possa rimappare silenziosamente pagine liberate o manomettere le mappature senza passare da controlli sicuri.
> KTRR (Kernel Text Read-Only Region) blocca la sezione di codice del kernel come sola lettura dopo il boot. Questo impedisce qualsiasi modifica runtime al codice del kernel, chiudendo un grande vettore d'attacco su cui gli exploit physical UAF spesso fanno affidamento.
> Inoltre, le allocazioni di `IOSurface` sono meno prevedibili e più difficili da mappare in regioni accessibili da userland, il che rende il trucco della "scansione del magic value" molto meno affidabile. E `IOSurface` è ora protetto da entitlements e restrizioni di sandbox.
> Tieni presente che i dispositivi iOS 16+ (A12+) introducono mitigazioni hardware (come PPL o SPTM) che rendono le tecniche di physical UAF molto meno praticabili.
> PPL impone protezioni MMU rigide sulle pagine relative al code signing, alle entitlements e ai dati sensibili del kernel, quindi, anche se una pagina viene riutilizzata, le scritture da userland o da codice kernel compromesso verso pagine protette da PPL vengono bloccate.
> Secure Page Table Monitor (SPTM) estende PPL rafforzando gli aggiornamenti dei page tables stessi. Assicura che anche codice kernel privilegiato non possa rimappare silenziosamente pagine liberate o manomettere le mappings senza passare per controlli sicuri.
> KTRR (Kernel Text Read-Only Region) blocca la sezione di codice del kernel come read-only dopo il boot. Questo impedisce qualsiasi modifica a runtime al codice del kernel, chiudendo una grande superficie dattacco su cui gli exploit physical UAF spesso fanno affidamento.
> Inoltre, le allocazioni IOSurface sono meno prevedibili e più difficili da mappare in regioni accessibili da userland, il che rende il trucco della scansione per il valore magico molto meno affidabile. E IOSurface ora è protetto da entitlements e restrizioni di sandbox.
### Step-by-Step Heap Spray Process
1. **Spray IOSurface Objects**: L'attaccante crea molti oggetti IOSurface con un identificatore speciale ("magic value").
2. **Scan Freed Pages**: Controllano se uno degli oggetti è stato allocato su una pagina liberata.
3. **Read/Write Kernel Memory**: Manipolando i campi nell'oggetto IOSurface, ottengono la possibilità di eseguire **arbitrary reads and writes** nella memoria del kernel. Questo permette di:
* Usare un campo per **read any 32-bit value** nella memoria del kernel.
* Usare un altro campo per **write 64-bit values**, ottenendo un primitivo stabile di **kernel read/write**.
1. **Spray IOSurface Objects**: Lattaccante crea molti IOSurface objects con un identificatore speciale ("magic value").
2. **Scan Freed Pages**: Controllano se uno qualsiasi degli oggetti è stato allocato su una pagina liberata.
3. **Read/Write Kernel Memory**: Manipolando i campi dellIOSurface object, ottengono la capacità di eseguire **arbitrary reads and writes** nella kernel memory. Questo permette di:
* Usare un campo per **leggere qualsiasi valore a 32-bit** nella kernel memory.
* Usare un altro campo per **scrivere valori a 64-bit**, ottenendo un stabile **kernel read/write primitive**.
Generate IOSurface objects with the magic value IOSURFACE\_MAGIC to later search for:
Genera oggetti IOSurface con il valore magico IOSURFACE\_MAGIC per cercarli in seguito:
```c
void spray_iosurface(io_connect_t client, int nSurfaces, io_connect_t **clients, int *nClients) {
if (*nClients >= 0x4000) return;
@ -148,25 +148,25 @@ free(surfaceIDs);
return 0;
}
```
### Ottenere Lettura/Scrittura nel Kernel con IOSurface
### Ottenere lettura/scrittura del kernel con IOSurface
Dopo aver ottenuto il controllo di un oggetto IOSurface nella memoria kernel (mappato su una pagina fisica liberata accessibile da userspace), possiamo usarlo per **operazioni arbitrarie di lettura e scrittura nel kernel**.
Dopo aver ottenuto il controllo di un oggetto IOSurface nella memoria del kernel (mappato su una pagina fisica liberata accessibile da userspace), possiamo usarlo per **operazioni arbitrarie di lettura e scrittura sul kernel**.
**Campi chiave in IOSurface**
**Key Fields in IOSurface**
L'oggetto IOSurface ha due campi cruciali:
1. **Use Count Pointer**: Permette una **lettura a 32 bit**.
2. **Indexed Timestamp Pointer**: Permette una **scrittura a 64 bit**.
1. **Use Count Pointer**: Consente una **lettura 32-bit**.
2. **Indexed Timestamp Pointer**: Consente una **scrittura 64-bit**.
Sovrascrivendo questi puntatori, li reindirizziamo verso indirizzi arbitrari nella memoria kernel, abilitando capacità di lettura/scrittura.
Sovrascrivendo questi puntatori, li reindirizziamo verso indirizzi arbitrari nella memoria del kernel, abilitando capacità di lettura/scrittura.
#### Lettura a 32 bit nel kernel
#### Lettura 32-bit del kernel
Per eseguire una lettura:
1. Sovrascrivi il **use count pointer** affinché punti all'indirizzo di destinazione meno un offset di 0x14 byte.
2. Usa il metodo `get_use_count` per leggere il valore a quell'indirizzo.
1. Sovrascrivere il **use count pointer** per puntare all'indirizzo target meno un offset di 0x14 byte.
2. Usare il metodo `get_use_count` per leggere il valore a quell'indirizzo.
```c
uint32_t get_use_count(io_connect_t client, uint32_t surfaceID) {
uint64_t args[1] = {surfaceID};
@ -184,7 +184,7 @@ iosurface_set_use_count_pointer(info.object, orig);
return value;
}
```
#### Scrittura kernel a 64 bit
#### Scrittura del kernel a 64 bit
Per effettuare una scrittura:
@ -203,13 +203,13 @@ set_indexed_timestamp(info.client, info.surface, value);
iosurface_set_indexed_timestamp_pointer(info.object, orig);
}
```
#### Flusso dell'exploit (riepilogo)
#### Riepilogo del flusso dell'exploit
1. **Trigger Physical Use-After-Free**: Le pagine liberate sono disponibili per il riutilizzo.
2. **Spray IOSurface Objects**: Allocare molti oggetti IOSurface con un unico "magic value" nella memoria kernel.
3. **Identify Accessible IOSurface**: Individuare un IOSurface su una pagina liberata che controlli.
4. **Abuse Use-After-Free**: Modificare i puntatori nell'oggetto IOSurface per abilitare arbitrari **kernel read/write** tramite i metodi IOSurface.
3. **Identify Accessible IOSurface**: Individua un IOSurface su una pagina liberata che controlli.
4. **Abuse Use-After-Free**: Modifica i puntatori nell'oggetto IOSurface per abilitare **kernel read/write** arbitrari tramite i metodi di IOSurface.
Con queste primitive, l'exploit fornisce **32-bit reads** controllati e **64-bit writes** nella memoria kernel. Passaggi successivi per il jailbreak potrebbero richiedere primitive di read/write più stabili, che potrebbero richiedere il bypass di protezioni aggiuntive (ad es., PPL su dispositivi arm64e più recenti).
Con queste primitive, l'exploit fornisce **32-bit reads** controllate e **64-bit writes** alla memoria kernel. Passi successivi del jailbreak potrebbero richiedere primitive di read/write più stabili, che potrebbero richiedere il bypass di protezioni aggiuntive (es. PPL su dispositivi arm64e più recenti).
{{#include ../../banners/hacktricks-training.md}}