The authors competed at Pwn2Own Berlin 2025 in the VMWare Workstation category. The vulnerability exists within the PVSCSI (Paravirtualized SCSI) controller emulation code. This is responsible for handling SCSI commands and forwarding them to the proper device on the machine. The guest OS splits the data into variable-sized chunks, each specifying a guest physical address to use.
The code copies entries via the guest driver into an internal array, then compacts it by combining nearby entries. To begin with, it has 512 segments, totaling 0x2000 bytes. If there are more than 512 entries, it allocates a 0x4000 buffer to store all entries and reallocates it for each newly added entry. The intended design is to double the size of the internal buffer when it needs to grow. The vulnerability is that the buffer allocation is statically set to 0x4000 instead of doubling each time. This leads to a very large out-of-bounds heap write. With more than 1024 entries, it's an OOB write every time.
The Windows 11 Low Fragmentation Heap (LFH) is where this chunk is placed. Typically, the strategy is to target different size classes to shift allocation to a less hardened allocator, but that's not possible here. Notably, the LFH heap has strict checks on chunk metadata and shuffles around allocations.
To exploit this vulnerability, they will need to find an object of size 0x4000 that can be directly allocated from the guest. They ended up using shaders to spray the heap since they can be freed, kept alive, or created at will. The URB objects have a length value on them that is used for writing to host memory directly. This makes them a great primitive for memory corruption.
To exploit this, it required a great deal of knowledge about the heap algorithm. They first filled two buckets of 16 each of shaders. After this, they freed all but one bucket in B1 to create a hole and allocated 15 URBs around it. Finally, a hole is created in B2 and we're ready for the exploit. The allocator will bounce between the two available slots in the two buckets. We use B2 to eat the bad write so that we don't corrupt the metadata of a heap chunk on another object. B1 has an object that can now be corrupted safely. This circumvents the mitigations and allows for the corrupting of OOB chunks on the heap.
This bug can be used to leak ASLR. Once ASLR is leaked, a fake URB structure can be created to cause havoc. For an arbitrary read, overwrite the URB.data_ptr. For an arbitrary write, corrupt URB.pipe and use a writeback mechanism to write those bytes. From there, they corrupted a callback function on a USB pipe object structure to call WinExec() because it's a CFG-whitelisted gadget.
The exploit was unreliable because it assumed knowledge of the heap at startup. They used some tricks to make the exploit more predictable and reliable. Their strategy was that creating a new bucket should take longer. They used this as a time-side channel to understand the current LFH state. Luckily for them, it worked first try during the contest.
They conducted this research over three months, evenings, and weekends. The first month was spent on reverse engineering and identifying the vulnerability. The exploitation took two months to do because of the LFH mitigations. Overall, a good post on the discovery and exploitation to win some money at Pwn2Own!