Joblib ZF Compat + Companion .npy Scanner Bypass
Vulnerability
Scanner Bypass via Joblib ZF Compat Mode + NDArrayWrapper + np.load(allow_pickle=True)
ProtectAI ModelScan 0.8.8 and PickleScan 1.0.4 both fail to detect malicious code embedded in joblib model files that use the ZF compat format (joblib ≤0.9.3 compatibility) with a companion .npy file.
Attack Chain
model.joblib— Uses ZF format (prefixZF+ zlib compressed pickle). Scanners attempt to parse as raw pickle, fail on unknown opcodeZ, and report "0 dangerous globals".- Inside the ZF payload:
NDArrayWrapper("payload.npy", np.ndarray)— This is a legacy joblib class that loads tensor data from a separate .npy file. payload.npy— A numpy NPY file containing an object-dtype array with a malicious pickle payload.- When
joblib.load('model.joblib')is called: ZF→zlib decompress→unpickle NDArrayWrapper→np.load('payload.npy', allow_pickle=True)→RCE
Scanner Results
| Scanner | model.joblib | payload.npy | Result |
|---|---|---|---|
| ModelScan 0.8.8 | ❌ Parsing error: opcode 'Z' unknown | ❌ numpy._check_version error | Bypassed |
| PickleScan 1.0.4 | ❌ opcode 'Z' unknown | Scans .npy as pickle → detects | Bypassed (if .npy not scanned) |
In practice, scanners only check .joblib and .pkl files, not companion .npy files.
Novelty
This is a third distinct bypass technique for joblib, different from:
- Compression bypass (zlib/zstd/bz2/lzma wrapping)
- Hybrid object-dtype array embedding
- ZF compat mode + companion .npy ← this work
Usage
python3 poc_verify.py
# Output: RCE triggered via np.load(allow_pickle=True)
Inference Providers NEW
This model isn't deployed by any Inference Provider. 🙋 Ask for provider support