CVE-2026-17106 is a container-to-host arbitrary file-write vulnerability in Docker’s docker cp implementation. The flaw is not a conventional container escape in which a process inside a container directly gains access to the host kernel or host filesystem which is dubbed as “CopyEscape”. Instead, an attacker-controlled container abuses the archive pipeline used by docker cp so that the Docker CLI, running on the host, performs a filesystem write outside the destination selected by the user.
The vulnerability chains two separate filesystem problems. First, a live container filesystem can be modified while Docker is walking it, allowing the archive producer to observe the same pathname as different filesystem object types at different points in time. Second, the affected archive extractor does not correctly enforce the destination boundary when handling the resulting symlink structure. Together, these conditions produce a host-side arbitrary file create or overwrite primitive.
The impact is determined by the privileges of the process executing docker cp. An ordinary developer may be able to overwrite files writable by that account. A privileged CI worker, administrator, or process invoking Docker with elevated privileges can potentially overwrite privileged files. Imperva demonstrated replacement of /usr/bin/runc, after which a subsequent Docker lifecycle operation executed the replacement and produced root-level code execution.
Docker Engine/CLI 29.7.2 and Docker Desktop 4.86.0 contain the final fixes described in the supplied disclosure. Docker Sandboxes 0.38.0 also addresses the corresponding sbx cp copy-out path.
The vulnerable docker cp architecture

The triggering command is ordinary:
docker cp container:/path/to/file.txt ./file.txt
The expected security boundary is straightforward: /path/to/file.txt is inside the container and ./file.txt is the destination selected by the user.
Internally, however, a container-to-local copy is an archive operation.
Docker’s daemon walks the requested path inside the container filesystem and serializes the result into a tar archive. The Docker CLI then receives that archive and extracts it on the client machine. This means the container controls input to an archive extractor running with the permissions of the local Docker CLI process.
The security model therefore requires two independent properties:
- The archive producer must generate a coherent representation of the source filesystem.
- The extractor must guarantee that every resulting filesystem operation remains inside the requested destination.
CVE-2026-17106 breaks both properties in one operation.
📬 Stay Ahead of Cyber Threats
Get the latest cybersecurity news, critical vulnerabilities, threat intelligence, tutorials, and exclusive giveaways delivered straight to your inbox. No spam. Unsubscribe anytime.
Subscribe to the Newsletter →The affected source code path used github.com/moby/go-archive version v0.2.0 in the relevant Moby and Docker CLI trees. The disclosure identifies containerArchivePath as the Moby path responsible for creating the tarballer and executing the archive operation against the container filesystem.
The source-side TOCTOU condition
The first vulnerability exists because Docker is walking a filesystem whose contents can continue to change.
The Docker container object can be locked internally while the archive is being created, but that does not freeze processes executing inside the container. Those processes can continue to rename files, replace directories and create symbolic links while Docker traverses the filesystem.
The relevant archive walk uses:
filepath.WalkDir
WalkDir examines directory entries while recursively traversing the tree. The important issue is that the traversal decision and the subsequent archive operation do not necessarily operate on one immutable filesystem state.
The affected flow can be simplified conceptually to:
WalkDir(path) | | sees "escape" as directory vdescend into "escape" | | filesystem changes vaddTarFile(path) | | sees "escape" as symlink vemit symlink entry
The research identifies addTarFile as the function that subsequently examines the pathname and constructs the tar header. It uses Lstat to determine the object represented by the pathname when the archive entry is generated.

