功能性 CVE-2026-17106 (CopyEscape) PoC:docker cp 中的 TOCTOU 竞态条件,可对 Docker 主机进行任意写入。Docker 实验环境 + inotify 监控 + LD_PRELOAD。macOS 变体无害,Linux 变体具有破坏性。
关于 CopyEscape(CVE-2026-17106)的教育性概念验证(PoC),这是
docker cp中的一个 TOCTOU 竞态条件漏洞,允许恶意容器在 Docker 主机上写入任意文件。灵感来自 masasron 的原创研究,他在博客 "CopyEscape: Taking Over Docker Hosts with docker cp" 中发现了该漏洞并进行了记录。
CVE-2026-17106 是 Docker daemon 在 docker cp <contenedor>:<ruta> <destino> 期间生成的 tar 文件解压流程中的一个竞态条件(TOCTOU)漏洞。影响 Docker CLI 29.6.1 及更早版本;自 29.7.0 起已修复。该 bug 位于 CLI(在主机上解压 tar),而非 daemon。
名为 CopyEscape 的攻击链展示出,攻击者仅凭一个正在运行且没有额外权限的容器,就能:
LD_PRELOAD 库拦截 open、openat、openat2、fopen、stat、lstat、fstatat、statx 以及 64 变体,将真实目录 /watched/file.txt/ 伪装成普通文件,欺骗容器内任何打开它的进程。aaa.txt,迫使 daemon 在生成 tar 时耗时足够长,从而让监控程序赢得竞态。inotify 监视 daemon 的访问,并在竞态窗口内通过两个带回滚(rollback)的原子 rename(2) 操作,将 escape/ 子目录替换为指向主机目标的绝对符号链接(macOS 上为 ,Linux 上为 )。最终结果是:以执行 docker cp 的进程的权限,在主机上任意写入文件。Linux 变体会覆盖 /usr/bin/runc,并在下一次 docker run / exec 时在主机上实现 以 root 身份执行代码。
本仓库包含的内容严格属于教育与防御性质。
runc:只能在带已验证备份的一次性虚拟机中使用。该 PoC 附带一个无害的 macOS 变体(写入 ~/pwnd 作为标记),其唯一用途是作为可复现的演示。
┌─────────────────────────────────────────────────────────────────────┐
│ 1. LD_PRELOAD camufla /watched/file.txt como archivo regular │
│ open/openat/openat2/fopen/stat/lstat/fstatat/statx → decoy │
│ docker exec ... cat /watched/file.txt → "top-level file" │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 2. Layout real dentro del contenedor │
│ /watched/file.txt/ │
│ ├── aaa.txt (16 MiB de 'B', cebo lento) │
│ ├── escape/ (directorio real inicial) │
│ ├── escape/<basename> (payload: runc o pwnd) │
│ └── .swap-escape (symlink → /Users/<usr> o /usr/bin) │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 3. docker cp pide tar al daemon │
│ daemon recorre /watched/file.txt/ y abre aaa.txt │
│ inotify detecta IN_OPEN sobre aaa.txt │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 4. Monitor pivot: dos rename(2) atomicos con rollback │
│ escape/ → .old-escape/ (backup) │
│ .swap-escape → escape/ (symlink al target del host) │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 5. Tar resultante contiene: │
│ escape → /Users/<usr> (symlink absoluto) │
│ escape/<basename> (entrada hija que atraviesa el symlink) │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 6. CLI vulnerable extrae symlink + hija │
│ crea escape → /Users/<usr> en el destino local │
│ extrae escape/<basename> A TRAVES del symlink │
│ escribe /Users/<usr>/<basename> en el host │
└──────────────────────────────────────────┬──────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ macOS: ~/pwnd con "COPYESCAPE_MACOS_DEMO" (demo inocua) │
│ Linux: /usr/bin/runc sobrescrito (RCE como root) │
└─────────────────────────────────────────────────────────────────────┘
该 PoC 面向 macOS 运行环境构建,需要:
PATH 中包含 curl、tar、git 和 docker。alpine:3.21。HOME 形如 /Users/<usuario>。macOS 变体不需要 root。Linux 变体需要 root 和一台一次性虚拟机。
运行脚本 run-poc.sh 会自动从 Docker 官方网站(download.docker.com)下载存在漏洞的 CLI 29.6.1 二进制文件。它不会修改系统中的 Docker Desktop 安装:使用当前的 daemon,但调用下载的二进制文件来执行 docker cp 操作。
POC CopyEscape (CVE-2026-17106)/
├── README.md ← Este archivo
├── LICENSE ← Licencia MIT
├── .gitignore
├── Dockerfile ← Dockerfile unificado (macos | linux)
├── entrypoint.sh ← Arranque del monitor sin LD_PRELOAD
├── monitor.c ← Monitor inotify unificado (una sola fuente)
├── watched_preload.c ← LD_PRELOAD con cobertura ampliada
└── run-poc.sh ← Runner robusto con reintentos
目录
cli-vulnerable/(已下载的 CLI 29.6.1)通过.gitignore从仓库中排除。如果该目录不存在,运行脚本会 自动下载它。
cd poc-v2
./run-poc.sh
运行脚本将:
copyescape-macos-pocv2:local。COPYESCAPE_HOST_HOME=$HOME 启动准备好的容器。/watched/file.txt 变为可读。docker cp。~/pwnd 是否已创建。./run-poc.sh 10 # 10 intentos (la carrera es probabilistica)
./run-poc.sh --cleanup # 5 intentos y borra la imagen al final
[run-poc] CLI vulnerable: 29.6.1
[run-poc] Intento 1/5 (contenedor copyescape-pocv2-NNNN-RRRR)
[run-poc] /watched/file.txt lee: top-level file
[run-poc] docker cp (CLI 29.6.1)...
[run-poc] EXITO en intento 1: se creo /Users/<tu-usuario>/pwnd
Contenido del marcador: COPYESCAPE_MACOS_DEMO
[run-poc] Logs del monitor:
[monitor] plataforma=macos target_dir=/Users/<tu-usuario> target_file=/Users/<tu-usuario>/pwnd quiet_ms=400 trigger=aaa.txt
[monitor] pivoted /watched/file.txt/escape -> /Users/<tu-usuario>
[run-poc] Resumen: 1 exito en 1 intento(s).
rm -f -- "$HOME/pwnd"
rm -rf -- poc-v2/file.txt
docker image rm copyescape-macos-pocv2:local
攻击链完成后,以下各项可作为证据:
思路与策略源自 masasron:针对 tar 生产者的 TOCTOU 竞态、通过 LD_PRELOAD 进行的伪装、16 MiB 诱饵、两个 rename(2) 的 pivot,以及 escape/ + .swap-escape 结构。没有这一设计,就不会有这次重写。概念方案完全相同。
大约 15-20% 的行逐字一致(简短的实用函数和布局常量)。其余均为新代码。
docker cp,在 Linux 上绝不要以 root 身份执行。docker cp 目标目录中是否创建了绝对符号链接,或在 daemon 端阻止包含符号链接下子条目的 tar。基于 MIT 许可证分发。更多详情请参阅 LICENSE 文件。
/Users/<usuario>/usr/binescape 以及紧随其后的子条目 escape/<basename>;存在漏洞的 CLI 在客户端创建该符号链接,并穿过该链接解压子条目,从而在用户选择的目标目录之外写入文件。| 资源 | 预期证据 |
|---|
~/pwnd | 内容为 COPYESCAPE_MACOS_DEMO\n 的文件 |
poc-v2/file.txt/ | 目录(而非文件),包含 escape -> /Users/<tu-usuario>(符号链接)和 aaa.txt(16 MiB) |
docker logs <contenedor> | 输出行 pivoted /watched/file.txt/escape -> /Users/<tu-usuario> |
CLI 29.7.2(使用已修复的 CLI 执行 docker cp) | 不会创建标记文件;PoC 失败 |
| 文件 | 原版行数 | 新版行数 | 变更说明 |
|---|
watched_preload.c | 89 | 257 | 重写:openat2、fstatat、statx、stat64、lstat64、open64、fopen64 包装函数;线程安全的构造函数;针对 dlsym == NULL 的防护;O_TMPFILE |
monitor.c(macos + linux) | 259 + 233 = 492(重复) | 344(统一) | 使用 -DCOPYESCAPE_TARGET_* 重写并统一;try_pivot 中的回滚;可配置的 exit_quiet_ms 与触发器;稳健的检测 |
entrypoint.sh | 3 | 5 | 使用 env -u LD_PRELOAD 替代 unset |
Dockerfile | 22 | 43 | 统一;固定摘要(digest pin);--platform;不带 -Werror;-static;LABEL |
demo-macos.sh → run-poc.sh | 88 | 166 | 重写:竞态重试;失败日志;--cleanup;验证委托给二进制文件 |