Summary
MLFLOWALLOWPICKLE_DESERIALIZATION=False safety control bypassed by mlflow.statsmodels flavor, RCE via crafted model artifact
MLflow introduced MLFLOW_ALLOW_PICKLE_DESERIALIZATION 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.load_model() against the malicious model, even when MLFLOW_ALLOW_PICKLE_DESERIALIZATION=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.load_model() dispatches to flavor _load_pyfunc implementations via:
# mlflow/pyfunc/__init__.py L1170-1172
model_impl = importlib.import_module(conf[MAIN])._load_pyfunc(data_path)
The guarded pattern (from mlflow/sklearn/__init__.py L526-533, the reference implementation) is:
if (
not MLFLOW_ALLOW_PICKLE_DESERIALIZATION.get()
and not is_in_databricks_runtime()
and not is_in_databricks_model_serving_environment()
):
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 _load_model(path):
import statsmodels.iolib.api as smio
return smio.load_pickle(path) # calls pickle.load() directly
def _load_pyfunc(path):
return _StatsmodelsModelWrapper(_load_model(path))
statsmodels.iolib.api.load_pickle 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:
python_function:
loader_module: mlflow.statsmodels
data: model.pkl
statsmodels:
data: model.pkl
statsmodels_version: 0.14.0
With a malicious model.pkl placed alongside it in the artifact store, any call to:
os.environ["MLFLOW_ALLOW_PICKLE_DESERIALIZATION"] = "False"
mlflow.pyfunc.load_model("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__.pyL307-310:_load_model, callssmio.load_picklewithout checkingMLFLOW_ALLOW_PICKLE_DESERIALIZATIONmlflow/statsmodels/__init__.pyL313-320:_load_pyfunc, dispatches to_load_modelwithout checking the control
Permalink (commit 0b0c576c):
Impact
Untrusted serialized data is processed by a deserializer that can instantiate arbitrary objects or execute code as a side effect. Typical impact: arbitrary code execution or logic abuse.
GHSA-GQVG-GMMX-X4HM has a CVSS score of 8.8 (High). The vector is network-reachable, no privileges required, and user interaction required. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (3.15.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Add the missing guard to mlflow/statsmodels/__init__.py:
from mlflow.environment_variables import MLFLOW_ALLOW_PICKLE_DESERIALIZATION
from mlflow.utils.databricks_utils import (
is_in_databricks_model_serving_environment,
is_in_databricks_runtime,
)
def _load_model(path):
if (
not MLFLOW_ALLOW_PICKLE_DESERIALIZATION.get()
and not is_in_databricks_runtime()
and not is_in_databricks_model_serving_environment()
):
raise MlflowException(
"Deserializing model using pickle is disallowed, but this statsmodels "
"model requires pickle deserialization. Set environment variable "
"'MLFLOW_ALLOW_PICKLE_DESERIALIZATION' to 'true' to allow this."
)
import statsmodels.iolib.api as smio
return smio.load_pickle(path)
Frequently Asked Questions
- What is GHSA-GQVG-GMMX-X4HM? GHSA-GQVG-GMMX-X4HM is a high-severity insecure deserialization vulnerability in mlflow (pip), affecting versions >= 2.1.0, < 3.15.0. It is fixed in 3.15.0. Untrusted serialized data is processed by a deserializer that can instantiate arbitrary objects or execute code as a side effect.
- How severe is GHSA-GQVG-GMMX-X4HM? GHSA-GQVG-GMMX-X4HM has a CVSS score of 8.8 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of mlflow are affected by GHSA-GQVG-GMMX-X4HM? mlflow (pip) versions >= 2.1.0, < 3.15.0 is affected.
- Is there a fix for GHSA-GQVG-GMMX-X4HM? Yes. GHSA-GQVG-GMMX-X4HM is fixed in 3.15.0. Upgrade to this version or later.
- Is GHSA-GQVG-GMMX-X4HM exploitable, and should I be worried? Whether GHSA-GQVG-GMMX-X4HM is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether GHSA-GQVG-GMMX-X4HM is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix GHSA-GQVG-GMMX-X4HM? Upgrade
mlflowto 3.15.0 or later.