The problem is therefore a classic time-of-check/time-of-use condition.
At time T1, the pathname may represent:
/watched/file.txt/escape | +-- directory
At time T2, the same pathname may represent:
/watched/file.txt/escape -> /usr/bin | +-- symbolic link
The pathname string is identical. The filesystem object behind it is not.
WalkDir has already made a traversal decision based on the first state. addTarFile can subsequently make an archive-entry decision based on the second state.
The resulting tar stream can consequently contain information representing two different filesystem states.
Making the race reliable
A blind race between Docker’s filesystem walker and a malicious container process would normally be difficult to exploit consistently. The research instead constructs a controlled timing mechanism.
The proof of concept uses an LD_PRELOAD interposer so that processes inside the container can be made to see /watched/file.txt as a regular file while Docker sees the underlying path as a directory. The regular-file view is redirected to a hidden backing file:
/watched/file.txt/watched/.file.txt.regular
The attacker then controls the directory tree underneath the path Docker is actually traversing.
The relevant conceptual state initially looks like:
/watched/└── file.txt/ └── escape/ └── ...
Docker’s walker reaches escape and records it as a directory.
The attacker then performs two rename operations:
escape directory | | rename vstaged directory moved asidestaged symlink | | rename vescape -> /usr/bin
The research widens the timing window by placing a large file immediately before the pivot directory. Filesystem notifications are used to determine when Docker has opened that file, indicating that the traversal has reached the desired location. The attacker then performs the rename operations at the controlled point.
The result is not simply a random race.
Docker first has a directory entry saying:
escape = directory
The attacker changes the filesystem so that:
escape -> /usr/bin
Docker’s subsequent addTarFile operation can then emit the symbolic link while the earlier traversal state still causes the walker to descend through the pathname.
The resulting archive contains two logically contradictory entries:
file.txt/escape
as a symbolic link, and a child such as:
file.txt/escape/runc
as though escape were still a directory.
That is the malformed archive state required by the second half of the exploit.
The archive extraction flaw
The archive itself is not sufficient to achieve the host write. The client-side extractor must incorrectly process it.
For container-to-local copies, the Docker CLI reaches the archive extraction implementation through:
archive.CopyTo
The vulnerable extraction path in moby/go-archive did not adequately prevent traversal through an intermediate symlink.
The critical error was a mismatch between the value used for validation and the value used for the actual filesystem operation.
The affected logic constructed a path using:
filepath.Join(...)
and validated that constructed path.
The subsequent symlink creation used the original archive link target through:
os.Symlink(...)
In simplified form, the security-sensitive distinction is:
targetPath := filepath.Join(destination, hdr.Linkname)// validation operates on targetPathos.Symlink(hdr.Linkname, path)
The exact source surrounding this logic is identified in the disclosure as the affected symlink-extraction code in moby/go-archive v0.2.0. The important point is that the validated representation and the value supplied to os.Symlink were not equivalent.
Consider:
destination:/safe/output
and an extraction path:
/safe/output/file.txt/escape
with an archive link target:
/usr/bin
A lexical construction using:
filepath.Join(...)
can produce a path that appears associated with the extraction destination, while the original absolute link target supplied to:
os.Symlink(...)
still means /usr/bin to the operating system.

