Instructions to use MBM7/skops-dtype-decompression-bomb-poc with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Scikit-learn
How to use MBM7/skops-dtype-decompression-bomb-poc with Scikit-learn:
# β οΈ Model filename not specified in config.json
- Notebooks
- Google Colab
- Kaggle
skops β .skops NPY Dtype Decompression Bomb (PoC)
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
Summary
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 |
Root Cause
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.
Attack
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)
Distinct from numpy NPY finding
| 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 |
Reproduce
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
Impact
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.
Suggested Fix
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)
Environment
| 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.
- Downloads last month
- -