Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -892,6 +892,7 @@
- [Pixel Bigwave Bigo Job Timeout Uaf Kernel Write](binary-exploitation/linux-kernel-exploitation/pixel-bigwave-bigo-job-timeout-uaf-kernel-write.md)
- [Linux kernel exploitation - toctou](binary-exploitation/linux-kernel-exploitation/posix-cpu-timers-toctou-cve-2025-38352.md)
- [PS5 compromission](binary-exploitation/freebsd-ptrace-rfi-vm_map-prot_exec-bypass-ps5.md)
- [Vmware Workstation Pvscsi Lfh Escape](binary-exploitation/vmware-workstation-pvscsi-lfh-escape.md)
- [Windows Exploiting (Basic Guide - OSCP lvl)](binary-exploitation/windows-exploiting-basic-guide-oscp-lvl.md)
- [Windows Vectored Overloading](binary-exploitation/windows-vectored-overloading.md)
- [iOS Exploiting](binary-exploitation/ios-exploiting/README.md)
Expand Down
63 changes: 63 additions & 0 deletions src/binary-exploitation/vmware-workstation-pvscsi-lfh-escape.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# VMware Workstation PVSCSI LFH Escape (VMware-vmx on Windows 11)

{{#include ../banners/hacktricks-training.md}}

## Bug anatomy: fixed-size realloc + scattered OOB writes

- `PVSCSI_FillSGI` copies guest scatter/gather entries into an internal array. It starts with a 512-entry static buffer (0x2000). Above 512 entries it reallocates to **0x4000** bytes and, because of a functional bug, **reallocates on every iteration**.
- The reallocation size never grows: 0x4000 / 0x10-byte entries = **1024 usable entries**. When the guest supplies **>1024 entries**, each new entry is written **16 bytes past the freshly allocated 0x4000 chunk**, corrupting the adjacent chunk header or object.
- Overflow content: VMware stores `{u64 addr; u64 len}`; guest provides `{u64 addr; u32 len; u32 flags}`. The 32-bit `len` is **zero-extended**, so the last dword of every 16-byte OOB element is **always 0x00000000**.

## LFH constraints & deterministic "Ping-Pong" placement

- 0x4000 allocations land in the **Windows 11 LFH** (16 chunks/bucket, 0x10-byte metadata with keyed checksum). Any chunk whose header checksum is hit later will terminate the process, so corrupted headers must never be reused.
- LFH returns a random free chunk, but **prefers the bucket containing the most recently freed chunk**. Force two free slots only:
1. Allocate all free 0x4000 chunks to align the allocator; spray **32 SVGA shaders** to fill **B1** and **B2** buckets.
2. Free B1 except one pinned shader (**Hole0**) so B1 stays active; allocate **15 URBs** into B1.
3. Free one shader in B2 (**PONG**), then immediately free **Hole0**. LFH will alternate allocations between the two available slots **PING (B1)** and **PONG (B2)**.
- Iteration 1025 corrupts the header after PONG (never touched again); iteration 1026 hits the first 16 bytes of the URB after PING (safe metadata bypass). Reclaim PING/PONG with placeholder shaders to keep the layout stable and repeatable.

## Reap Oracle: labeling contiguous holes

- UHCI URBs live in a FIFO queue and are freed when fully **reaped**. The constrained 16-byte overwrite always zeroes `actual_len`, giving a marker.
- Reap URBs in order; when a zeroed `actual_len` is seen, immediately refill the freed slot with a recognisable shader. Iterating lets you map **Hole0–Hole3** as four contiguous chunks in known order for later adjacency-dependent primitives.

## Turning constrained writes into arbitrary overwrite (coalescing abuse)

`PVSCSI` coalesces adjacent entries using `AddrA + LenA == AddrB` and **compacts** later entries upward.

- **Two-pass overflow:** Trigger starting at PING (odd indices) and exit early to skip coalescing; trigger again starting at PONG (even indices) to fill the gaps and continue writing into a sprayed shader containing fake S/G entries.
- **Vacuum + payload:** 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}
{addr = X, len = Y}
```
so coalescing merges them into `{addr=X,len=Y}`. Even-indexed zero-size elements come from the constrained overflow; odd-indexed values live in the shader. Result: **arbitrary 16-byte patterns** despite the forced zero dword.

## Hybrid URB infoleak via coalescing side-effects

- Arrange contiguous chunks: `[Hole0 (free/PING), URB1 (target), URB2 (valid, actual_len=0), URB3 (leak target)]`.
- Fill URB1 with contiguous fake entries (sizes `0xFFFFFFFF`), touching URB2 minimally. Coalescing merges them into one entry; the sum `0xFFFFFFFF * 0x401` sets the upper dword at URB1’s `actual_len` offset to **0x400**.
- Compaction copies the following data **upward**, pulling **URB2’s header into URB1**. URB1 now has a valid header (pipe/list pointers), `actual_len=0x400`, and a data pointer already at the end of URB2’s buffer.
- Reaping URB1 copies 0x400 bytes starting just before URB3, yielding an **OOB read** of URB3’s header/self-references, which reveals absolute heap addresses and defeats ASLR for subsequent forged structures.

## Post-leak primitives (no re-triggering the bug)

- Forge a URB structure inside a shader occupying **Hole0**, then use the coalescing "move up" to replace URB1 with the forged data.
- Make the URB persistent: set `URB1.next = Hole0` and increment `refcount`; reaping URB1 puts the **Hole0-backed fake URB** at the FIFO head. Future primitives are just reallocations of Hole0 with new fake URBs.
- **Arbitrary read:** fake URB with chosen `data_ptr` and `actual_len`, then reap to copy host memory to the guest.
- **Arbitrary write (32-bit):** fake URB whose `pipe` points to controlled memory and abuse the UHCI **TDBuffer writeback** to store a chosen dword at an arbitrary address.
- **Arbitrary call:** overwrite a USB pipe callback; the host calls it with controlled data at `RCX+0x90`. Resolve `WinExec` dynamically (guest-side read of Kernel32) and pivot through a **CFG-valid gadget inside vmware-vmx** that loads args from `RCX+0x100` before dispatching to `WinExec("calc.exe")`.

## LFH timing side-channel to learn the initial bucket offset

- Deterministic Ping-Pong requires knowing the LFH free-chunk offset (which of 16 slots will be hit first). Use the **VMware backdoor** instruction (`inl %%dx, %%eax`) with the synchronous VMware Tools command `vmx.capability.unified_loop` and a **0x4000-byte string**, which forces **two 0x4000 allocations** per call.
- Time 8 calls (16 allocations) via `gettimeofday`; one call shows a consistent spike when the LFH creates a new bucket. Repeat with one extra allocation: if the spike stays at the same index the offset is odd, if it shifts it is even; otherwise restart due to noise.
- Caveat: `unified_loop` stores unique strings in an unfreeable list, causing **O(n) lookup overhead** and rising noise, so the side-channel must converge quickly.

## References

- [Synacktiv – On the clock: Escaping VMware Workstation at Pwn2Own Berlin 2025](https://www.synacktiv.com/en/publications/on-the-clock-escaping-vmware-workstation-at-pwn2own-berlin-2025.html)

{{#include ../banners/hacktricks-training.md}}
7 changes: 7 additions & 0 deletions src/binary-exploitation/windows-vectored-overloading.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

{{#include ../banners/hacktricks-training.md}}

> [!TIP]
> Looking for Windows 11 LFH heap shaping and VMware Workstation PVSCSI (vmware-vmx) escape techniques?
>
> {{#ref}}
> vmware-workstation-pvscsi-lfh-escape.md
> {{#endref}}

## Technique overview

Vectored Overloading is a **Windows PE injection primitive** that fuses classic [Module Overloading](https://github.com/hasherezade/module_overloading) with **Vectored Exception Handlers (VEHs)** and **hardware breakpoints**. Instead of patching `LoadLibrary` or writing its own loader, the adversary:
Expand Down