Resumen
MLflow introdujo la variable de entorno MLFLOW_ALLOW_PICKLE_DESERIALIZATION como control de seguridad para prevenir la ejecución insegura de pickle.load durante la carga de modelos, en respuesta a los CVE-2024-37052 hasta CVE-2024-37060. Cuando se establece en False, los operadores esperan que toda deserialización pickle quede bloqueada. La corrección más reciente relacionada (PR #21188) parcheó un bypass en el flavor pyfunc.
Sin embargo, el flavor mlflow.statsmodels omite completamente esta protección. Un atacante que coloque un artefacto MLmodel manipulado en cualquier almacén de artefactos accesible puede desencadenar ejecución de código arbitrario en cualquier proceso que invoque mlflow.pyfunc.load_model() contra el modelo malicioso, incluso cuando MLFLOW_ALLOW_PICKLE_DESERIALIZATION=False.
Se trata de un bypass de control de seguridad. El operador cree que el RCE por pickle está mitigado; el flavor statsmodels ignora silenciosamente el control.
Causa raíz
mlflow.pyfunc.load_model() delega en las implementaciones _load_pyfunc de cada flavor mediante:
# mlflow/pyfunc/__init__.py L1170-1172
model_impl = importlib.import_module(conf[MAIN])._load_pyfunc(data_path)
El patrón protegido (tomado de mlflow/sklearn/__init__.py L526-533, la implementación de referencia) es:
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 no contiene ninguna verificación equivalente:
# L307-320: sin protección en ninguna parte de este archivo
def _load_model(path):
import statsmodels.iolib.api as smio
return smio.load_pickle(path) # invoca pickle.load() directamente
def _load_pyfunc(path):
return _StatsmodelsModelWrapper(_load_model(path))
statsmodels.iolib.api.load_pickle es un envoltorio delgado sobre pickle.load. Su propia documentación advierte: "Nunca deserialices datos recibidos de una fuente no confiable o no autenticada".
Vector de ataque
Un atacante construye un YAML MLmodel que especifica mlflow.statsmodels como módulo cargador:
flavors:
python_function:
loader_module: mlflow.statsmodels
data: model.pkl
statsmodels:
data: model.pkl
statsmodels_version: 0.14.0
Con un archivo model.pkl malicioso colocado junto a él en el almacén de artefactos, cualquier llamada como la siguiente:
os.environ["MLFLOW_ALLOW_PICKLE_DESERIALIZATION"] = "False"
mlflow.pyfunc.load_model("models:/MaliciousModel/1")
deserializa el archivo pickle sin ninguna verificación de protección, ejecutando código arbitrario con los privilegios del proceso invocante.
En despliegues MLflow por defecto (sin --app-name basic-auth), la autenticación está deshabilitada, por lo que la subida de artefactos no requiere credenciales.
Código afectado
mlflow/statsmodels/__init__.pyL307-310: función_load_model, invocasmio.load_picklesin verificarMLFLOW_ALLOW_PICKLE_DESERIALIZATION.mlflow/statsmodels/__init__.pyL313-320: función_load_pyfunc, delega en_load_modelsin verificar el control.
Enlaces permanentes (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
Corrección recomendada
Añadir la verificación ausente en 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)
