GHSA-gqvg-gmmx-x4hm: High severity pip/mlflow vulnerability

Published Sep 1, 2026
·
Updated

Summary

MLflow introduced MLFLOWALLOWPICKLEDESERIALIZATION as a security control to prevent unsafe pickle.load execution during model loading, in response to CVE-2024-37052 through CVE-2024-37060. When set to False, operators expect all pickle deserialization to be blocked. The most recent related fix (#21188) patched a bypass in the pyfunc flavor.

However, the mlflow.statsmodels flavor completely omits this guard. An attacker who places a crafted MLmodel artifact into any accessible artifact store can trigger arbitrary code execution on any process that calls mlflow.pyfunc.loadmodel() against the malicious model — even when MLFLOWALLOWPICKLEDESERIALIZATION=False.

This is a security control bypass. The operator believes pickle RCE is mitigated; the statsmodels flavor silently ignores the control.

---

Root Cause

mlflow.pyfunc.loadmodel() dispatches to flavor loadpyfunc implementations via:

mlflow/pyfunc/init.py L1170-1172 modelimpl = importlib.importmodule(conf[MAIN]).loadpyfunc(datapath)

The guarded pattern (from mlflow/sklearn/init.py L526-533, the reference implementation) is:

if ( not MLFLOWALLOWPICKLEDESERIALIZATION.get() and not isindatabricksruntime() and not isindatabricksmodelservingenvironment() ): raise MlflowException("Deserializing model using pickle is disallowed...")

mlflow/statsmodels/init.py has no such check:

L307-320 — no guard anywhere in this file def loadmodel(path): import statsmodels.iolib.api as smio return smio.loadpickle(path) # calls pickle.load() directly

def loadpyfunc(path): return StatsmodelsModelWrapper(loadmodel(path))

statsmodels.iolib.api.loadpickle is a thin wrapper around pickle.load. Its own docstring warns: "Never unpickle data received from an untrusted or unauthenticated source."

---

Trigger

An attacker crafts an MLmodel YAML that specifies mlflow.statsmodels as the loader module:

flavors: pythonfunction: loadermodule: mlflow.statsmodels data: model.pkl statsmodels: data: model.pkl statsmodelsversion: 0.14.0

With a malicious model.pkl placed alongside it in the artifact store, any call to:

os.environ["MLFLOWALLOWPICKLEDESERIALIZATION"] = "False" mlflow.pyfunc.loadmodel("models:/MaliciousModel/1")

...deserializes the pickle file with no guard check, executing arbitrary code with the privileges of the calling process.

On default MLflow deployments (no --app-name basic-auth), authentication is disabled, so artifact upload requires no credentials.

---

Affected Code

- mlflow/statsmodels/init.py L307-310: loadmodel — calls smio.loadpickle without checking MLFLOWALLOWPICKLEDESERIALIZATION - mlflow/statsmodels/init.py L313-320: loadpyfunc — dispatches to loadmodel without checking the control

Permalink (commit 0b0c576c): - https://github.com/mlflow/mlflow/blob/0b0c576c642b5b0d9496c829809c7d097403bc9f/mlflow/statsmodels/init.py#L307-L310 - https://github.com/mlflow/mlflow/blob/0b0c576c642b5b0d9496c829809c7d097403bc9f/mlflow/statsmodels/init.py#L313-L320

---

Recommended Fix

Add the missing guard to mlflow/statsmodels/init.py:

from mlflow.environmentvariables import MLFLOWALLOWPICKLEDESERIALIZATION from mlflow.utils.databricksutils import ( isindatabricksmodelservingenvironment, isindatabricksruntime, )

def loadmodel(path): if ( not MLFLOWALLOWPICKLEDESERIALIZATION.get() and not isindatabricksruntime() and not isindatabricksmodelservingenvironment() ): raise MlflowException( "Deserializing model using pickle is disallowed, but this statsmodels " "model requires pickle deserialization. Set environment variable " "'MLFLOWALLOWPICKLEDESERIALIZATION' to 'true' to allow this." ) import statsmodels.iolib.api as smio return smio.loadpickle(path)

Affected Software

1 affected componentFixes available
pip/mlflow>=2.1.0<3.15.0
3.15.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/mlflow to a version that resolves this vulnerability.

    Fixed in 3.15.0
  2. Configuration

    Set the environment variable MLFLOW_ALLOW_PICKLE_DESERIALIZATION to "false" to prevent unsafe pickle deserialization during model loading.

    MLflow environment variable MLFLOW_ALLOW_PICKLE_DESERIALIZATION = false

Event History

Sep 1, 2026
Advisory Published
via GitHub·05:04 PM
Data Sourced
via GitHub·05:04 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Does setting MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False prevent exploitation?

No. The mlflow.statsmodels flavor omits the pickle-deserialization guard, so the setting does not block deserialization when a statsmodels model is loaded through mlflow.pyfunc.load_model().

2

Which deployments are exposed?

Any process that calls mlflow.pyfunc.load_model() on a malicious statsmodels model artifact from an artifact store accessible to an attacker is exposed. The impact is arbitrary code execution in the loading process.

3

What does an attacker need to trigger the issue?

The attacker needs to place a crafted MLmodel artifact in an artifact store that the target process can access. Exploitation is triggered when that process loads the malicious model using mlflow.pyfunc.load_model().

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203