
poc für CVE-2024-38063 (RCE in tcpip.sys)
Dies ist ein (eher unzuverlässiger) PoC für CVE-2024-38063, eine RCE in tcpip.sys, die am 13. August 2024 gepatcht wurde. Ich habe diese Schwachstelle nicht gefunden und gemeldet, das war Wei.
pip3 install scapy
Ändere die Felder im Skript:
iface <- Wenn du mehrere Adapter hast, musst du auswählen, mit welchem die Pakete gesendet werden sollen, z. B. "eth0" unter Linux oder "Hyper-V Virtual Ethernet Adapter" unter Windows. Wenn du dein Standard-Interface verwenden möchtest, lass es leer.ip_addr <- IP-Adresse des Zielsystems (IPv6)num_tries & num_batches <- Wie viele verschiedene Paket-Batches gesendet werden sollen. Mehr davon = mehr Heap-Korruptionen verursacht + höhere Chance, die Schwachstelle auszulösen.mac_addr <- Leer lassen, außer scapy meldet, dass es die MAC-Adresse nicht finden kann. Siehe unten unter Fehlerbehebung.Führe das Skript aus:
python3 cve-2024-38063.py
Der einfachste Weg, die Schwachstelle zu reproduzieren, ist die Verwendung von bcdedit /set debug on auf dem Zielsystem und ein Neustart des Rechners/der VM. Dadurch wird kdnic.sys zum Standard-Netzwerkadapter-Treiber, der Pakete sehr gerne koalesziert. Wenn du versuchst, die Schwachstelle in einem anderen Setup zu reproduzieren, musst du das System in einen Zustand bringen, in dem es die von dir gesendeten Pakete koalesziert. Weitere Details findest du im Abschnitt Fehlerbehebung unten.
Du kannst diese großartige Analyse der Schwachstelle von Marcus lesen, wenn du an den technischen Details interessiert bist. Die Details, die ich unten geschrieben habe, sollen eher als Zusammenfassung dienen denn als ernsthafte technische Analyse.
NET_BUFFER -Objekt, das die gepufferten Paketdaten enthält. Bei Offset 0x30 gibt es außerdem ein Current-Offset-Feld, das angibt, wie weit das Paket bereits geparst wurde. In dieser Phase beträgt der Offset-Wert in der Regel 0x28, was bedeutet, dass der IPv6-Header geparst wurde, aber sonst nichts.tcpip!Ipv6pReceiveDestinationOptions ein Parsing-Fehler auftritt, führt dies dazu, dass tcpip!IppSendErrorList aufgerufen wird. Diese Funktion ruft tcpip!IppSendError für jedes Paketobjekt in der verknüpften Liste auf (beginnend mit dem aktuellen).tcpip!IppSendError Nebenwirkungen. Es setzt die gepufferten Paketdaten auf den Anfang zurück und setzt das Current-Offset-Feld auf null.0x8C). Das bedeutet, dass der Treiber weiterhin die Extension Headers anderer Pakete in der verknüpften Liste parst, selbst wenn sie in IppSendError "zurückgesetzt" wurden.0x28.Ipv6pReceiveFragment. Die Funktion parst den Fragment-Extension-Header und geht bei der Berechnung der Länge der Nicht-Header-Daten im Paket davon aus, dass das Offset-Feld des Pakets mindestens 0x28 beträgt, indem sie 0x30 vom aktuellen Offset-Wert subtrahiert. Dieser Wert wird dann im Reassembly-Objekt gespeichert, dessen Zweck es ist, das fragmentierte Paket wieder zusammenzusetzen.IppSendError zurückgesetzt wurde. Der Offset-Wert ist null und wird an einer früheren Stelle in Ipv6pReceiveFragment auf 8 erhöht. Bei der Berechnung der Größe der Nicht-Header-Daten kommt es zu einem Unterlauf, und der Wert entspricht 0xffd8 (die Subtraktion erfolgt in 16 Bit).Ipv6pReassembleDatagram, wo er zur Berechnung der Länge eines Ausgabepuffers für das reassemblierte Paket verwendet wird. Alle Berechnungen erfolgen jedoch in 32 Bit, und es gibt eine Plausibilitätsprüfung, dass die Gesamtlänge 0xFFFF nicht überschreitet, was in diesem Fall geschieht.Ipv6pReassemblyTimeout, wo er ebenfalls auf dieselbe Weise verwendet wird. Hier erfolgen die Berechnungen jedoch in 16 Bit, und es kommt zu einem Integer-Überlauf. Dies führt später zu einem Pufferüberlauf, wenn Daten in den Puffer kopiert werden.Um Ipv6pReassemblyTimeout auszulösen, muss der Absender des Fragments 1 Minute lang inaktiv sein. Unsere Strategie ist dann:
IppSendError auszulösen, gefolgt von einem Fragment-PaketIpv6pReceiveFragment und erstelle ein neues Reassembly-Objekt mit einer Fragment-Datenlänge, die ein hoher 16-Bit-Wert istIpv6pReassemblyTimeout ausgelöst wird.Ipv6pReassemblyTimeout und löse einen Heap-basierten Pufferüberlauf aus.Die Pakete im Skript werden gespammt, damit die Wahrscheinlichkeit höher ist, dass sie koalesziert werden. Die Haupt-Payload ist ziemlich einfach:
Wir setzen außerdem die Hop-Limit- und Flow-Label-Felder im IPv6-Header manuell. Erinnere dich: Die gepufferten Paketdaten werden aufgrund der Schwachstelle zurückgesetzt. Das bedeutet, dass bei der Verarbeitung des Fragment-Pakets der IPv6-Header als Fragment-Header-Daten interpretiert wird. Das Hop-Limit-Feld im IPv6-Header wird als eines der Bits des ID-Felds im Fragment-Header interpretiert. Indem wir es ändern, stellen wir sicher, dass wir die Schwachstelle für mehrere verschiedene Fragmente auslösen und mehrere verschiedene Korruptionen verursachen, was die Wahrscheinlichkeit eines Absturzes erhöht (schließlich ist es ein PoC). Das Flow-Label-Feld des IP-Headers wird als die Offset- und "More-Indikator"-Felder des Fragment-Headers interpretiert. Indem wir es auf 1 setzen, geben wir an, dass weitere Header folgen (und wir später Ipv6pReassemblyTimeout auslösen können) und dass der Offset null ist (da dies das erste eintreffende Paket mit einer solchen ID ist).
Ipv6pReassemblyTimeout erfordert, dass das ursprüngliche Fragment-Paket als Unicast gesendet wird.Wenn es nicht funktioniert, könnte das daran liegen:
Ether(raw(sr1(IPv6(dst={your_dest_ip})/ICMPv6EchoRequest()))).src, aber das funktioniert manchmal nichttcpip!Ipv6pReceiveDestinationOptions -> tcpip!Ipv6pProcessOptions -> tcpip!IppSendErrorList erreicht?tcpip!Ipv6pProcessOptions und prüfe, ob [rcx] die ganze Zeit null ist. Falls ja, werden die Pakete aus irgendeinem Grund nicht koalesziert.tcpip!Ipv6pReceiveFragment und prüfe, ob [rcx+0x30] gleich null ist. Falls nicht, konnte die Schwachstelle aus irgendeinem Grund nicht ausgelöst werden.