VMware Workstation PVSCSI LFH Escape (VMware-vmx on Windows 11)

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Bug-Anatomie: fixed-size realloc + scattered OOB writes

  • PVSCSI_FillSGI kopiert Guest scatter/gather-Einträge in ein internes Array. Es beginnt mit einem 512-Einträge statischen Puffer (0x2000). Bei mehr als 512 Einträgen macht es ein Realloc auf 0x4000 Bytes und aufgrund eines funktionalen Bugs reallokiert es bei jeder Iteration.
  • Die Größe der Reallokation wächst nie: 0x4000 / 0x10-Byte Einträge = 1024 nutzbare Einträge. Wenn der Gast >1024 Einträge liefert, wird jeder neue Eintrag 16 Bytes hinter dem frisch allozierten 0x4000-Chunk geschrieben und korruptiert das angrenzende Chunk-Header oder Objekt.
  • Overflow-Inhalt: VMware speichert {u64 addr; u64 len}; der Gast liefert {u64 addr; u32 len; u32 flags}. Das 32-bit len wird auf 64 Bit mit Nullen erweitert, sodass das letzte dword jedes 16-Byte OOB-Elements immer 0x00000000 ist.

LFH-Einschränkungen & deterministische “Ping-Pong”-Platzierung

  • 0x4000-Allokationen landen im Windows 11 LFH (16 Chunks/Bucket, 0x10-Byte Metadaten mit keyed checksum). Jeder Chunk, dessen Header-Checksum später getroffen wird, beendet den Prozess, daher dürfen korrumpierte Header niemals wiederverwendet werden.
  • LFH gibt einen zufälligen freien Chunk zurück, bevorzugt aber den Bucket, der den zuletzt freigegebenen Chunk enthält. Erzwinge nur zwei freie Slots:
  1. Alloziere alle freien 0x4000-Chunks, um den Allocator auszurichten; spray 32 SVGA shaders, um B1 und B2 Buckets zu füllen.
  2. Free B1 bis auf einen gepinnten Shader (Hole0), so dass B1 aktiv bleibt; alloziere 15 URBs in B1.
  3. Free einen Shader in B2 (PONG), dann sofort Hole0. LFH wechselt dann die Allokationen zwischen den beiden verfügbaren Slots PING (B1) und PONG (B2).
  • Iteration 1025 korruptiert den Header nach PONG (wird nie wieder angerührt); Iteration 1026 trifft die ersten 16 Bytes des URB nach PING (sichere Metadaten-Bypass). Reclaim PING/PONG mit Platzhalter-Shaders, um das Layout stabil und reproduzierbar zu halten.

Reap Oracle: Kennzeichnung zusammenhängender Lücken

  • UHCI URBs liegen in einer FIFO-Queue und werden freigegeben, wenn sie vollständig reaped sind. Der eingeschränkte 16-Byte-Overwrite nullt immer actual_len und liefert damit einen Marker.
  • Reape URBs in Reihenfolge; wenn ein nulliertes actual_len gesehen wird, fülle den freigewordenen Slot sofort mit einem erkennbaren Shader nach. Iteration erlaubt es, Hole0–Hole3 als vier zusammenhängende Chunks in bekannter Reihenfolge für spätere adjazenzabhängige Primitives zu kartieren.

Constrained writes zu beliebigem Overwrite machen (Coalescing-Missbrauch)

PVSCSI coalesciert benachbarte Einträge mit AddrA + LenA == AddrB und kompaktiert spätere Einträge nach oben.

  • Zwei-Pass-Overflow: Trigger starte bei PING (ungerade Indizes) und breche früh ab, um Coalescing zu überspringen; trigger dann erneut bei PONG (gerade Indizes), um die Lücken zu füllen und weiter in einen gesprayten Shader zu schreiben, der gefälschte S/G-Einträge enthält.
  • Vacuum + Payload: Setze Einträge [1023..2047] auf {addr=0,len=0}, sodass Coalescing sie zu einem zusammenfasst und ein logisches Loch erzeugt. Payload-Einträge, die danach (im Shader) liegen, werden nach oben verschoben und landen innerhalb des Victim-URB.
  • Adjazenz-Check-Bypass: Indem LenA=0 gesetzt wird, wird die Bedingung zu AddrA==AddrB. Baue Paare
{addr = X, len = 0}
{addr = X, len = Y}

