VMware Fusion is a popular hypervisor for macOS. It allows users to run nearly any operating system on a Mac. We identified a local privilege escalation. This issue was assigned CVE-2020-3974 / VMSA-2020-0017 and was fixed in version 11.5.5 released on 2020-07-09..
VMWare Fusion has several components that need to run with high privileges. These all run as separate
LaunchDaemons that run with root privileges. The rest of the program runs with the privileges of the normal user. The
LaunchDaemons are configured here:
$ ls -al /Library/LaunchDaemons/com.vmware.* -rw-r--r-- root 558 /Library/LaunchDaemons/com.vmware.DiskHelper.plist -rw-r--r-- root 536 /Library/LaunchDaemons/com.vmware.IDHelper.plist -rw-r--r-- root 586 /Library/LaunchDaemons/com.vmware.KextControlHelper.plist -rw-r--r-- root 562 /Library/LaunchDaemons/com.vmware.MountHelper.plist -rw-r--r-- root 562 /Library/LaunchDaemons/com.vmware.VMMonHelper.plist
To facility communication between these daemons Apple recommends to use a technology called XPC. XPC is an inter-process communication framework build on top of Mach. The main reasons for using XPC are privilege separation and stability. One of the features provided by XPC is the ability to filter incoming connections. We will see how VMware uses this later to (try) to block certain connections.
Looking at the content of a
LaunchDaemon script shows it registers an XPC service called
com.vmware.IDHelper. The other daemon register services with similar names:
$ cat /Library/LaunchDaemons/com.vmware.IDHelper.plist <plist version="1.0"> <dict> <key>Label</key> <string>com.vmware.IDHelper</string> <key>MachServices</key> <dict> <key>com.vmware.IDHelper</key> <true/> </dict> <key>Program</key> <string>/Library/PrivilegedHelperTools/com.vmware.IDHelper</string> ...
Any user can connect to the XPC services registered above. That is not an issue in itself, but since the services run as root it makes them an interesting target for attackers. Great care should be taken in handling the data. The most critical step is to decide if an incoming connection is trusted or not. At this point the service should deny any unauthorized connections.
Unfortunately, the Apple documentation does not show the correct way of doing this. Even more so, to actually do so securely developers need to go through some trouble and use a private symbol. you can think of this as an undocumented feature. Not surprisingly this resulted in many bugs in many products before. And as we will see, VMware Fusion has a similar issue.
When a client connects to a VMware LaunchDaemon it always starts by verifying the incoming connection:
If any of these steps fails the request is rejected.
The vulnerability lies in the way the executable for a pid is resolved. The implementation is vulnerable to a race condition. An attacker can send a malicious request, quickly swap his process with one that passes all the remaining verifications, and pass the checks.
Decompiled code from
At the moment the function
proc_pidpath is called the pid might belong the a different process. This is shown in the following diagram:
The important steps are marked with numbers:
The vulnerability is the time between check (1) and (3). An attacker can race this window and try to swap out the binary at (2).
The vulnerability allows any user to call functions exported by the LaunchDaemons. This is already interesting, but further exploitation depends on the functions that the programs export. This exploit uses a function exported by
collectSupportInformation. This particular function generates problem reports, and does so by calling an external program:
The external program used to create the reports is supplied by an attacker. It might look like an attacker can just supply any program he wants, but if you look carefully in the snippet above you can see VMware performs additional checks on the program. Even though the client should already have been verified as this point, the daemon still doesn't trust the input and first verifies the code signature of the program. This is a good example of defense-in-depth, and more work it required to get code execution.
The exploit uses a second bug: a command injection in the script
/Applications/VMware Fusion.app/Contents/Library/VMware Fusion Problem Reporter.tool. This script is signed by VMware and therefore passes all the checks. At line 365 this tools evaluates one of the arguments:
if [ ! -z $DEFUSER ]; then printf -v HOME %s $(eval echo ~$DEFUSER) fi
By setting a username that includes back-ticks this results in code execution. The script will continue executing after that and start generating error reports. To prevent this the exploit sets some additional arguments to trigger a special code path at line 408 to prevent further execution of the script:
# Cancel running support tool if [ "$CANCEL_PID" ]; then printf "Cancelling process %08x\n" "$CANCEL_PID" /bin/kill -9 "$CANCEL_PID" exit # Use the status of the last command. fi
If you are developing an XPC service and want to do so securely there are some good (unofficial) resources available. Objective Development has a great blog post on how they ran into a similar issue and the process they went through to fix it.