YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
modelscan-joblib-compression-bypass
This repository contains a proof of concept for a scanner bypass in .joblib model files.
Vulnerability
joblib.load() transparently supports compressed persistence files such as zlib, gzip, bz2, lzma, and xz. A malicious payload embedded in a compressed .joblib file still reaches Python deserialization and can execute arbitrary code during model load.
The open-source protectai/modelscan scanner, however, scans .joblib files as raw pickle bytes and does not decompress supported joblib compression layers first. This means:
- an uncompressed malicious
.joblibfile is detected - the same payload, when wrapped with standard joblib compression, becomes undetected
Primary PoC
The main PoC file is:
artifacts/zlib.joblib
Control file:
artifacts/plain.joblib
Both contain the same harmless test payload. On load, the payload writes a local marker file (JOBLIB_PWNED) to demonstrate code execution without doing anything destructive.
Included Files
artifacts/plain.joblibartifacts/zlib.joblibartifacts/plain.joblib.modelscan.jsonartifacts/zlib.joblib.modelscan.jsonartifacts/reproduce_output.txtartifacts/sha256sums.txt
Tested Environment
- Date tested: July 16, 2026
- Python: 3.13.12
joblib: 1.5.2modelscan:protectai/modelscan@61fcec9c2a37c24c1fb12d84ede30fe248a364bd
Reproduction
1. Verify code execution in joblib.load()
import joblib
joblib.load("zlib.joblib")
Expected behavior:
- the payload executes during deserialization
- a local marker file is created with content
JOBLIB_PWNED
2. Compare scanner behavior
Uncompressed control:
PYTHONPATH=/path/to/modelscan python - <<'PY'
from modelscan.modelscan import ModelScan
import json
print(json.dumps(ModelScan().scan("plain.joblib"), indent=2))
PY
Expected result:
plain.joblibis flagged with aCRITICALissue forposix.system
Compressed PoC:
PYTHONPATH=/path/to/modelscan python - <<'PY'
from modelscan.modelscan import ModelScan
import json
print(json.dumps(ModelScan().scan("zlib.joblib"), indent=2))
PY
Expected result:
zlib.joblibreports0issues- scanner only emits a pickle parsing error on the compressed envelope
Observed Results
From artifacts/reproduce_output.txt:
joblib.load("plain.joblib")executes the payloadjoblib.load("zlib.joblib")executes the payloadmodelscanflagsplain.joblibas maliciousmodelscanmisseszlib.joblib
Impact
This is a practical scanner bypass for a supported model format:
- the malicious model file remains loadable by the target ecosystem
- load-time arbitrary code execution is preserved
- scanner coverage is bypassed by using a standard compression mode already supported by
joblib
Notes
zlib.joblibis the main PoC artifact for submissionplain.joblibis included to show that the bypass comes from compression handling, not from a different payload