Downloads · 30 days
0
MBM7/skops-dtype-decompression-bomb-poc
skops-dtype-decompression-bomb-poc is a machine learning model from MBM7. Use it for the machine learning task on the model card, and read the license before you ship it in a product. It is set up for sklearn. The card lists the license as mit.
Repo: MBM7/skops-dtype-decompression-bomb-poc Status: Responsible disclosure — submitted to Huntr Severity: High / CWE-789 Package: skops (PyPI) — HuggingFace official sklearn model format
Downloads · 30 days
0
Access
Public
Updated Jul 18, 2026
Repo size
—
Likes
0
Public
Click a slice to open those files.
.py5.7 KB · 52%
From the Hugging Face model README
Repo: MBM7/skops-dtype-decompression-bomb-poc
Status: Responsible disclosure — submitted to Huntr
Severity: High / CWE-789
Package: skops (PyPI) — HuggingFace official sklearn model format
A crafted 8.9 KB .skops sklearn model file causes skops.io.loads()
to allocate 2 GB of memory before failing.
| File size | Claimed dtype itemsize | Peak allocation | Amplification |
|---|---|---|---|
| 8,935 bytes | 500,000,000 | 500 MB | 1 : 55,959 |
| 8,935 bytes | 2,000,000,000 | 2,000 MB | 1 : 223,838 |
skops/io/_numpy.py, NdArrayNode._construct():
content = np.load(
self.children["content"], # BytesIO of .npy file from .skops ZIP
allow_pickle=False
) # ← np.load allocates count * dtype.itemsize BEFORE reading data
The .skops format is a ZIP archive containing schema.json + .npy files.
When loading, NdArrayNode reads each .npy file into a BytesIO and passes
it directly to np.load() — with no validation of dtype.itemsize or shape.
A crafted .npy file inside the ZIP claiming dtype=|S2000000000 (2 GB
itemsize) causes np.load() to pre-allocate 2 GB before attempting to
read data.
Replace any .npy file inside a legitimate .skops model archive with a
dtype bomb .npy header. schema.json remains unchanged — skops trusts it
and loads all .npy files unconditionally:
import skops.io as sio
# Load triggers NdArrayNode._construct() → np.load() → 2GB allocation
trusted = sio.get_untrusted_types(data=bomb_skops_bytes)
model = sio.loads(bomb_skops_bytes, trusted=trusted)
| numpy NPY | skops | |
|---|---|---|
| Entry point | np.load('file.npy') | skops.io.loads(skops_bytes) |
| Format | .npy raw | .skops ZIP (sklearn model) |
| Ecosystem | NumPy arrays | HuggingFace sklearn model sharing |
| Context | Direct array loading | sklearn model deserialization |
pip install skops scikit-learn numpy
python poc_skops_dtype_bomb.py
Expected:
.skops file size : 8,935 bytes
Expected allocation: 2,000,000,000 bytes
Amplification : 1:223,838
Result : ValueError: EOF: reading array data...
Peak memory : 2000 MB ← allocated before error
Any service loading user-supplied .skops files from HuggingFace Hub or
other sources can be OOM-killed by a sub-10KB payload. skops is the
official HuggingFace format for sharing sklearn models — directly
relevant to the AI/ML supply chain.
In NdArrayNode._construct(), validate before np.load():
MAX_ARRAY_BYTES = 512 * 1024 * 1024 # 512 MB
buf = self.children["content"]
# Peek at NPY header without allocating
hdr = np.lib.format.read_array_header_1_0(buf)
buf.seek(0)
if hdr[1].itemsize > MAX_ARRAY_BYTES:
raise ValueError(
f"NPY dtype itemsize {hdr[1].itemsize} exceeds limit — "
"possible decompression bomb"
)
content = np.load(buf, allow_pickle=False)
| Package | Version |
|---|---|
| skops | 0.14.0 |
| numpy | 2.4.4 |
| Python | 3.12 |
skops was created as a safer alternative to pickle for sklearn models.
Ironic: the safety-focused format has an unvalidated allocation path.