July 27, 2020·6 min read·engineeringdockersecuritymacos

Docker Desktop repairs client verification in its privileged Mac helper

CVE-2020-15360 concerns a local caller reaching a root helper. The July patch shows why Desktop security includes host services outside the Linux virtual machine.

A local socket is a communication mechanism, but its presence alone does not establish that the caller should receive privileged service. CVE-2020-15360 concerns that distinction in Docker Desktop's Mac helper, com.docker.vmnetd. Today's 2.3.0.4 release notes record a fix for the helper's privilege-escalation vulnerability. The CVE description, published in June, identifies missing client verification in Desktop 2.3.0.3.

The affected component is part of the host application. A discussion limited to the Linux container runtime would miss the relevant boundary. The Mac application needs services on macOS as well as a Linux environment for its containers. Some of those services have privileges that ordinary application processes lack, which makes their callers part of the security design.

I value Docker's transferable images and repeatable build descriptions. Desktop makes those tools practical on a Mac by supplying the surrounding environment. That convenience also makes the host-side implementation worth examining independently of the images it runs. This vulnerability concerns the authority of that implementation, rather than a change to the Dockerfile format or an application dependency.

The helper is a separate privileged process

Docker's Mac FAQ at the time of the release describes com.docker.vmnetd as a privileged helper started by launchd. It runs in the background and can remain after the user quits Docker.app. That process lifetime is useful context. Closing the visible application is not the same operation as removing every service installed to support it.

The researcher's June analysis describes the helper running with root authority and accepting communication through a local socket. It reports that the communication process does not verify the client. The defect therefore concerns a less privileged local process asking a more privileged process to perform an operation on its behalf.

A helper can be small and still require a precise caller policy. Restricting the privileged code to a separate process reduces how much code needs that authority. It does not establish who may invoke the process or what arguments are acceptable. Those checks are necessary at the boundary between the ordinary application and the helper, where a request acquires the ability to affect privileged host state.

Process lifetime makes that distinction concrete. The FAQ describes the helper as waiting for Docker.app to connect, but the security question is whether the implementation can establish that a particular connection deserves the same treatment. Expected and actual clients are different pieces of information. A service description naming Docker.app does not enforce that expectation.

The handshake carries protocol information

The researcher separates the connection handling into a handshake and a command-dispatch path. The handshake reads an initialization message containing a flag, a version and a client hash. It then responds and reads a requested command. The analysis identifies a command handler that selects a callback according to the command it receives.

Those fields explain how a caller can speak the protocol. They do not, on their own, explain how the helper determines that the caller is authorized. A version can establish compatibility between participants without establishing either participant's identity. Similarly, a client-supplied value called a hash needs an account of what it proves and how the receiver validates it. The published finding is that client verification is missing.

This distinction matters when reading reverse-engineered protocol details. A structured initialization exchange may look substantial because it contains several fields and stages. Security depends on the properties the exchange actually establishes. Accepting a well-formed request and recognizing a command are necessary for useful communication, but neither operation alone grants the requester the right to perform privileged work.

The public account gives enough information to locate the defect without requiring a reader to reproduce an exploit. It describes a root process, a local request path and absent client verification. That combination explains why the issue is classified as privilege escalation. It does not imply that an arbitrary remote network client can reach the same helper directly.

Installation behavior becomes security-relevant

The research identifies an installation-related command handler for symbolic links. It says the handler accepts installation-path information, sets links in /usr/local/bin to the corresponding programs, and later changes file permissions through a helper function. Its demonstration reports modifying the permissions of a test file under /var/log. That is the researcher's reported result, rather than an incident involving an application container.

This connects the missing caller check to an operation with consequences on the host filesystem. Installation tasks are often privileged because they configure locations used outside a single user's application directory. When those tasks are exposed through a helper protocol, the caller policy and argument handling determine who can cause them to run. The command's normal administrative purpose does not limit who can attempt to request it.

The demonstration also helps distinguish the finding from a general claim that Docker containers are privileged. In the report, the caller communicates with a macOS helper. The resulting permission change occurs in the host filesystem. Neither fact requires treating every container process as equivalent to host root, and the vulnerability description does not establish that equivalence.

I find this separation useful because it identifies the component that needs repair. Rebuilding an application image would not replace a privileged helper installed with Desktop. The vendor's fix is a Desktop release, and the relevant inventory includes the version of that host application.

The Mac environment has several permission models

Docker's contemporary osxfs documentation gives another example of why those components need separate treatment. It says osxfs does not run as root and can access only filesystem resources available to the Desktop user. The document also says it performs no additional access checks beyond that model. Its account of file sharing is different from the root authority described for vmnetd.

That difference prevents an overly broad description of Desktop as one uniformly privileged process. File sharing and privileged installation services have different responsibilities and different rights. An engineer evaluating a defect needs to identify the process involved and the authority attached to its operation. A claim about osxfs cannot establish the caller policy of vmnetd, just as a vmnetd vulnerability does not describe every file-sharing access.

The same FAQ describes HyperKit as a userspace hypervisor built on the macOS Hypervisor framework. Docker therefore documents multiple parts of the environment even before considering an application running inside a container. Understanding which part produced a security finding helps determine which update addresses it and which assumptions the finding actually challenges.

What the July release establishes

The release notes name the local privilege-escalation fix and its CVE. They do not describe the replacement client-verification algorithm. It would go beyond the available account to claim a particular certificate check, peer-credential rule or protocol revision as the repair. The defensible statement is that Desktop 2.3.0.4 contains the vendor's fix for the vulnerability identified in 2.3.0.3.

Other entries in the release include a correction for startup with expired Kubernetes certificates and a HyperKit interaction with osquery that caused excessive CPU use. Those are separate maintenance items in the same Desktop update. They reinforce the practical point that the installed product includes a managed environment with host integrations, rather than only a container engine binary.

Docker's installation documentation from this period directs users to About Docker to verify the installed release. It lists the Engine, CLI and Compose among the bundled tools. These version relationships matter when identifying the repaired product. A command-line client version describes one bundled program, while the release note identifies the Desktop distribution containing the helper fix. Recording the Desktop version connects the machine's installed environment to the vendor's stated remediation without inferring helper behavior from the version of an unrelated component.

For the privilege-escalation issue, the decisive relationship is between the local requester and the root helper. The public analysis identifies a request path through which that relationship was insufficiently checked. The July update repairs the affected Desktop component, while the original disclosure supplies the mechanism needed to understand why a local helper belongs in the security assessment of a developer's container environment.