VMware Workstation PVSCSI LFH Escape (VMware-vmx on Windows 11)
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.
Anatomia do bug: realloc de tamanho fixo + escritas OOB dispersas
PVSCSI_FillSGIcopia entradas scatter/gather do guest para um array interno. Começa com um buffer estático de 512 entradas (0x2000). Acima de 512 entradas ele realoca para 0x4000 bytes e, por causa de um bug funcional, realoca em cada iteração.- O tamanho da realocação nunca cresce: 0x4000 / entradas de 0x10 bytes = 1024 entradas utilizáveis. Quando o guest fornece >1024 entradas, cada nova entrada é escrita 16 bytes além do chunk 0x4000 recém-alocado, corrompendo o header do chunk adjacente ou o objeto.
- Overflow content: VMware armazena
{u64 addr; u64 len}; o guest fornece{u64 addr; u32 len; u32 flags}. Olende 32 bits é estendido com zeros, então o último dword de cada elemento OOB de 16 bytes é sempre 0x00000000.
Restrições do LFH & posicionamento determinístico “Ping-Pong”
- Alocações de 0x4000 caem no Windows 11 LFH (16 chunks/bucket, metadata de 0x10 bytes com checksum keyed). Qualquer chunk cujo checksum do header for afetado depois fará o processo terminar, então headers corrompidos nunca devem ser reutilizados.
- LFH retorna um chunk livre aleatório, mas prefere o bucket contendo o chunk liberado mais recentemente. Force apenas dois slots livres:
- Alocar todos os chunks livres de 0x4000 para alinhar o allocator; spray 32 SVGA shaders para preencher os buckets B1 e B2.
- Liberar B1 exceto por um shader fixo (Hole0) para que B1 permaneça ativo; alocar 15 URBs em B1.
- Liberar um shader em B2 (PONG) e, imediatamente, liberar Hole0. O LFH vai alternar alocações entre os dois slots disponíveis PING (B1) e PONG (B2).
- A iteração 1025 corrompe o header depois de PONG (nunca tocado novamente); a iteração 1026 atinge os primeiros 16 bytes do URB depois de PING (bypass seguro de metadata). Reivindique PING/PONG com shaders placeholder para manter o layout estável e repetível.
Reap Oracle: rotulagem de buracos contíguos
- UHCI URBs vivem em uma fila FIFO e são liberadas quando totalmente reaped. A sobrescrita restrita de 16 bytes sempre zera
actual_len, fornecendo um marcador. - Reap os URBs em ordem; quando um
actual_lenzerado é observado, imediatamente preencha o slot liberado com um shader reconhecível. Iterar permite mapear Hole0–Hole3 como quatro chunks contíguos em ordem conhecida para primitivas dependentes de adjacência posteriores.
Transformando escritas restritas em overwrite arbitrário (abuso de coalescing)
PVSCSI coalesces entradas adjacentes usando AddrA + LenA == AddrB e compacta as entradas posteriores para cima.
- Two-pass overflow: Dispare começando em PING (índices ímpares) e saia cedo para pular coalescing; dispare novamente começando em PONG (índices pares) para preencher as lacunas e continuar escrevendo em um shader sprayado contendo entradas S/G falsas.
- Vacuum + payload: Configure as entradas
[1023..2047]para{addr=0,len=0}para que o coalescing as colapse em uma só, criando um buraco lógico. Entradas de payload colocadas depois (no shader) são movidas para cima para memória anterior, pousando dentro do URB vítima. - Bypass da checagem de adjacência: Ao definir
LenA=0, a condição viraAddrA==AddrB. Construa pares
{addr = X, len = 0}
{addr = X, len = Y}
para que o coalescing os una em {addr=X,len=Y}. Elementos de tamanho zero em índices pares vêm do overflow restrito; valores em índices ímpares vivem no shader. Resultado: padrões arbitrários de 16 bytes apesar do dword forçado zero.
Hybrid URB infoleak via efeitos colaterais do coalescing
- Arranje chunks contíguos:
[Hole0 (free/PING), URB1 (alvo), URB2 (válido, actual_len=0), URB3 (alvo de leak)]. - Preencha URB1 com entradas falsas contíguas (tamanhos
0xFFFFFFFF), tocando minimamente URB2. O coalescing os funde em uma entrada; a soma0xFFFFFFFF * 0x401define o dword superior no offsetactual_lende URB1 para 0x400. - A compactação copia os dados seguintes para cima, puxando o header de URB2 para dentro de URB1. URB1 agora tem um header válido (ponteiros pipe/list),
actual_len=0x400, e um ponteiro de dados já no final do buffer de URB2. - Reaping de URB1 copia 0x400 bytes começando logo antes de URB3, produzindo uma leitura OOB do cabeçalho/autorreferências de URB3, o que revela endereços absolutos do heap e derrota ASLR para estruturas forjadas subsequentes.
Primitivas pós-leak (sem re-disparar o bug)
- Forge um structure URB dentro de um shader ocupando Hole0, então use o “move up” do coalescing para substituir URB1 pelos dados forjados.
- Faça o URB persistente: defina
URB1.next = Hole0e incrementerefcount; reaping URB1 coloca o fake URB backed por Hole0 no head da FIFO. Primitivas futuras são apenas realocações de Hole0 com novos fake URBs. - Arbitrary read: URB fake com
data_ptreactual_lenescolhidos, então reap para copiar memória do host para o guest. - Arbitrary write (32-bit): URB fake cujo
pipeaponta para memória controlada e abuse do UHCI TDBuffer writeback para armazenar um dword escolhido em um endereço arbitrário. - Arbitrary call: sobrescreva um callback de pipe USB; o host o chama com dados controlados em
RCX+0x90. ResolvaWinExecdinamicamente (leitura guest-side de Kernel32) e pivote através de um gadget CFG-valid dentro de vmware-vmx que carrega args deRCX+0x100antes de despachar paraWinExec("calc.exe").
LFH timing side-channel para aprender o offset inicial do bucket
- O Ping-Pong determinístico requer saber o offset do chunk livre do LFH (qual dos 16 slots será atingido primeiro). Use a instrução backdoor do VMware (
inl %%dx, %%eax) com o comando síncrono do VMware Toolsvmx.capability.unified_loope uma string de 0x4000 bytes, que força duas alocações de 0x4000 por chamada. - Cronometre 8 chamadas (16 alocações) via
gettimeofday; uma chamada mostra um spike consistente quando o LFH cria um novo bucket. Repita com uma alocação extra: se o spike permanecer no mesmo índice o offset é ímpar, se ele se deslocar é par; caso contrário reinicie devido ao ruído. - Caveat:
unified_looparmazena strings únicas numa lista não liberável, causando overhead de lookup O(n) e aumentando o ruído, então o side-channel deve convergir rápido.
Referências
Tip
Aprenda e pratique Hacking AWS:
HackTricks Training AWS Red Team Expert (ARTE)
Aprenda e pratique Hacking GCP:HackTricks Training GCP Red Team Expert (GRTE)
Aprenda e pratique Hacking Azure:
HackTricks Training Azure Red Team Expert (AzRTE)
Supporte o HackTricks
- Confira os planos de assinatura!
- Junte-se ao 💬 grupo do Discord ou ao grupo do telegram ou siga-nos no Twitter 🐦 @hacktricks_live.
- Compartilhe truques de hacking enviando PRs para o HackTricks e HackTricks Cloud repositórios do github.


