VMware Workstation PVSCSI LFH Escape (VMware-vmx on Windows 11)
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.
Anatomía del bug: realloc de tamaño fijo + escrituras OOB dispersas
PVSCSI_FillSGIcopia las entradas scatter/gather del guest en un array interno. Empieza con un buffer estático de 512 entradas (0x2000). Por encima de 512 entradas se realloca a 0x4000 bytes y, debido a un bug funcional, se realloca en cada iteración.- El tamaño de la realocación nunca crece: 0x4000 / entradas de 0x10 bytes = 1024 entradas utilizables. Cuando el guest suministra >1024 entradas, cada nueva entrada se escribe 16 bytes más allá del chunk recién asignado de 0x4000, corrompiendo el header del chunk adyacente u objeto.
- Contenido del overflow: VMware almacena
{u64 addr; u64 len}; el guest provee{u64 addr; u32 len; u32 flags}. Ellende 32 bits es extendido con ceros, por lo que el último dword de cada elemento OOB de 16 bytes es siempre 0x00000000.
Restricciones de LFH y colocación determinista “Ping-Pong”
- Las asignaciones de 0x4000 caen en el Windows 11 LFH (16 chunks/bucket, 0x10-byte metadata with keyed checksum). Cualquier chunk cuyo checksum de cabecera sea alterado más tarde provocará la terminación del proceso, así que los headers corrompidos nunca deben reutilizarse.
- LFH devuelve un chunk libre aleatorio, pero prefiere el bucket que contiene el chunk liberado más recientemente. Forzar solo dos huecos libres:
- Asignar todos los chunks libres de 0x4000 para alinear el allocator; spray 32 SVGA shaders para llenar los buckets B1 y B2.
- Liberar B1 excepto un shader fijado (Hole0) para que B1 permanezca activo; asignar 15 URBs en B1.
- Liberar un shader en B2 (PONG), luego liberar inmediatamente Hole0. LFH alternará asignaciones entre las dos ranuras disponibles PING (B1) y PONG (B2).
- La iteración 1025 corrompe el header tras PONG (ya no será tocado); la iteración 1026 alcanza los primeros 16 bytes del URB después de PING (bypass seguro de metadatos). Recuperar PING/PONG con shaders placeholder para mantener el layout estable y repetible.
Reap Oracle: etiquetado de huecos contiguos
- Las URBs UHCI viven en una cola FIFO y se liberan cuando son completamente reaped. La sobrescritura constreñida de 16 bytes siempre pone a cero
actual_len, proporcionando un marcador. - Reap URBs en orden; cuando se ve un
actual_lena cero, rellenar inmediatamente la ranura liberada con un shader reconocible. Iterar permite mapear Hole0–Hole3 como cuatro chunks contiguos en orden conocido para primitivas futuras que dependen de adyacencia.
Convertir escrituras constreñidas en sobrescritura arbitraria (abuso de coalescing)
PVSCSI coalesces las entradas adyacentes usando AddrA + LenA == AddrB y compacta las entradas siguientes hacia arriba.
- Two-pass overflow: Disparar empezando en PING (índices impares) y salir temprano para evitar la coalescencia; disparar de nuevo empezando en PONG (índices pares) para llenar las brechas y continuar escribiendo en un shader sprayado que contiene entradas S/G falsas.
- Vacuum + payload: Establecer entradas
[1023..2047]a{addr=0,len=0}para que la coalescencia las colapse en una, creando un hueco lógico. Las entradas payload colocadas después (en el shader) son movidas hacia arriba en memoria, aterrizando dentro del URB víctima. - Adjacency-check bypass: Al poner
LenA=0, la condición se vuelveAddrA==AddrB. Fabricar pares
{addr = X, len = 0}
{addr = X, len = Y}
así la coalescencia los fusiona en {addr=X,len=Y}. Los elementos de tamaño cero en índices pares provienen del overflow constreñido; los valores en índices impares residen en el shader. Resultado: patrones arbitrarios de 16 bytes a pesar del dword forzado a cero.
Hybrid URB infoleak vía efectos secundarios de coalescing
- Organizar chunks contiguos:
[Hole0 (free/PING), URB1 (target), URB2 (valid, actual_len=0), URB3 (leak target)]. - Rellenar URB1 con entradas falsas contiguas (tamaños
0xFFFFFFFF), tocando URB2 mínimamente. La coalescencia las fusiona en una entrada; la suma0xFFFFFFFF * 0x401establece el dword alto en el offsetactual_lende URB1 a 0x400. - La compactación copia los datos siguientes hacia arriba, trayendo el header de URB2 dentro de URB1. URB1 ahora tiene un header válido (punteros pipe/list),
actual_len=0x400, y un puntero a datos ya al final del buffer de URB2. - Reaping URB1 copia 0x400 bytes empezando justo antes de URB3, produciendo una OOB read del header/self-references de URB3, lo que revela direcciones absolutas del heap y derrota ASLR para estructuras forjadas posteriores.
Primitivas post-leak (sin reactivar el bug)
- Forjar una estructura URB dentro de un shader que ocupe Hole0, luego usar el “move up” de la coalescencia para reemplazar URB1 con los datos forjados.
- Hacer persistente el URB: setear
URB1.next = Hole0e incrementarrefcount; reaping URB1 pone el Hole0-backed fake URB en la cabeza de la FIFO. Las primitivas futuras son simplemente reasignaciones de Hole0 con nuevos fake URBs. - Arbitrary read: URB falsa con
data_ptryactual_lenelegidos, luego reap para copiar memoria del host al guest. - Arbitrary write (32-bit): URB falsa cuyo
pipeapunta a memoria controlada y abusar del UHCI TDBuffer writeback para almacenar un dword elegido en una dirección arbitraria. - Arbitrary call: sobrescribir un callback de pipe USB; el host lo llama con datos controlados en
RCX+0x90. ResolverWinExecdinámicamente (lectura guest-side de Kernel32) y pivotar mediante un CFG-valid gadget inside vmware-vmx que carga argumentos desdeRCX+0x100antes de despachar aWinExec("calc.exe").
LFH timing side-channel para aprender el offset inicial del bucket
- El Ping-Pong determinista requiere conocer el offset de chunk libre en LFH (cuál de las 16 ranuras será golpeada primero). Usar la instrucción VMware backdoor (
inl %%dx, %%eax) con el comando síncrono de VMware Toolsvmx.capability.unified_loopy una cadena de 0x4000 bytes, lo que fuerza dos asignaciones de 0x4000 por llamada. - Cronometrar 8 llamadas (16 asignaciones) vía
gettimeofday; una llamada muestra un pico consistente cuando LFH crea un nuevo bucket. Repetir con una asignación extra: si el pico se mantiene en el mismo índice el offset es impar, si se desplaza es par; de lo contrario reiniciar por ruido. - Caveat:
unified_loopalmacena cadenas únicas en una lista no liberable, causando O(n) lookup overhead y aumento de ruido, así que el side-channel debe converger rápidamente.
References
Tip
Aprende y practica Hacking en AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica Hacking en GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Hacking en Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Revisa los planes de suscripción!
- Únete al 💬 grupo de Discord o al grupo de telegram o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud repositorios de github.


