
Функциональный PoC для CVE-2026-17106 (CopyEscape): гонка TOCTOU в docker cp, допускающая произвольную запись на хосте Docker. Лаборатория Docker + монитор inotify + LD_PRELOAD. Вариант для macOS безвреден, для Linux — разрушителен.
Образовательный PoC для CopyEscape (CVE-2026-17106), уязвимость состояния гонки TOCTOU в
docker cp, которая позволяет вредоносному контейнеру записывать произвольные файлы на хост Docker. Вдохновлено оригинальной работой masasron, который обнаружил и задокументировал уязвимость в блоге "CopyEscape: Taking Over Docker Hosts with docker cp".
CVE-2026-17106 — это уязвимость состояния гонки (TOCTOU) при извлечении tar-файла, который демон Docker создаёт во время docker cp <contenedor>:<ruta> <destino>. Затрагивает Docker CLI 29.6.1 и более ранние версии; исправлена начиная с 29.7.0. Ошибка находится в CLI (извлечение tar на хосте), а не в демоне.
Цепочка, названная CopyEscape, демонстрирует, как, имея только запущенный контейнер без дополнительных возможностей, атакующий может:
/watched/file.txt/ под обычный файл для любого процесса, открывающего его внутри контейнера, с помощью библиотеки LD_PRELOAD, перехватывающей open, openat, openat2, fopen, stat, lstat, fstatat, statx и варианты 64.aaa.txt размером 16 МиБ, вынуждающий демон тратить достаточно времени на создание tar, чтобы монитор успевал выиграть гонку.inotify обращения демона и в окне гонки заменять подкаталог escape/ на абсолютную символическую ссылку к целевой директории хоста (/Users/<usuario> в macOS или /usr/bin в Linux) с помощью двух атомарных rename(2) с откатом.escape, за которой следует дочерняя запись escape/<basename>; уязвимая CLI создаёт символическую ссылку на клиенте и извлекает дочернюю запись через неё, записывая за пределы выбранного пользователем места назначения.Результат — произвольная запись файлов на хост с правами процесса, выполняющего docker cp. Вариант Linux перезаписывает /usr/bin/runc и добивается выполнения кода с правами root на хосте при следующем docker run / exec.
Этот репозиторий содержит строго образовательные и защитные материалы.
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 со следующими требованиями:
curl, tar, git и docker в PATH.alpine:3.21.HOME в виде /Users/<usuario>.Для варианта macOS root не требуется. Вариант Linux требует root и одноразовую VM.
Скрипт run-poc.sh автоматически загружает бинарный файл уязвимой CLI 29.6.1 с официального сайта Docker (download.docker.com). Он не изменяет установленную в системе Docker Desktop: используется текущий демон, но для операции docker cp вызывается загруженный бинарный файл.