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.
Why it matters
- What's now possible 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.
- What it limits 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.
- Who has to decide 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.
Next story · Security · Oct 7
LMCache's multiprocess cache server runs attacker code from one unauthenticated messageConfirmed2 publishers
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. [17] 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. [18] The company noted the tool sits on a large number of developer and enterprise Macs. [16]
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.
Clarity's read
What the record supports and how the coverage leans. The claims behind it follow.
Reality
- Evidence80
- Adoption42
- Hype gap+10
- Incentives55
- Confidence76
Perspective Coverage
3 publishers- Builder
- Builder 34%
- Operator
- Operator 48%
- Investor
- Investor 18%
Claim ledger
Ranked by verification strength, evidence, and original report placement.
- [1]
ParaShells needs three things a normal Parallels Desktop install already provides by default.
- [2]
prl_disp_service is the privileged host daemon behind Parallels Desktop; it installs appliance packages, registers VMs, and starts networking helpers, and runs as root because those jobs touch host networking and archives as root.
- [3]
Local clients connect to /var/run/prl_disp_service.socket, whose mode is srwxrwxrwx (0777), so any process can connect.
- [4]
JFrog found the issue while testing Parallels Desktop 26.4.0, build 57513, on Apple silicon (macOS arm64).
- [5]
A running VM is not required; on a stock install the launch daemon starts prl_disp_service at load.
- [6]
PrlSrv_LoginLocal authenticates using peer credentials from the kernel, typically uid, gid, and sometimes pid.
- [7]
There is no Parallels Team ID validation for the login call, so PrlSrv_LoginLocal succeeds for an unsigned Python client and the same login works for a non-admin user.
- [8]
The proven path works when PrlUsrCfg_IsLocalAdministrator reports 0, that is, when the caller is not a local administrator, and no Parallels code signature is required on the client.
- [9]
The SDK exposes PrlSrv_InstallAppliance(hServer, hAppCfg, sVmParentPath, nFlags); the optional sVmParentPath string chooses where the new VM bundle is provisioned.
- [10]
The appliance unpack path builds tar arguments using Qt string splitting.
- [11]
A quote in the parent path injects --use-compress-program=, and macOS tar runs the attacker's script as uid 0.
- [12]
The exploit is argument injection, not classic command injection: there is no shell, so tokens like a semicolon and id do nothing useful.
- [13]
In argument injection the payload is a feature flag of the child binary, here macOS tar.
- [14]
JFrog did not regression-test every older build and said to treat any Desktop install that still exposes the same InstallAppliance extract template and world-writable dispatcher socket as in scope.
- [15]
The App Store Edition may differ in how services start, but JFrog said the core risk is the same class of root helper IPC.
- [16]
Parallels Desktop is on a large number of developer and enterprise Macs.
- [17]
JFrog's detection guidance is to read the product version and check whether /var/run/prl_disp_service.socket is world-writable.
- [18]
If the socket mode is 0777 (srwxrwxrwx) and Desktop is at or near 26.4.0 (57513), JFrog says to assume exposure until a patched build is confirmed.
Sources
3 independent publishers whose own reporting we read for this story.
- helpnetsecurity.comParallels Desktop flaw hands any local user root on a Mac (CVE-2026-90894)
1 article · September 16, 2026
- jfrog.comParaShells: Parallels Desktop Turns Appliance Install Into a Root Shell
1 article · September 15, 2026
- thecyberexpress.comNew Parallels Desktop Flaw Lets Local Users Seize Root Control of Macs
1 article · September 17, 2026
Topics and entities
Topics
- Local Privilege Escalation
- macOS Security
- Virtualization Software
- Argument injection in Git tooling
Entities
- JFrog
- Yuval Moravchick
- prl_disp_service
- Parallels Desktop