This story is the research put into a VM escape for the Parallels Virtual machine for Pwn2Own. It is interesting to see the start and the end of the research, as we usually only see the final result.
The author did a solid amount of reading into the VM escape world. After a while, they noticed that the hardware components of a VM were something that had been done wrong on several occasions for VirtualBox and VMWare. As a result, they choose to target this virtual hardware component.
The author wrote a simple port I/O fuzzer. This fuzzer wrote random data to random I/O port addresses in the guest in the Linux VM. From there, they had a more precise fuzzer that fuzzed MMIO (memory mapped I/O) with random bytes. Both of these fuzzers caused crashes on either ASSERTs or real crashes.
Parallels dumps logs quite nicely for us! From the crash, we could see a memory dump of what had happened. From reverse engineering one of the crashes, they noticed that something had occurred in the hypervisor that should not have happened. The code had to do with the virtio VGA device settings.
From more reverse engineering and reading the specification of virtio the functionality was in the PCIVirtIOWriteMM function.
All of the real crashes came down to one bug: the driver feature select was user controllable and being used as a index in an array access without ever being validated. This led to a straight forward relative out of bounds read and out of bounds write.
To get code execution, they found a list of function pointers called port I/O handlers. By writing to one of the unused handlers, they could get code execution in the kernel by adding their own port writing commands to the Hypervisor code.
Taking this to code execution was trivial, as the __data and __bss sections do not have the NX bit set on them. As a result, the shellcode could be written to some user controlled data into some of these sections of memory, then executed via the fake port handler. So, what is next?
Arbitrary code execution on a test platform is the hard part but there is still more to it. They needed to move from their test Macbook to the most recent version of Big Sur. Additionally, they needed to demonstrate complete control of the system, not just simply code execution.
To demonstrate code execution and control over the main device, they decided to pop a calculator on the host machine. This was done by mapping a physical memory page in the hypervisor as readable and writable. From there, they scan for a page on the host machine. If they had found a target page, they would modify the process to pop a calc. Not hard but just annoying to do.
The change to Big Sur ended up being a big deal. The exploit code had to now account for several exploit mitigations and several changes to the architecture of the hypervisor.
Bypassing ASLR was quite easy with the relative read. To get a more powerful arbitrary write/arbitrary read, they overwrite the port I/O handler parameters. By controlling these values, AhciIdpIndexInPortFunc gave absolute read and AhciIdpIndexOutPortFunc gave an absolute write.
Here are some tips from the author at the end:
- For Pwn2Own, just try! Not everything for this competition is novel and crazy work. Sometimes, simple things work well. You find vulns by DOING and not READING.
- Read previous work and specifications help a lot for ideas.
- When first on a target, do something real simple to help you get familiar. Reverse something easy or setup a simple fuzzer to get some momentum.
- Post-mortem analysis in security research is real important to see if you missed anything and why you did.