Hacktricks-skills vmware-pvscsi-lfh-exploit
Analyze and understand the VMware Workstation PVSCSI LFH escape vulnerability (CVE-2025-20947). Use this skill whenever the user mentions VMware Workstation heap exploitation, LFH (Low Fragmentation Heap) attacks, PVSCSI driver vulnerabilities, or Windows 11 kernel escape techniques. Also trigger for discussions about scatter/gather buffer overflows, heap coalescing abuse, or URB-based exploitation primitives.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/binary-exploitation/vmware-workstation-pvscsi-lfh-escape/SKILL.MDVMware Workstation PVSCSI LFH Escape Analysis
A skill for understanding and analyzing the VMware Workstation PVSCSI driver vulnerability that enables VM escape on Windows 11.
Vulnerability Overview
This skill helps analyze the fixed-size realloc + scattered OOB write vulnerability in VMware's PVSCSI driver:
- Trigger: Guest supplies >1024 scatter/gather entries to
PVSCSI_FillSGI - Root cause: Static 512-entry buffer (0x2000 bytes) reallocates to fixed 0x4000 bytes, but reallocates on every iteration without growing
- Effect: Each entry beyond 1024 writes 16 bytes past the allocated chunk, corrupting adjacent heap metadata
- Constraint: Overflow content is
where{u64 addr; u64 len}
is zero-extended from 32-bit, making the last dword alwayslen0x00000000
Exploitation Technique: LFH Ping-Pong
Why LFH Matters
Windows 11 uses the Low Fragmentation Heap (LFH) for 0x4000 allocations:
- 16 chunks per bucket with 0x10-byte metadata (keyed checksum)
- Corrupted headers that get reused will crash the process
- LFH returns random free chunks but prefers the bucket with the most recently freed chunk
Deterministic Placement Strategy
Force exactly two free slots to create predictable "Ping-Pong" allocation:
- Spray phase: Allocate all free 0x4000 chunks (32 SVGA shaders) to fill buckets B1 and B2
- Free B1 except one: Keep one shader pinned as Hole0 so B1 stays active; allocate 15 URBs into B1
- Create PONG: Free one shader in B2, then immediately free Hole0
- Result: LFH alternates between PING (B1) and PONG (B2) slots
Critical timing:
- Iteration 1025: Corrupts header after PONG (never touched again)
- Iteration 1026: Hits first 16 bytes of URB after PING (safe metadata bypass)
- Reclaim PING/PONG with placeholder shaders to maintain stable layout
Reap Oracle: Mapping Contiguous Holes
URBs live in a FIFO queue and are freed when fully reaped. The constrained 16-byte overwrite zeroes
actual_len, creating a marker:
Reap URBs in order → when zeroed actual_len seen → refill slot with recognizable shader
This maps Hole0–Hole3 as four contiguous chunks in known order for adjacency-dependent primitives.
Coalescing Abuse: Constrained Writes → Arbitrary Overwrite
PVSCSI coalesces adjacent entries using AddrA + LenA == AddrB and compacts later entries upward.
Two-Pass Overflow
- First pass: Trigger at PING (odd indices), exit early to skip coalescing
- Second pass: Trigger at PONG (even indices) to fill gaps, continue writing into sprayed shader with fake S/G entries
Vacuum + Payload Technique
Set entries
[1023..2047] to {addr=0, len=0} so coalescing collapses them into one, creating a logical hole. Payload entries placed afterwards (in the shader) are moved up into earlier memory, landing inside the victim URB.
Adjacency-Check Bypass
By setting
LenA=0, the condition becomes AddrA==AddrB. Craft pairs:
{addr = X, len = 0} // from constrained overflow (even indices) {addr = X, len = Y} // from shader (odd indices)
Coalescing merges them into
{addr=X, len=Y}. Result: arbitrary 16-byte patterns despite the forced zero dword.
Hybrid URB Infoleak
Setup
Arrange contiguous chunks:
[Hole0 (free/PING), URB1 (target), URB2 (valid, actual_len=0), URB3 (leak target)]
Execution
- Fill URB1 with contiguous fake entries (sizes
), touching URB2 minimally0xFFFFFFFF - Coalescing merges them into one entry; sum
sets upper dword at URB1's0xFFFFFFFF * 0x401
offset to 0x400actual_len - Compaction copies following data upward, pulling URB2's header into URB1
- URB1 now has valid header (pipe/list pointers),
, data pointer at end of URB2's bufferactual_len=0x400 - Reaping URB1 copies 0x400 bytes starting just before URB3, yielding OOB read of URB3's header/self-references
Result: Absolute heap addresses revealed, ASLR defeated for subsequent forged structures.
Post-Leak Primitives (No Re-Triggering)
Persistent Fake URB
- Forge URB structure inside shader occupying Hole0
- Use coalescing "move up" to replace URB1 with forged data
- Set
and incrementURB1.next = Hole0refcount - Reaping URB1 puts Hole0-backed fake URB at FIFO head
- Future primitives: just reallocate Hole0 with new fake URBs
Arbitrary Read
Fake URB with chosen
data_ptr and actual_len, then reap to copy host memory to guest.
Arbitrary Write (32-bit)
Fake URB whose
pipe points to controlled memory, abuse UHCI TDBuffer writeback to store chosen dword at arbitrary address.
Arbitrary Call
- Overwrite USB pipe callback
- Host calls it with controlled data at
RCX+0x90 - Resolve
dynamically (guest-side read of Kernel32)WinExec - Pivot through CFG-valid gadget inside vmware-vmx that loads args from
before dispatching toRCX+0x100WinExec("calc.exe")
LFH Timing Side-Channel
Deterministic Ping-Pong requires knowing the LFH free-chunk offset (which of 16 slots will be hit first).
Technique
- Use VMware backdoor instruction (
) with synchronous VMware Tools commandinl %%dx, %%eaxvmx.capability.unified_loop - Pass 0x4000-byte string, forcing two 0x4000 allocations per call
- Time 8 calls (16 allocations) via
gettimeofday - One call shows consistent spike when LFH creates new bucket
- Repeat with one extra allocation:
- If spike stays at same index: offset is odd
- If spike shifts: offset is even
- Otherwise: restart due to noise
Caveat
unified_loop stores unique strings in unfreeable list, causing O(n) lookup overhead and rising noise. Side-channel must converge quickly.
Analysis Workflow
When analyzing this vulnerability:
- Understand the constraint: 16-byte OOB writes with forced zero dword
- Map the heap: Use LFH timing side-channel to learn bucket offset
- Create Ping-Pong: Force deterministic allocation pattern
- Build Reap Oracle: Map contiguous holes via URB reaping
- Exploit coalescing: Two-pass overflow + vacuum technique for arbitrary writes
- Infoleak: Hybrid URB technique to defeat ASLR
- Forge primitives: Persistent fake URB for arbitrary read/write/call
Security Research Context
This vulnerability demonstrates:
- Heap metadata corruption via fixed-size reallocation bug
- LFH exploitation on Windows 11 with checksummed headers
- Coalescing abuse to bypass constrained write patterns
- Timing side-channels for deterministic heap manipulation
- VM escape via driver vulnerability in virtualization software
References
Note: This skill is for educational and security research purposes. Understanding these vulnerabilities helps improve virtualization security and defensive measures.