During a recent research project we performed an in-depth security assessment of Microsoft’s virtualization technologies, including Hyper-V and Azure. While we already had experience in discovering security vulnerabilities in other virtual environments (e.g. here and here), this was our first research project on the Microsoft virtualization stack and we took care to use a structured evaluation strategy to cover all potential attack vectors.
Part of our research concentrated on the Hyper-V hypervisor itself and we discovered a critical vulnerability which can be exploited by an unprivileged virtual machine to crash the hypervisor and potentially compromise other virtual machines on the same physical host. This bug was recently patched, see MS13-092 and our corresponding post.
In this post, we will focus on the DoS vulnerability, which is rated as important and can affect the availability of the complete hypervisor and potentially all cloud environments based on Hyper-V technology. We think that a skilled and motivated attacker can discover and exploit the vulnerability within two to four weeks without the need for non-public knowledge (or a specific window of opportunity).
Due to the close relationship between the Hyper-V and Azure hypervisor and the fundamental nature of the exploit, we assume that our Proof-of-Concept code will affect the Azure Cloud environment as well. As MS13-092 is an official Microsoft Security Bulletin, we furthermore assume that the vulnerability is patched in Azure in the meantime.
The bug itself is caused by a mishandling of an error condition and was discovered rather fast after the initial setup of our lab environment: Hyper-V partitions can communicate with the hypervisor using so called hypercalls. They work similar to system calls that are used to communicate between ring 3 and 0 but operate between ring 0D (“guest-ring-0”) and 0P (“hypervisor-ring-0”) instead. The actual implementation differs between AMD and Intel systems but the overall interface is the same.
For Intel systems, the vmcall instruction is used to execute a hypercall. Register rcx stores the hypercall number, rdx and r8 contain the guest physical address (GPA) of supplied input and output parameters (we ignore the so-called fast calling convention for now). On the hypervisor side, multiple checks are performed before the hypercall handler is executed. This includes a check if the request originates from Ring 0 (no such restriction is enforced by Intel. The vmcall instruction can also be executed from user mode from an unprivileged partition (read: typical virtual machine), if all supplied memory addresses are properly aligned. In addition the user supplied input GPA is checked against an upper boundary as illustrated in the next image:
If this check fails, the code continues through an error handling function and finally lands in the function at hv+0x2B42DC. This sub function uses the user controlled input address (now stored in rax) directly for a memory read as shown below:
Here, by choosing the right value for our input address we can trigger an invalid memory read and crash the hypervisor itself, resulting in the infamous BSOD.
We think this bug is an interesting example for the kind of issues that can compromise the security and availability assumptions that many users might have with regard to their hypervisors and virtualized environments. We will release our PoC, further detail on the (even more interesting) privilege escalation part and additional Hyper-V research at our Troopers 14 talk. Furthermore the bug will be discussed in much more detail in our Exploting Hypervisors workshop at Troopers.
Felix & Matthias