
CVE-2026-17106 (CopyEscape) का कार्यात्मक PoC: docker cp में TOCTOU रेस जो Docker होस्ट पर मनमाना लेखन (arbitrary write) की अनुमति देता है। Docker लैब + inotify मॉनिटर + LD_PRELOAD। macOS के लिए हानिरहित और Linux के लिए विनाशकारी वैरिएंट।
CopyEscape (CVE-2026-17106) की शैक्षिक Proof of Concept —
docker cpमें TOCTOU रेस स्थिति भेद्यता जो एक दुर्भावनापूर्ण कंटेनर को Docker होस्ट पर मनमानी फ़ाइलें लिखने की अनुमति देती है। masasron के मूल कार्य से प्रेरित, जिन्होंने "CopyEscape: Taking Over Docker Hosts with docker cp" ब्लॉग में इस भेद्यता की खोज की और दस्तावेज़ित किया।
CVE-2026-17106 docker cp <कंटेनर>:<पथ> <गंतव्य> के दौरान Docker डेमॉन द्वारा उत्पादित tar फ़ाइल के निष्कर्षण में एक रेस स्थिति (TOCTOU) भेद्यता है। यह 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 बनाए रखना जो डेमॉन को tar उत्पादित करने में इतना समय लेने के लिए बाध्य करता है कि मॉनिटर रेस जीत सके।inotify के साथ डेमॉन की पहुँच पर नज़र रखना और, रेस विंडो में, रोलबैक के साथ दो परमाणु rename(2) के माध्यम से उपनिर्देशिका को होस्ट के लक्ष्य के पूर्ण symlink से बदलना (macOS पर या Linux पर )।परिणाम docker cp चलाने वाली प्रक्रिया के अनुमतियों के साथ होस्ट पर मनमानी फ़ाइल लेखन है। Linux वेरिएंट /usr/bin/runc को अधिलेखित करता है और अगले docker run / exec पर होस्ट पर root के रूप में कोड निष्पादन प्राप्त करता है।
इस रिपॉज़िटरी में पूर्णतः शैक्षिक और रक्षात्मक सामग्री है।
runc को नष्ट कर देता है: इसे केवल सत्यापित बैकअप वाली डिस्पोजेबल VM में उपयोग करें।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/<उपयोगकर्ता> हो।macOS वेरिएंट के लिए root की आवश्यकता नहीं है। Linux वेरिएंट के लिए root और एक डिस्पोजेबल VM आवश्यक है।
run-poc.sh रनर स्वचालित रूप से Docker की आधिकारिक साइट (download.docker.com) से कमजोर CLI 29.6.1 का बाइनरी डाउनलोड करता है। यह सिस्टम के Docker Desktop इंस्टॉलेशन को संशोधित नहीं करता: यह वर्तमान डेमॉन का उपयोग करता है, लेकिन 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 प्रयास (रेस संभाव्य है)
./run-poc.sh --cleanup # 5 प्रयास और अंत में इमेज हटा देता है
[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) के साथ पिवट, और escape/ + .swap-escape संरचना। उस डिज़ाइन के बिना यह पुनर्लेखन अस्तित्व में नहीं होता। अवधारणात्मक योजना समान है।
लगभग 15-20 % पंक्तियाँ ऐसी हैं जो शाब्दिक रूप से मेल खाती हैं (छोटी उपयोगिताएँ और लेआउट स्थिरांक)। बाकी नया कोड है।
docker cp न चलाएँ जिस पर भरोसा नहीं है, और Linux में कभी भी root के रूप में नहीं।docker cp के गंतव्य के भीतर पूर्ण symlinks के निर्माण की निगरानी करें, या डेमॉन में symlink के अंतर्गत चाइल्ड प्रविष्टियों वाले tars को ब्लॉक करें।MIT लाइसेंस के अंतर्गत वितरित। अधिक विवरण के लिए LICENSE फ़ाइल देखें।
escape//Users/<उपयोगकर्ता>/usr/binescape और उसके बाद एक चाइल्ड प्रविष्टि escape/<basename> होती है; कमजोर CLI क्लाइंट में symlink बनाती है और चाइल्ड प्रविष्टि को उसके माध्यम से निकालती है, उपयोगकर्ता द्वारा चुने गए गंतव्य के बाहर लिखते हुए।| संसाधन | अपेक्षित साक्ष्य |
|---|
~/pwnd | सामग्री COPYESCAPE_MACOS_DEMO\n वाली फ़ाइल |
poc-v2/file.txt/ | निर्देशिका (फ़ाइल नहीं) जिसमें escape -> /Users/<tu-usuario> (symlink) और 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 | unset के स्थान पर env -u LD_PRELOAD |
Dockerfile | 22 | 43 | एकीकृत; digest pin; --platform; बिना -Werror; -static; LABEL |
demo-macos.sh → run-poc.sh | 88 | 166 | पुनर्लेखित: रेस पुनःप्रयास; विफलता पर लॉग; --cleanup; बाइनरी को सौंपा गया सत्यापन |