YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
MLflow S3 Model Store RCE
Pattern: Trusted shared S3 bucket + mlflow.pyfunc.load_model("s3://...") → arbitrary code execution at load time. Here the malicious model is delivered over the network from a shared artifact bucket (AV:N).
Attack
- Attacker has
s3:PutObjecton a team/shared MLflow artifact bucket (or compromises a CI deploy key). - Attacker replaces the model prefix (
MLmodel,code/mfv_loader.py, env files) with a poisoned copy. - Victim runs normal MLflow workflow:
mlflow.pyfunc.load_model("s3://shared-bucket/fraud-detector/"). - MLflow downloads the full model directory from S3, adds
code/tosys.path, andimportlib.import_module("mfv_loader")runs attacker code in_load_pyfunc()before the model is returned.
Root cause: MLflow executes the loader_module Python file at load time with no integrity check beyond bucket ACLs.
Prerequisites
- Attacker: write access to the shared S3 model prefix (
s3:PutObject). - Victim: AWS credentials that can read the bucket + calls
mlflow.pyfunc.load_model()on ans3://URI (ormodels:/URI resolving to S3).
Reproduce locally (moto mock S3)
python3 -m venv .venv && source .venv/bin/activate
pip install -r mlflow_s3_rce/hf_repo/requirements.txt
python mlflow_s3_rce/hf_repo/repro.py
Expected: RCE CONFIRMED via S3 model store and marker /tmp/MLFLOW_S3_RCE created.
ModelScan
cd mlflow_s3_rce/hf_repo
modelscan -p MLmodel
modelscan -p code/mfv_loader.py
modelscan -p .
Malicious payload is plain Python + YAML config - ModelScan typically PASS or SKIP (no pickle/safetensors weights to scan).
CVSS 3.1 (proposed)
AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H (~8.0 High)
| Vector | Rationale |
|---|---|
| AV:N | Victim fetches model from S3 over network |
| AC:L | Requires bucket write, not public exploit |
| PR:L | Attacker needs write on shared artifact store |
| UI:R | Victim must load the poisoned model URI |
| C:H/I:H/A:H | Full code execution in victim process |
MLmodel note
Real mlflow.pyfunc.save_model() output includes code: code under python_function. MLflow only prepends the code/ directory to sys.path when this key is present. The PoC mirrors that layout.
References
- CVE-2026-9291 pattern: trusted storage + auto-deserialize
- MLflow docs:
load_model()acceptss3://,gs://,wasbs://URIs