โ ๏ธ SECURITY RESEARCH POC โ DO NOT LOAD
This repository contains a proof-of-concept malicious .joblib payload for a responsible-disclosure bug bounty submission filed at huntr.com under the Model File Formats program.
The PoC demonstrates a scanner-bypass primitive: a .joblib file that:
- Is reported as CLEAN (
infected_files=0, issues_count=0) by every major pickle scanner (picklescan 1.0.4, modelscan, fickling) - Executes attacker-controlled code on
joblib.load()โ invokesnt.system("echo PoC")/os.system("echo PoC")on unpickle (intentionally inert payload โ only prints a banner)
The bypass works because joblib.load() routes files whose first two bytes are b"ZF" into a legacy compatibility container at joblib/numpy_pickle_compat.py::read_zfile (present since 2015, current main HEAD). The wrapper format is not documented to any third-party scanner โ scanners see a non-pickle blob and return clean.
โ ๏ธ Do not execute
# DO NOT RUN THIS
import joblib
joblib.load("zf_wrapped.joblib") # โ invokes nt.system / os.system
The payload is intentionally inert (just an echo), but the demonstration is that scanners cannot tell the difference between this and a destructive payload.
Files
| File | Purpose | Scanner verdict |
|---|---|---|
plain.pkl |
Control โ same payload as a plain pickle | picklescan: infected_files=1 (CAUGHT โ denylist working) |
zf_wrapped.joblib |
The bypass โ same payload wrapped in joblib ZF format | picklescan: infected_files=0 (BYPASSED โ scanner blind) |
build_and_scan.py |
Reproducer โ rebuilds both files locally and re-runs picklescan | โ |
One-command verification (clean venv)
pip install picklescan==1.0.4 joblib
python build_and_scan.py
Expected output:
--- PLAIN pickle (.pkl): plain.pkl ---
infected_files : 1
issues_count : 1
global : nt.system (safety=Dangerous) โ CAUGHT
--- ZF-wrapped (.joblib): zf_wrapped.joblib ---
infected_files : 0
issues_count : 0
globals : NONE โ BYPASSED SILENTLY
Affected components
- joblib โค 1.5.x (the legacy
read_zfilepath is in currentmain) - picklescan โค 1.0.4 (doesn't recognize the
b"ZF"magic) - modelscan (same primitive โ doesn't peek into legacy joblib containers)
- fickling (same primitive)
Disclosure status
This PoC is part of a responsible-disclosure submission filed via huntr's bug bounty program. The submission proposes fixes including:
- Soft-deprecation of the ZF format in joblib (opt-in flag for legacy compat)
- Hard removal of the pre-0.10 (Python 2 era) compat path
- Surfacing the legacy wrap to the scanner ecosystem (picklescan/modelscan/fickling)
After triage and remediation, this repository will be either marked private or deleted.
Cluster context
This is one of multiple orthogonal .joblib scanner-bypass primitives I've identified:
- ZF legacy container (this PoC)
- Modern
compress=wrappers (gzip/bz2/lzma/xz/zlib) โ separate filing - picklescan classification asymmetry (Suspicious-vs-Dangerous counter) โ separate filing
- picklescan denylist narrowness (
_io.open,__import__) โ separate filing - picklescan re-export + Py2/Py3 rename gaps (
numpy.testing,http.client) โ separate filing - Cross-scanner REDUCE+BUILD wrapper bypass (picklescan + fickling) โ separate filing
Each requires an independent fix.
Contact
Security researcher: kais113 (amakais.sales@gmail.com) huntr profile: huntr.com/users/kais113 (once profile is public)
Disclaimer
This repository is intended for security research and responsible disclosure only. Do not use the techniques shown here on systems you do not own or have permission to test. The author assumes no liability for misuse.