The check therefore answers one question while the filesystem operation performs another.
That is the fundamental extraction bug.
Why string-based path containment fails
Filesystem containment cannot safely be reduced to string comparison.
For example:
/safe/output/file.txt
and:
/safe/output-elsewhere/file.txt
share a textual prefix, but the second path is not located below the first directory.
More importantly, symbolic links make lexical containment fundamentally different from resolved filesystem containment.
Suppose the extractor believes this path is safe:
/safe/output/file.txt/escape/runc
If:
/safe/output/file.txt/escape
is an ordinary directory, the final object is inside the extraction tree.
If it is instead:
/safe/output/file.txt/escape -> /usr/bin
the same textual pathname can resolve to:
/usr/bin/runc
Nothing in the string:
file.txt/escape/runc
indicates ../ traversal.
The escape occurs because one of the path components has changed the meaning of the remaining pathname.
This is why checking for ../ alone cannot provide a complete archive-extraction security boundary.
The child-entry write
After the malicious symlink entry has been processed, the extractor processes the child entry:
file.txt/escape/runc
The archive name itself contains no lexical ../ traversal. Docker can therefore regard it as a path below the requested destination.
But the filesystem state created by the previous archive entry changes the interpretation of escape.
The effective resolution becomes:
/safe/output/file.txt/escape/runc | v escape -> /usr/bin | v /usr/bin/runc
When the extractor creates the child, the kernel follows the intermediate symlink.
The attacker-controlled bytes therefore land at:
/usr/bin/runc
rather than inside:
/safe/output/
The complete chain is:
1. WalkDir sees "escape" as a directory.2. Attacker replaces "escape" with: /usr/bin3. addTarFile sees "escape" as a symlink.4. The tar archive receives the symlink entry.5. WalkDir retains its earlier directory traversal decision.6. The archive also receives: file.txt/escape/runc7. The CLI validates the extraction path lexically.8. The CLI creates the attacker-controlled symlink.9. Extraction of: file.txt/escape/runc follows the symlink.10. The write lands at: /usr/bin/runc
The supplied research explicitly identifies this sequence as the mechanism that turns the two weaknesses into a host filesystem write.
From arbitrary file write to root execution
The arbitrary write is the primitive. Privilege escalation depends on what the Docker CLI can write.
If the Docker CLI runs as an ordinary user, the attacker is restricted by that account’s filesystem permissions.
Potential targets include:
shell startup filesSSH configurationuser-level executablessource codecloud configurationuser persistence locations
On macOS, the research specifically identifies:
~/Library/LaunchAgents
as a potential persistence location. A malicious modification to a shell startup file could execute attacker-controlled content when the user later starts a shell.
On Linux, the impact increases if the Docker CLI is executed with root privileges.
The demonstrated target was:
/usr/bin/runc
The attacker replaces the runtime executable with attacker-controlled content. A later Docker lifecycle operation invokes runc, causing the replacement to execute with the privileges available to that operation.
Conceptually:
docker cp | voverwrite /usr/bin/runc | vlater Docker lifecycle operation | vexecute /usr/bin/runc | vattacker-controlled code | vroot
The important distinction is that CVE-2026-17106 itself provides the file-write primitive. The subsequent execution of a privileged binary turns that primitive into code execution. The container does not directly acquire host root privileges through the Docker daemon.
Docker Desktop changes the attack boundary
Docker Desktop runs the Docker engine inside a Linux virtual machine, but the vulnerable extraction operation for a container-to-local copy occurs in the client-side environment.
On macOS, the sequence is therefore approximately:
Malicious container | vDocker Engine inside Linux VM | | tar archive vDocker CLI on macOS | | vulnerable extraction vmacOS filesystem
The container does not have to escape the Linux VM.
Instead, it causes the host-side Docker CLI to perform the filesystem operation after the archive crosses the VM boundary.
This makes host-side developer files relevant even though the Docker daemon itself is isolated inside the Desktop VM. The supplied research demonstrated potential targets including shell configuration, SSH configuration, source trees, user executables and:
~/Library/LaunchAgents
on macOS.
The supplied research validated the issue on:
Docker Engine 29.6.1Docker Desktop 4.81.0 (232925)
Why CI/CD systems are exposed
The vulnerability becomes particularly relevant when an automated system copies data from attacker-controlled containers.
Typical operations include:
docker cp container:/build/artifacts ./artifacts
or equivalent collection of test results, logs and forensic data.
The dangerous property is that the attacker does not necessarily need to trigger the vulnerability immediately after container creation.
The malicious container can prepare its filesystem and wait.
A CI job later performs:
docker cp
and the copy operation becomes the trigger.
The same model applies to incident response. An analyst may intentionally interact with a known-compromised container to retrieve evidence. If that evidence collection is performed with a vulnerable Docker CLI, the operation intended to extract data can instead cause attacker-controlled filesystem operations on the analysis machine.
The supplied research specifically identifies developer workstations, CI systems, privileged automation and incident-response workflows as relevant environments.
Docker Sandboxes and sbx cp
The vulnerability also extends to Docker Sandboxes.
The affected copy-out operation is:
sbx cp
Docker Sandboxes 0.38.0 fixed a destination-escape flaw in sandbox copy-out under CVE-2026-17106.
The security model is similar: data produced inside an isolated environment is transferred to the host through a client-side filesystem operation.
That means a compromised or untrusted coding-agent sandbox cannot automatically be considered safe merely because it is isolated. The transfer mechanism itself becomes part of the security boundary.
Why stopping the container breaks the demonstrated race
The demonstrated source-side primitive depends on a live process being able to change the filesystem while Docker walks it.
Stopping the container removes that ability.
The disclosure therefore recommends stopping the container before copying files when an immediate upgrade is not possible:
docker stop <container>docker cp <container>:/path/to/file ./file
The first command prevents the running workload from continuing to perform the filesystem mutations required by the demonstrated TOCTOU condition.
This is a mitigation for the source-side race, not a replacement for upgrading. The archive extractor must still be treated as security-sensitive when handling untrusted archive content.
Privilege reduction matters
The final write is performed with the authority of the Docker CLI process.
That makes this workflow dangerous:
sudo docker cp <container>:/path ./destination
when the source container is untrusted.
The same applies to CI or maintenance jobs running the Docker CLI as root.
If a workflow only needs to retrieve files accessible to an ordinary account, running the complete copy operation as root unnecessarily increases the consequences of a client-side filesystem vulnerability.
The practical security model is:
Attacker-controlled container | v docker cp | vCLI privileges | vMaximum possible host impact
Therefore, least privilege directly limits the impact of the vulnerability.
Detection and forensic investigation
CVE-2026-17106 does not primarily present as a network exploitation event. The critical operation is local execution of:
docker cp
or, in the affected sandbox workflow:
sbx cp
Detection should therefore correlate Docker client activity with unexpected host filesystem modifications.
On Linux, unexpected changes to:
/usr/bin/runc
or other privileged executables should be treated as high-priority events, particularly when they coincide with Docker copy operations involving an untrusted container.
For developer systems, investigate unexpected modifications to:
~/.ssh/shell startup filessource repositoriesuser executable directories~/Library/LaunchAgents
and other files writable by the affected account.
The investigation should establish which container was the source of the copy, which user or service account ran the Docker CLI, the exact destination supplied to docker cp, and which host files changed around the time of the operation.
A compromised container should not be treated as trustworthy evidence merely because the analyst is only reading or copying files from it. The vulnerable workflow demonstrates why extraction itself can be an execution boundary.
Patch history
The supplied disclosure records the following remediation sequence.
On April 11, 2026, Imperva reported the vulnerability to Docker with technical details and reproduction information. Docker acknowledged the report on April 14 and confirmed the behavior on April 15.
The initial 90-day disclosure period elapsed on July 10 without a public patch. On July 14, Docker described two remediation tracks: hardening the extraction library as the primary security fix and hardening the source filesystem walk as defense in depth.
On July 24, Docker provided:
CVE-2026-17106GHSA-hfg8-hc9c-6c3h
and targeted an August 3 coordinated release.
On July 27, Docker confirmed that the source-walk TOCTOU would not receive a separate CVE and would instead be addressed as defense-in-depth hardening.
On July 30, the following were released:
moby/go-archive v0.3.0Docker Engine/CLI 29.7.0
The initial hardening introduced significant functional regressions. Docker subsequently requested another extension to the coordinated disclosure schedule while follow-up releases addressed those regressions.
On August 6, Docker Sandboxes 0.38.0 shipped its fix for the sbx cp destination-escape issue.
On August 10, Docker released Docker Desktop 4.86.0, bundling Docker Engine 29.7.2 and the corresponding Docker Desktop remediation.
Fixed versions
The final versions identified by the disclosure are:
Docker Engine / CLI: 29.7.2 or laterDocker Desktop: 4.86.0 or laterDocker Sandboxes: 0.38.0 or later
For the Docker CLI and Engine, version information can be checked with:
docker version
For Docker Desktop, the Desktop CLI provides:
docker desktop version
Docker documents docker desktop version as the command for displaying Docker Desktop CLI plugin version information.
Organizations should check the Docker CLI hosts, not only Docker daemon servers. The vulnerable extraction occurs in the client-side portion of the container-to-local copy path, so developer workstations and CI workers can be security-relevant even when the Docker daemon itself is centrally managed.
Technical root cause
At the source side, the vulnerability is fundamentally a filesystem TOCTOU condition:
WalkDir | | observes directory vattacker changes pathname | vLstat in addTarFile | | observes symlink varchive contains inconsistent state
At the extraction side, the fundamental problem is a mismatch between validated and actually used path representations:
archive link target | vfilepath.Join(...) | vvalidation
while the actual symlink creation uses:
hdr.Linkname | vos.Symlink(...)
The two operations do not establish the same filesystem guarantee.
The child entry then crosses the intended boundary through the previously created symlink:
destination/file.txt/escape/runc | v escape -> /usr/bin | v /usr/bin/runc
The source-side race creates the malicious archive state. The extraction flaw turns that state into a host filesystem operation.
Neither component should be analyzed in isolation.
Conclusion
CVE-2026-17106 is a container-to-host arbitrary file-write vulnerability caused by the interaction of a source-side filesystem race and a client-side archive-extraction flaw.
The attack begins inside a malicious container. While Docker’s archive walker is processing the container filesystem, the attacker changes a pathname from a directory into a symbolic link. Docker can retain the earlier directory traversal decision while later serializing the same pathname as a symlink. This creates an internally inconsistent tar archive containing both the symlink and a child entry beneath it.
The Docker CLI then extracts the archive. The vulnerable symlink handling validates a constructed path but creates the link using the original archive target. The subsequent child entry is resolved through the attacker-controlled symlink, allowing its contents to be written outside the selected extraction directory.
The final capability is:
container | vmalicious filesystem race | vinconsistent tar archive | vDocker CLI extraction | vsymlink traversal | varbitrary host file create/overwrite
The privilege boundary is the Docker CLI process. An ordinary user is limited to files that account can modify. A root-run Docker CLI can expose privileged system files, and the demonstrated replacement of:
/usr/bin/runc
shows how the file-write primitive can be converted into root code execution by a subsequent Docker operation.
The correct remediation is to upgrade to:
Docker Engine / CLI 29.7.2+Docker Desktop 4.86.0+Docker Sandboxes 0.38.0+
and to avoid privileged docker cp operations against untrusted or compromised running containers until patched.
The underlying lesson is specific and technical: archive extraction is a filesystem security boundary. Lexical checks, ../ filtering and string-prefix comparisons are insufficient when symbolic links and concurrent filesystem changes are involved. A secure implementation must ensure that the filesystem operation itself cannot escape the intended extraction root.
CVE-2026-17106 did not require the container to directly break Docker’s VM or namespace isolation. It made the trusted host-side copy mechanism perform the filesystem write instead.









