Aug 18, 2026·6 min read·193 visits
A TOCTOU race condition in the directory-scanning phase of 'docker cp' combined with lexical path validation errors on the client host allows a compromised container to write arbitrary files outside the extraction destination, potentially causing full host system takeover.
CVE-2026-17106 (CopyEscape) is a container-to-host arbitrary file-write vulnerability within Docker's archiving and extraction library moby/go-archive. By utilizing a Time-of-Check to Time-of-Use (TOCTOU) race condition during the file-walking stage inside a running container, a malicious container process can force the host engine to produce a compromised tar stream. During client-side extraction, the Docker CLI resolves directory entries through absolute symbolic links, resulting in arbitrary file creation or modification on the host system.
CVE-2026-17106, colloquially known as CopyEscape, represents a container escape vector through the archiving and extraction utilities of the Docker ecosystem. The vulnerability resides within the moby/go-archive dependency, which is heavily relied upon by components such as docker cp and Docker Sandboxes' sbx cp commands. Under typical conditions, these utilities facilitate the copying of files between the virtual container environment and the physical host system.
The underlying data transfer operates using a producer-consumer model where files are walked, packed into a tar archive stream by the Docker daemon inside the container context, and then decoded and unpacked on the host client filesystem. This architecture assumes the container filesystem remains static during the walk and serialization phases.
An attacker controlling a running container can exploit this operational model. By introducing an on-the-fly path substitution during the serialization phase, the attacker can force the host client to extract arbitrary payloads directly onto its native filesystem, inheriting the system privileges of the local user running the client CLI utility.
The vulnerability stems from two independent implementation flaws: a Time-of-Check to Time-of-Use (TOCTOU) race condition on the daemon-side path walk, and a client-side directory containment failure during path verification. On the daemon side, file selection for packaging is executed using directory scanning helpers like filepath.WalkDir. While this traversal is underway, the container runtime environment remains active, allowing concurrent filesystem updates by container-bound processes.
On the host client side, path verification is performed lexically before filesystem writes occur. The extraction logic computes target locations by concatenating the extraction destination with the raw file headers. If the generated path resides mathematically within the designated destination structure, the extraction logic accepts the operation.
This validation method fails because lexical checks do not resolve symbolic links on disk. An attacker can set up a subdirectory, wait for the daemon to inspect the path, and then replace that subdirectory with an absolute symbolic link pointing to a host directory. Because the lexical check passes for child entries under the parent directory path, the host client follows the newly created symbolic link during physical file extraction, resulting in out-of-boundary file creation.
The original, vulnerable logic in moby/go-archive verified directory containment using pure string manipulation. The string comparison evaluated whether the target path contains the destination directory path as a prefix without verifying intermediate links on the physical filesystem:
// INSECURE: Lexical verification bypass
dstPath := filepath.Join(dest, hdr.Name)
rel, err := filepath.Rel(dest, dstPath)
if strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
return breakoutError(fmt.Errorf("%q is outside of %q", hdr.Name, dest))
}If the archive contains a symbolic link named escape pointing to /usr/bin, and a child element escape/runc, the lexical analysis verifies both dest/escape and dest/escape/runc as structurally internal to dest. During the physical write, however, the OS resolves the symbolic link escape on disk, allowing the payload file to overwrite the physical host binary /usr/bin/runc.
To correct this defect, the remediation replaces lexical validation with an OS-enforced directory sandboxing model using the modern os.Root API introduced in Go 1.26. The patched implementation opens the destination target first and isolates all subsequent file creation routines to this file descriptor:
// SECURE: Enforced filesystem sandboxing via os.Root
root, err := os.OpenRoot(dest)
if err != nil {
return err
}
defer func() { _ = root.Close() }()
// Writes are restricted using file-descriptor-relative operations
file, err := root.OpenFile(dstPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, hdrInfo.Mode()&0o777)By leveraging openat2 with the RESOLVE_BENEATH flag under Linux, the operating system kernel prevents file operations from traversing symbolic links pointing outside the open root folder. Any attempt to traverse an absolute symbolic link triggers a directory escape error and halts extraction.
An exploitation chain requires a running container under the attacker's control and active user interaction on the host. The attacker prepares the internal container environment by creating a nested file structure containing a large dummy file, a target subdirectory, and a staged absolute symbolic link. The large file acts as an artificial delay during the serialization phase.
The attacker runs an internal filesystem monitor utilizing inotify watches. When a host administrator executes docker cp to extract files, the daemon begins scanning the container filesystem and reading the large dummy file. This interaction triggers the inotify open event, signaling the monitor process to initiate the race.
The monitor process immediately invokes rename system calls to swap the target directory with the prepared absolute symbolic link pointing to a host destination like /usr/bin. The daemon packages the symbolic link alongside the nested payload files. Upon receiving this stream, the host CLI extracts the absolute link, follows it, and overwrites target files on the host.
The impact of CVE-2026-17106 is highly critical, with potential for arbitrary host code execution. If the host administrator runs the client utilities as root, an attacker can modify host binaries. For example, overwriting /usr/bin/runc grants complete command execution as root the next time a container is started or stopped on the host.
If the executing host user has limited privileges, the write capabilities are restricted to the directories owned by that user. However, this still permits high-impact actions, such as writing to local user shell profiles like ~/.bashrc or ~/.zshrc to achieve privilege escalation.
The CVSS v4.0 metrics yield a base score of 7.1. While confidentiality, integrity, and availability impacts are elevated, the requirement for active host user interaction limits spontaneous remote execution vectors.
Remediation requires upgrading container client and engine environments to versions incorporating the moby/go-archive 0.3.0 patch. Users must update Docker Desktop to version 4.86.0 or higher, and Docker Engine to 29.7.0 or higher.
Temporary workarounds should be applied if immediate patching is not possible. Administrators should avoid running docker cp against containers executing unverified workloads. Restricting the execution of host-side copy commands to non-root users ensures any malicious write remains contained within normal user permissions.
Additionally, systems should run monitoring utilities like auditd to identify anomalous modifications to crucial container helper files. Configuring logging mechanisms to report file write operations on paths such as /usr/bin/runc provides immediate detection capabilities.
CVSS:4.0/AV:L/AC:L/AT:P/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
go-archive moby | < 0.3.0 | 0.3.0 |
Docker Desktop Docker | < 4.86.0 | 4.86.0 |
Docker Engine Docker | < 29.7.0 | 29.7.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-59 |
| Attack Vector | Local (AV:L) |
| CVSS v4.0 Score | 7.1 |
| Exploit Status | Proof of Concept (PoC) |
| Vulnerability Class | Improper Link Resolution ('Link Following') |
| Impact | Arbitrary File Write / Privilege Escalation |
The application attempts to access a file based on a filename, but it does not properly prevent that file from being a symbolic link or hard link that points to an unintended external resource.
An information disclosure vulnerability in Contao CMS allows unauthenticated site visitors to view protected page titles, URLs, and text excerpts through search queries when protected page indexing is disabled after previously being enabled.
In Vikunja prior to version 2.6.0, relation creation via the CalDAV endpoint fails to invoke the TaskRelation.CanCreate authorization check. This missing access control allows an authenticated user to establish unauthorized relationships and perform write operations against any task, provided its unique identifier (UID) is known.
A cross-project information disclosure vulnerability in Vikunja allows authenticated users with read access to one project to view private task details from unauthorized projects via subtask expansion parameters.
An information disclosure vulnerability in Vikunja allows authenticated users with read access to a task to expose private email addresses of assigned users through the API task assignees endpoint due to an unmasked database query.
An authorization bypass vulnerability in Vikunja versions prior to v2.6.0 permits authenticated users to delete relationships between tasks across project boundaries without requiring read or write authorization for the target related task.
A path traversal vulnerability in Shiny for Python (posit-dev/py-shiny) versions 1.4.0 through 1.6.3 allows unauthenticated remote attackers to read arbitrary files and traverse directories via crafted _state_id_ query parameters.