What you need to understand instructions are the Intel software Developer Manuals. For what it's worth, I sometimes find them a bit opaque - so I contrast what I read on google and in the AMD Manuals as they both implement the x86/x64 architecture.
Specifically, for this you need Vol 3C: Virtualisation. As of the current manual you want page 293 ("VMCALL—Call to VM Monitor"). In there, you have this helpful expression:
IF not in VMX operation
THEN #UD;
ELSIF in VMX non-root operation
THEN VM exit;
ELSIF (RFLAGS.VM = 1) or (IA32_EFER.LMA = 1 and CS.L = 0)
THEN #UD;
ELSIF CPL > 0
THEN #GP(0);
...
Translation into English: #GP
is a general protection exception, CPL
is the current privilege level. VMX non-root operation is:
There are two kinds of VMX operation: VMX root operation and
VMX non-root operation. In general, a VMM will run in VMX root operation and guest software will run in VMX non-root operation.
So, that would imply that VMCall
instructions can be issued from any privilege level on the guest. The ring 0 requirement only applies to the host system. However, I caveat that with a disclaimer - see my implications bulletpoints in a minute.
Crazily insane I know. Apparently, however, it's true. Have a read of this thread where KVM kernel devs are discussing exactly this. The KVM patch in question did this:
kvm_get_segment(vcpu,&cs, VCPU_SREG_CS);
if (cs.dpl != 0) {
ret = -KVM_EPERM;
goto out;
}
Which considers the "hypercall" invalid if not made from guest ring 0.
Implications:
- Any guest level code can issue a
VMCall
. However, from the very preliminary reading I've done it seems as though it (might) might be impossible for that to be useful in any way - as ring 3 addresses would presumably be virtual w.r.t the guest operating system (and therefore mean nothing to the host). I strongly suspect this is the case, but I cannot (yet) confirm it.
- If you think about it this makes sense - if guest level code triggers some other kind of VM Exit you want the VMM to take over.
- However, I suspect many hypervisors implement the above restriction - ring 0 guest only, or some other conditions on the hypercall API they apply.
- However, they may not.
So, what you need to do if you really want to dig into it is verify:
- What hypercalls, if any, your VMM supports.
- What conditions they consider a hypercall valid. As I say, I expect the
VMCall
will be restricted to guest ring 0 only.
If it is the case that VMCall
et al can only be meaningfully executed from ring 0, then your client-side guest code must be able to execute in ring 0 too, or persuade some of your existing code to do something it shouldn't. For example:
- You can persuade the kernel to execute an arbitrary piece of code that contains the
VMCall
to do whatever it is you want.
- You persuade an existing driver that uses a
VMCall
to make that call your way i.e. with your arguments, rather than the ones it might ordinarily use.
The success of that for an exploit depends entirely on the hypervisor and if those VMCalls are vulnerable or not. If they're not, it doesn't matter whether you managed to call them - the hypervisor can reject them as appropriate. At this point, I honestly don't think this exploit route (through a VMCall
) is very likely (although the consequences are of course pretty severe if there were one).
Two things I've missed here:
- There are many other triggers for VM Exits. Can guest processes cause them? Not programmatically I don't think - they happen because of something the guest is doing (conditions for exits supplied by the VMM) rather than in direct response to an opcode.
- Again, "breaking out" of a VM is a loose term. Users of virtual machines should also be wary of things like network emulation - if your guest and host can talk to each other over TCP/IP and your host IP stack has a vulnerability in it and code on the guest can exploit it - you could still be compromised. Likewise, transferring files from guest to host carries an inherent risk.