โš ๏ธ 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() โ€” invokes nt.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_zfile path is in current main)
  • 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:

  1. Soft-deprecation of the ZF format in joblib (opt-in flag for legacy compat)
  2. Hard removal of the pre-0.10 (Python 2 era) compat path
  3. 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.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. ๐Ÿ™‹ Ask for provider support