MLflow: SSRF on unauthenticated MLflow servers via third-party scorer kwargs in the scorer invoke, register, and GenAI evaluate endpoints
影響パッケージ
| 種類 | パッケージ | 影響を受ける版 | 直った版 |
|---|---|---|---|
| mlflow | mlflow | — | — |
| pip | mlflow |
3.17.0 未満 |
3.17.0 |
| PyPI | mlflow |
3.12.0rc0 以上 3.17.0 未満 |
3.17.0 |
原文の説明 取得元が公開している説明をそのまま載せています (多くは英語)。
Summary
MLflow's tracking server exposes POST /ajax-api/3.0/mlflow/scorer/invoke with no authentication, and the handler reflectively instantiates an attacker-named class from attacker-supplied JSON with unconstrained constructor kwargs. With the optional trulens + trulens-providers-litellm packages installed, an unauthenticated attacker can force the server to make an outbound HTTP request to an arbitrary host of their choosing, carrying attacker-controlled headers (SSRF).
Details
Scorer.model_validate_json() reflects a class name from a JSON payload behind an import-path allowlist that never constrains the reflected attribute or its kwargs, in mlflow/genai/scorers/base.py:549-604:
data = serialized.third_party_scorer_data # attacker JSON
module_path = data.get("module") or ""
class_name = data.get("class")
if not any(module_path == m or module_path.startswith(m + ".")
for m in THIRD_PARTY_SCORER_ALLOWED_MODULES):
raise MlflowException.invalid_parameter_value(...)
module = importlib.import_module(module_path)
scorer_class = getattr(module, class_name, None) # attribute NOT constrained
...
init_kwargs: dict[str, Any] = dict(data.get("kwargs") or {}) # kwargs NOT constrained
...
init_kwargs["model"] = data["model"] # attacker model URI
scorer_instance = scorer_class(**init_kwargs) # reflective instantiation
The allowlist (mlflow/genai/scorers/scorer_utils.py:46-51) only gates the import path…
出典: CVE.org (CVE Program・cvelistV5) NVD GitHub Advisory