Security1 publisher2 min readPublished
JFrog says a stock Parallels Desktop install hands any local user a root shell
JFrog found that an unprivileged account on a Mac running Parallels Desktop 26.4.0 can reach the root dispatcher over a world-writable socket and run code as uid 0 through argument injection in the appliance installer.
The Watch · Security desk

What happened
- JFrog found that an unprivileged local user on a Mac running Parallels Desktop 26.4.0, build 57513, on Apple silicon can execute code as root through the prl_disp_service daemon.
- A quote in the appliance installer's parent-path argument closes the tar command early and injects --use-compress-program=, making macOS tar run the attacker's script as uid 0.
- None of this needs a running VM, because the launch daemon starts the privileged service at load on a stock install.
Compiled by The WatchSomething wrong?How this is made
Why it matters
- capability Any account already on the host, a coworker login, a CI agent, or resident malware, gains a reliable local escalation to root without admin rights or a Parallels signature.
- constraint The payload is a poisoned argv flag rather than a shell metacharacter, so detection rules that watch for shell injection will not catch it.
- decision Defenders have to treat older builds and the App Store Edition as in scope until a patched build is confirmed, because JFrog did not regression-test them.
The chain has three links. A default install supplies all three. [1]
The dispatcher daemon, prl_disp_service, runs as root because it installs appliances, registers VMs, and starts networking helpers on the host. [2] Its Unix socket at /var/run/prl_disp_service.socket sits at mode 0777, so any process can connect. [3] JFrog tested this on Desktop 26.4.0, build 57513, on Apple silicon. [4] A running VM is not required; the launch daemon starts the service at load. [5]
Authentication is the second link. The login call, PrlSrv_LoginLocal, trusts the peer credentials the kernel supplies for the socket connection: uid, gid, sometimes pid. [6] It does not check a Parallels Team ID, so an unsigned Python client logs in, and a non-admin account logs in the same way. [7] JFrog's proven path works when the daemon reports the caller is not a local administrator. [8]
The third link is the appliance installer. The SDK call PrlSrv_InstallAppliance takes an optional sVmParentPath argument that chooses where the new VM bundle is provisioned. [9] That string flows into a tar command line built with Qt string splitting. [10] A quote in the parent path closes the intended argument early, and the leftover text becomes a --use-compress-program= flag. macOS tar then runs the attacker's script as uid 0. [11]
This is argument injection. There is no /bin/sh in the path, so metacharacters like a semicolon do nothing. [12] The payload is a feature flag of the child binary, tar's own --use-compress-program, which names a program tar will execute. [13]
JFrog did not regression-test older builds for the writeup. It said to treat any Desktop install that still exposes the same InstallAppliance extract template and the world-writable dispatcher socket as in scope. [14] The App Store Edition may start its services differently, but JFrog said the core risk is the same class of root helper IPC. [15]
To check exposure, read the socket mode and the product version. [16] If the socket is srwxrwxrwx and Desktop is at or near 26.4.0 build 57513, JFrog says to assume exposure until a patched build is confirmed. [17] The company noted the tool sits on a large number of developer and enterprise Macs. [18]
What to watch
- A patched Parallels build that tightens the socket permissions or adds Team ID validation on PrlSrv_LoginLocal, and a CVE assignment.
- Confirmation of whether builds older than 26.4.0 and the App Store Edition expose the same extract template and socket mode.
- Any in-the-wild use of the chain as a post-compromise escalation step on developer or enterprise Mac fleets.