
CVE-2018-4241: Heap-Überlauf im XNU-Kernel aufgrund fehlerhafter Grenzprüfung in MPTCP für iOS 11 - 11.3.1, veröffentlicht von Ian Beer
@i41nbeer
mptcp_usr_connectx ist der Handler für den connectx-Systemaufruf für die AP_MULTIPATH Socket-Familie.
Die Logik dieser Funktion behandelt Quell- und Ziel-Sockaddr, die weder AF_INET noch AF_INET6 sind, nicht korrekt:
// verify sa_len for AF_INET:
if (dst->sa_family == AF_INET &&
dst->sa_len != sizeof(mpte->__mpte_dst_v4)) {
mptcplog((LOG_ERR, "%s IPv4 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// verify sa_len for AF_INET6:
if (dst->sa_family == AF_INET6 &&
dst->sa_len != sizeof(mpte->__mpte_dst_v6)) {
mptcplog((LOG_ERR, "%s IPv6 dst len %u\n", __func__, dst->sa_len), MPTCP_SOCKET_DBG, MPTCP_LOGLVL_ERR);
error = EINVAL;
goto out;
}
// code doesn't bail if sa_family was neither AF_INET nor AF_INET6
if (!(mpte->mpte_flags & MPTE_SVCTYPE_CHECKED)) {
if (mptcp_entitlement_check(mp_so) < 0) {
error = EPERM;
goto out;
}
mpte->mpte_flags |= MPTE_SVCTYPE_CHECKED;
}
// memcpy with sa_len up to 255:
if ((mp_so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0) {
memcpy(&mpte->mpte_dst, dst, dst->sa_len);
}
Wenn man sich die Struktur ansieht, die man überläuft, stellt man fest, dass man beide Felder hier treffen kann:
if (mpte->mpte_itfinfo_size > MPTE_ITFINFO_SIZE) _FREE(mpte->mpte_itfinfo, M_TEMP);
mpte_itfinfo_size befindet sich direkt vor mpte_itfinfo.
Wenn die Struktur initialisiert wird, zeigt der mpte_itfinfo-Zeiger auf ein kleines Inline-Array. Wenn mehr Subflows hinzugefügt werden, als dort hineinpassen, werden sie stattdessen in einen Heap-Puffer gelegt, und mpte_itfinfo zeigt dann darauf.
Wenn man einen weiteren Bug hätte (z.B. den Kernel-Heap-Offenlegungsfehler aus async_wake), könnte man das mpte_itfinfo-Feld mit einem beliebigen gültigen Zonenobjekt überschreiben und es würde freigegeben werden (tatsächlich könnte man es auch mit einem Offset in dieses Objekt überschreiben, für noch mehr Spaß!)
Allerdings haben wir das nicht.
Stattdessen besteht ein anderer Ansatz darin, den Zeiger teilweise zu überschreiben. Wenn wir ihn teilweise mit NULL-Bytes überschreiben, können wir ihn auf einen 256-Byte-, 65k-, 16MB- oder 4GB-ausgerichteten Wert zeigen lassen.
In diesem Exploit wähle ich eine 3-Byte-NULL-Überschreibung, die ein kfree der mpte_itfinfo-Adresse auslöst, abgerundet auf die nächste 16MB-Grenze.
Der Exploit-Ablauf ist wie folgt:
Ich verwende den Thread-Exception-Port-Trick aus extra_recipe, um Nachrichten an den vorallokierten ipc_kmsg-Puffer zu senden. Jedes Mal überprüfen wir jede der Pipes, ob eine von ihnen die Nachricht enthält. Wenn wir das richtige (ipc_kmsg,pipe)-Paar gefunden haben, können wir die Nachricht umschreiben, um uns selbst einen Fake-Port zu senden, der sich im Pipe-Puffer befindet. Ich strukturiere diesen Fake-Port wie den aus async_wake (den ich auf yalu 10.2 von @qwertyoruiopz und @marcograss basiert habe), um mir eine frühe Kernel-Lese-Primitive zu verschaffen.
Mit der Kernel-Lese-Primitive finde ich den Kernel-Task und erstelle einen Fake-Port, der einfacheren Kernel-Speicher-Lese-/Schreibzugriff über mach_vm_read/mach_vm_write ermöglicht.
Hinweis: Um mptcp-Sockets zu verbinden, benötigt man die Berechtigung com.apple.developer.networking.multipath, die ein Apple-Entwicklerzertifikat erfordert, das jeder von Apple kaufen kann.
Zuverlässigkeit: Dies ist ein Sicherheitsforschungswerkzeug und ist weit entfernt von perfekt. Es sollte jedoch die meiste Zeit funktionieren, und wenn es funktioniert, sollte es gute Aufräumarbeiten leisten, sodass es später nicht zu einem Panic kommt.
Um die Erfolgswahrscheinlichkeit zu erhöhen:
Unterstützte Geräte: Es sollte auf iOS 11.0 bis einschließlich 11.3.1 funktionieren. Getestet auf: iPod Touch 6g, iPhone 6s, iPhone SE, iPhone 7, iPhone 8
API: #include "sploit.h" und rufe go() auf, um den Exploit auszuführen. Wenn es funktioniert hat, kannst du die Funktionen in kmem.h verwenden, um Kernel-Speicher zu lesen und zu schreiben
Anmerkungen: Mehrere Personen haben diesen Bug öffentlich aus dem Patch gebindiffed (oder ihr 0day wurde gepatched ;) lies ihre Beiträge für weitere Details: @elvanderb hielt einen Lightning Talk über den Bug auf dem rump.beer in Paris am 31. Mai: https://www.rump.beer/2018/slides/ios_48h.pdf @jaakerblom veröffentlichte einen funktionierenden Exploit auf GitHub am 1. Juni: https://github.com/potmdehex/multipath_kfree Johns Technik ist ähnlich wie meine, aber er führt einen Zwei-Byte-Überlauf anstelle eines Drei-Byte-Überlaufs durch und ersetzt sie mit anderen Objekten. Gutes Zeug!