
Windows Network File System Remote-Exploit für CVE-2022-30136
Autor: Ricardo Narvaja
Nur zu Demonstrationszwecken. Der vollständige Exploit funktioniert auf verwundbaren Windows Server-Systemen.
Siehe den Artikel Analysis of CVE-2022-30136 “Windows Network File System Vulnerability“.
Ich wollte diesen Artikel schreiben, um die Analyse zu demonstrieren, die ich während der Entwicklung des Core Impact Exploits „Windows Network File System Remote“ durchgeführt habe, der die Sicherheitslücke CVE-2022-30136 ausnutzt.
Die Windows Network File System-Remote-Codeausführungssicherheitslücke ist ein Größenberechnungsfehler, der beim Erstellen der Serverantwort in einer COMPOUND REQUEST mithilfe von NFS Version 4.1 auftritt.
Der Server berechnet eine kleinere Größe als nötig, um den Pool zu allozieren, und überläuft dann beim Kopieren der Daten zur Erzeugung der Antwort den Puffer.
Die Funktion Nfs4SvrXdrpGetEncodeOperationResultByteCount in wird für jeden Vorgang aufgerufen und gibt eine kleinere Größe als nötig zurück (4 Bytes weniger pro Vorgang).
Es wurde ein Patch für Nfs4SvrXdrpGetEncodeOperationResultByteCount erstellt.
Diese Funktion wird während jeder OPERATION einer COMPOSE REQUEST aufgerufen, sodass sie die benötigten Bytes für jede einzelne basierend auf dem OPCODE zurückgibt. Dann wird es zum Header und anderen Teilen der Antwort addiert. Als nächstes berechnet es die endgültige Größe der gesamten Antwort, um sie zu allozieren, und kopiert dann darauf, um zu antworten.
In jedem Fall können wir sehen, dass der Wert der für jeden Vorgang zurückgegebenen Größe in der verwundbaren Version vier Bytes kleiner ist als in der gepatchten Version.
Ich habe den PoC für Windows Server 2019 erstellt.
Unten ist die verwundbare Version von nfssvr.sys, die für diesen POC verwendet wird, gefolgt von der gepatchten Version für Windows Server 2019:

Das nächste Bild zeigt CASE 26 im Diff:

Im Beispiel von CASE 26 sehen wir, dass die zum berechneten Wert addierte Konstante in der verwundbaren Version 0x2c und in der gepatchten Version 0x30 beträgt.
Dasselbe ist in jedem Fall, der jedem OPCODE entspricht, zu sehen. Die verwundbare Version gibt immer eine um vier Bytes kleinere Größe zurück als die gepatchte.
Wir werden nicht alle Fälle zeigen, da der Patch für alle OPCODES ähnlich ist.
Die übergeordnete Funktion von Nfs4SvrXdrpGetEncodeOperationResultByteCount ist Nfs4SvrXdrEncodeCompoundResults. Sie liest die Anzahl der in der COMPOUND REQUEST gesendeten Vorgänge.
In diesem POC beträgt der Wert 0x34 (52d). Wenn mein POC eine Verbindung zum Server auf Port 2049 (dem Standardport für NFS) herstellt, muss ich einen bedingten Breakpoint für einen Stopp setzen.


In diesem Fall stoppt es, wenn number_of_operations=0x34 ist.

Der Pool mit dem Tag ARGS wird hier alloziiert.

Ich werde dann eine Struktur namens TAG_ARGS_0x10e0 erstellen, um die Felder zu reverse.

Es kopiert die number_of_operations in r13 und durchläuft die verwundbare Funktion einmal pro Vorgang, bis der Zähler den Wert von r13 erreicht.

Es zeigt, dass der erste package_OPCODE = 0x35 ist, was SEQUENCE in der ersten obligatorischen Operation in einer COMPOUND REQUEST entspricht. Im folgenden Bild zeigt der Pfeil auf diesen OPCODE in meinem Paket.

Hier sehen wir die Argumente der verwundbaren Funktion.


Innerhalb der verwundbaren Funktion liest sie den OPCODE und geht zum entsprechenden CASE.

Drei wird vom ursprünglichen OPCODE-Wert (53) subtrahiert.

Und springt zu CASE 50, das 0x28 für die benötigte Größe dieses Vorgangs zurückgibt.


Wir können im Diff sehen, wie die gepatchte Version 0x2c zurückgibt.

Dieser zurückgegebene Wert wird zum vorherigen Wert anderer Felder in der Antwort addiert, um die Größe der Vorgänge zu berechnen. In diesem Fall beträgt dieser Wert 0X40c.

Unten sehen wir die addierten Werte:

Wenn es die Schleife verlässt, wird die Gesamtgröße berechnet. In diesem Fall beträgt die Gesamtgröße 0x1310.