so dass Coalescing sie zu {addr=X,len=Y} verschmilzt. Die gerade indizierten Null-Elemente stammen aus dem eingeschränkten Overflow; ungerade indizierte Werte leben im Shader. Ergebnis: beliebige 16-Byte-Pattern trotz des erzwungenen null-Dwords.

Hybrid URB infoleak via Coalescing-Nebenwirkungen

  • Arrange zusammenhängende Chunks: [Hole0 (free/PING), URB1 (Ziel), URB2 (gültig, actual_len=0), URB3 (leak target)].
  • Fülle URB1 mit zusammenhängenden Fake-Einträgen (Größen 0xFFFFFFFF), URB2 nur minimal berühren. Coalescing verschmilzt sie zu einem Eintrag; die Summe 0xFFFFFFFF * 0x401 setzt das obere DWord am actual_len-Offset von URB1 auf 0x400.
  • Die Kompaktierung kopiert die folgenden Daten nach oben und zieht damit URB2s Header in URB1. URB1 hat jetzt einen gültigen Header (pipe/list pointers), actual_len=0x400 und einen Datenzeiger, der bereits am Ende von URB2s Buffer steht.
  • Reapt man URB1, kopiert es 0x400 Bytes, beginnend kurz vor URB3, was ein OOB-Read von URB3s Header/Self-References ergibt und absolute Heap-Adressen offenbart, wodurch ASLR für anschließend gefälschte Strukturen ausgehebelt wird.

Post-leak-Primitives (kein erneutes Triggern des Bugs)

  • Forge eine URB-Struktur innerhalb eines Shaders, der Hole0 belegt, und verwende dann das Coalescing-“Move up”, um URB1 durch die gefälschten Daten zu ersetzen.
  • Mache die URB persistent: setze URB1.next = Hole0 und inkrementiere refcount; das Reapen von URB1 bringt die Hole0-gesicherte Fake-URB an den Kopf der FIFO. Zukünftige Primitives sind nur Reallokationen von Hole0 mit neuen Fake-URBs.
  • Arbitrary read: gefakte URB mit gewähltem data_ptr und actual_len, dann reap, um Host-Speicher in den Guest zu kopieren.
  • Arbitrary write (32-bit): gefakte URB, deren pipe auf kontrollierten Speicher zeigt, und Missbrauch des UHCI TDBuffer writeback, um ein gewähltes dword an einer beliebigen Adresse zu schreiben.
  • Arbitrary call: überschreibe einen USB-Pipe-Callback; der Host ruft ihn mit kontrollierten Daten bei RCX+0x90 auf. Löse WinExec dynamisch auf (Guest-seitiger Read von Kernel32) und pivotiere durch ein CFG-valides Gadget inside vmware-vmx, das Argumente von RCX+0x100 lädt, bevor es zu WinExec("calc.exe") dispatcht.

LFH-Timing-Seitenkanal zur Bestimmung des initialen Bucket-Offsets

  • Deterministisches Ping-Pong erfordert Wissen über den LFH free-chunk offset (welcher der 16 Slots zuerst getroffen wird). Verwende die VMware backdoor Instruktion (inl %%dx, %%eax) mit dem synchronen VMware Tools Befehl vmx.capability.unified_loop und einem 0x4000-Byte String, was pro Aufruf zwei 0x4000-Allokationen erzwingt.
  • Messe 8 Aufrufe (16 Allokationen) via gettimeofday; ein Aufruf zeigt einen konsistenten Spike, wenn LFH einen neuen Bucket anlegt. Wiederhole mit einer zusätzlichen Allokation: bleibt der Spike am gleichen Index, ist das Offset odd; verschiebt er sich, ist es even; andernfalls wegen Rauschen neu starten.
  • Caveat: unified_loop speichert einzigartige Strings in einer nicht-freigabaren Liste, verursacht O(n) Lookup-Overhead und steigendes Rauschen, daher muss der Seitenkanal schnell konvergieren.

References

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks