VMware Workstation PVSCSI LFH Escape (VMware-vmx on Windows 11)
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.
Anatomia del bug: fixed-size realloc + scritture OOB sparse
PVSCSI_FillSGIcopia le guest scatter/gather entries in un array interno. Parte con un buffer statico da 512 entry (0x2000). Oltre 512 entry rialloca a 0x4000 bytes e, a causa di un bug funzionale, rialloca ad ogni iterazione.- La dimensione della riallocazione non cresce mai: 0x4000 / entry da 0x10 = 1024 entry utilizzabili. Quando l’host fornisce >1024 entry, ogni nuova entry viene scritta 16 byte oltre il chunk appena allocato da 0x4000, corrompendo l’header del chunk adiacente o l’oggetto.
- Contenuto dell’overflow: VMware memorizza
{u64 addr; u64 len}; il guest fornisce{u64 addr; u32 len; u32 flags}. Illena 32-bit è esteso a zero, quindi l’ultimo dword di ogni elemento OOB da 16 byte è sempre 0x00000000.
Vincoli LFH & posizionamento deterministico “Ping-Pong”
- Le allocazioni da 0x4000 finiscono nella Windows 11 LFH (16 chunk per bucket, metadata da 0x10 byte con checksum keyed). Qualsiasi chunk il cui checksum dell’header venga colpito successivamente terminerà il processo, quindi gli header corrotti non devono mai essere riutilizzati.
- LFH ritorna un chunk libero casuale, ma prefersce il bucket contenente il chunk liberato più di recente. Forzare soltanto due slot liberi:
- Allocare tutti i chunk liberi da 0x4000 per allineare l’allocatore; spray 32 SVGA shaders per riempire i bucket B1 e B2.
- Liberare B1 tranne uno shader pinned (Hole0) così B1 resta attivo; allocare 15 URBs in B1.
- Liberare uno shader in B2 (PONG), poi liberare immediatamente Hole0. LFH alternerà le allocazioni tra i due slot disponibili PING (B1) e PONG (B2).
- L’iterazione 1025 corrompe l’header dopo PONG (mai più toccato); l’iterazione 1026 colpisce i primi 16 byte dell’URB dopo PING (bypass sicuro dei metadata). Recuperare PING/PONG con shader placeholder per mantenere il layout stabile e ripetibile.
Reap Oracle: etichettare buchi contigui
- Gli URB UHCI stanno in una coda FIFO e vengono liberati quando vengono completamente reaped. La sovrascrittura vincolata a 16 byte azzera sempre
actual_len, fornendo un marcatore. - Reapare gli URB in ordine; quando si vede un
actual_lenazzerato, reinserire immediatamente lo slot liberato con uno shader riconoscibile. Iterando si può mappare Hole0–Hole3 come quattro chunk contigui in ordine noto per poi sfruttare primitive dipendenti dall’adiacenza.
Trasformare scritture vincolate in overwrite arbitrario (abuso del coalescing)
PVSCSI coalesces le entry adiacenti usando AddrA + LenA == AddrB e compatta le entry successive verso l’alto.
- Overflow in due passaggi: Triggerare a partire da PING (indici dispari) e uscire presto per saltare il coalescing; triggerare di nuovo a partire da PONG (indici pari) per riempire i gap e continuare a scrivere in uno shader sprayato che contiene false S/G entries.
- Vacuum + payload: Impostare le entry
[1023..2047]a{addr=0,len=0}così il coalescing le collassa in una sola, creando un hole logico. Le entry payload poste dopo (nello shader) vengono spostate in alto, finendo dentro l’URB vittima. - Bypass del controllo di adiacenza: Impostando
LenA=0, la condizione diventaAddrA==AddrB. Costruire coppie
{addr = X, len = 0}
{addr = X, len = Y}
così il coalescing le unisce in {addr=X,len=Y}. Gli elementi a dimensione zero con indice pari provengono dall’overflow vincolato; i valori con indice dispari vivono nello shader. Risultato: pattern arbitrari di 16 byte nonostante il dword forzato a zero.
Hybrid URB infoleak via coalescing side-effects
- Sistemare chunk contigui:
[Hole0 (free/PING), URB1 (target), URB2 (valid, actual_len=0), URB3 (leak target)]. - Riempire URB1 con fake entries contigue (size
0xFFFFFFFF), toccando URB2 minimamente. Il coalescing le fonde in una singola entry; la somma0xFFFFFFFF * 0x401imposta il dword superiore all’offsetactual_lendi URB1 a 0x400. - La compaction copia i dati successivi verso l’alto, trascinando l’header di URB2 dentro URB1. URB1 ora ha un header valido (puntatori pipe/list),
actual_len=0x400e un puntatore ai dati già alla fine del buffer di URB2. - Reapando URB1 si copiano 0x400 byte partendo appena prima di URB3, producendo una OOB read dell’header/self-reference di URB3, che rivela indirizzi assoluti dell’heap e sconfigge l’ASLR per le strutture forgiate successive.
Post-leak primitives (no re-triggering the bug)
- Forgiare una struttura URB dentro uno shader che occupa Hole0, poi usare il “move up” del coalescing per sostituire URB1 con i dati forgiati.
- Rendere l’URB persistente: impostare
URB1.next = Hole0e incrementare ilrefcount; reaping di URB1 mette la fake URB backed by Hole0 in testa alla FIFO. Le primitive future sono solo riallocazioni di Hole0 con nuove fake URB. - Arbitrary read: fake URB con
data_ptreactual_lenscelti, poi reap per copiare memoria host nel guest. - Arbitrary write (32-bit): fake URB il cui
pipepunta a memoria controllata e abuso della UHCI TDBuffer writeback per scrivere un dword scelto a un indirizzo arbitrario. - Arbitrary call: sovrascrivere una USB pipe callback; l’host la chiama con dati controllati a
RCX+0x90. RisolvereWinExecdinamicamente (guest-side read di Kernel32) e pivotare attraverso un gadget CFG-valid dentro vmware-vmx che carica gli argomenti daRCX+0x100prima di invocareWinExec("calc.exe").
LFH timing side-channel per apprendere l’offset iniziale del bucket
- Il Ping-Pong deterministico richiede di conoscere l’offset del chunk libero nella LFH (quale dei 16 slot verrà colpito per primo). Usare la VMware backdoor instruction (
inl %%dx, %%eax) con il comando sincrono di VMware Toolsvmx.capability.unified_loope una stringa da 0x4000 byte, che forza due allocazioni da 0x4000 per chiamata. - Cronometrare 8 chiamate (16 allocazioni) via
gettimeofday; una chiamata mostra un picco consistente quando la LFH crea un nuovo bucket. Ripetere con un’allocazione in più: se il picco rimane allo stesso indice l’offset è dispari, se si sposta è pari; altrimenti riavviare a causa del rumore. - Caveat:
unified_loopmemorizza stringhe uniche in una lista non liberabile, causando sovraccarico di lookup O(n) e aumento del rumore, quindi il side-channel deve convergere rapidamente.
References
Tip
Impara e pratica il hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica il hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporta HackTricks
- Controlla i piani di abbonamento!
- Unisciti al 💬 gruppo Discord o al gruppo telegram o seguici su Twitter 🐦 @hacktricks_live.
- Condividi trucchi di hacking inviando PR ai HackTricks e HackTricks Cloud repos github.