Wir können den Unterschied zwischen der verwundbaren und der gepatchten Version durch Berechnung der Größe mit der Formel number_of_operations * 4 abschätzen.
In diesem Fall ist die Allozierung in der gepatchten Version um 0x34 * 4 = 0x68 größer als in der verwundbaren Version.

Danach addiert sie 0x24. Dieser Wert wird sowohl in der verwundbaren als auch in der gepatchten Version auf ähnliche Weise berechnet.

Dann wird in beiden Fällen die Konstante 0xf addiert.

Bis zu diesem Punkt betrug die Größe in diesem Beispiel 0x1340.

Als nächstes erreicht es rpcxdr_OncRpcBufMgrpAllocate.

Dann wechselt es zu r15.


Es subtrahiert eins und addiert vier. Dann vergleicht es mit 0x800.
Diese falsch berechnete Größe wird nur verwendet, wenn sie größer als 0x800 ist. Aus diesem Grund löst nur eine COMPOUND REQUEST den Fehler aus.

Zuerst alloziiert es einen Pool mit der Größe = 0x80 und dem Tag XdBD.

Schließlich alloziiert es hier den Pool für die Antwort mit der Größe 0x1398, was einige konstante Werte hinzufügt.

Dann alloziiert es 0x13a0 (einschließlich Tag XdBP und Header).

Von dort speichert es die Adresse des neu alloziierten Pools im Feld tag_XdBD_0x80.p_TAG_XDBP_0x13a0.

Dies zeigt auf die Adresse der Antwort, in die stets kopiert wird.

Dann beginnt es mit dem Aufbau des Antwort-Headers.

Das Folgende ist ein Beispiel dafür, wie es die Daten im Inhalt eines temporären Zeigers speichert und vier addiert.


Unten sehen wir, wie es in den Inhalt der Antwortadresse kopiert.

Dies schreibt das erste DWORD und erhöht den Zeiger um vier.

Als nächstes schreibt es das zweite DWORD und addiert vier.


Nach Verlassen der Funktion ist der gesamte Header geschrieben.

Danach kehrt es zu nfssvr.sys zurück, um mit dem Schreiben der Antwort fortzufahren.

Es wird weiter dekodiert und in die Antwort geschrieben, wobei vier zum temporären Zeiger addiert werden.

Wenn es den Header vervollständigt hat, erreicht es diese Schleife, um alle Vorgänge zu schreiben. Es beginnt mit dem ersten OPCODE 0x35.


Wir können sehen, dass es 0x428 vom Beginn des Pools schreibt.

Jetzt zeigt es nach dem Tag.


Indem wir hier einen Haltepunkt setzen, können wir sehen, wie alle Vorgänge geschrieben wurden.


Nach Verlassen der Schleife sind alle Vorgänge kopiert.

Überprüfen wir das Ende des Pools.

Dort sehen wir das Schreiben nach der Grenze.

Die Allozierung ist kleiner als die kopierten Daten, was einen Pool-Überlauf erzeugt.
Dies erzeugt einen BSOD auf dem Zielsystem. Die Frage ist jedoch, können wir eine Remote-Codeausführung oder ein Write-what-where erreichen?
Ich habe verschiedene Opcode-Kombinationen ausprobiert, um eine Antwort mit kontrollierten Daten in den überlaufenen Bytes zu erhalten. Leider hatte ich kein Glück.
Der maximale Tag (von mir kontrolliert) kann nur am Anfang platziert werden und hat eine maximale Größe von 0x400.

Alle anderen von mir getesteten Opcodes antworten nicht mit kontrollierten Daten. Folglich halte ich es für unmöglich oder zumindest unglaublich schwierig, mit diesem Fehler eine RCE oder Privilegienausweitung zu erreichen. Allerdings könnte es dennoch möglich sein, da ich nicht alle Kombinationen der vielen vorhandenen Möglichkeiten ausprobiert habe.
Für den Aufbau des POC habe ich einen Client namens „NFS CLIENT“ verwendet. Er unterstützt NFS 4.1 und ich konnte verschiedene Opcodes zum Kopieren von Dateien, Bearbeiten, Erstellen von Ordnern usw. ausprobieren.

In diesem Aufbau konnte ich ein COMPOUND-Beispielpaket erstellen und die Größe, die Client-ID, die Session-ID usw. anpassen.

Als nächstes habe ich eine EXCHANGE_ID gesendet, um die Client-ID zu erhalten, und sie verwendet, um eine CREATE_SESSION und schließlich die große COMPOUND REQUEST zu senden.

An diesem Punkt haben wir den Fehler ausgenutzt, was zu einer Remote-Codeausführung führt, die einen DoS-Angriff ermöglicht.
Wir hoffen, dass Sie es nützlich finden. Bei Fragen können Sie uns unter [email protected] kontaktieren.
Viel Spaß!