File size: 2,127 Bytes
fd8ef7f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
---
license: apache-2.0
tags: [security, proof-of-concept, modelscan]
---

# PoC — modelscan fails **open** on `.npy` under numpy >= 2

Proof-of-concept for a **huntr Model File Vulnerabilities (MFV)** report against
[`modelscan`](https://github.com/protectai/modelscan) **0.8.8**.

Payload is deliberately harmless: it runs `echo NPY_EXEC_OK`. No network access, no deletion,
nothing written outside the working directory.

## The issue

`modelscan/tools/picklescanner.py:234` calls a **private numpy API**:

```python
np.lib.format._check_version(version)   # type: ignore[attr-defined]
```

`numpy` **2.0 removed `_check_version`**. Under numpy >= 2 the call raises
`AttributeError`, the `.npy` scanner aborts — and modelscan still prints an affirmative
clean verdict. The error is relegated to a secondary `Errors` section and the skipped
count is hidden behind `--show-skipped`.

`pip install modelscan` today resolves numpy 2.x by default, so a default install
returns a clean verdict for **any** malicious `.npy`.

## Measured differential — identical file, only numpy differs

| numpy | modelscan 0.8.8 verdict |
|---|---|
| **2.5.1** | ` No issues found! 🎉` + `Errors: module 'numpy.lib.format' has no attribute '_check_version'` + `Total skipped: 1` |
| **1.26.4** | `CRITICAL: 1`*Use of unsafe operator 'system' from module 'posix'* |

## Reproduce

```bash
python -m venv v2 && ./v2/bin/pip install modelscan==0.8.8 numpy==2.5.1
./v2/bin/python generate_poc.py
./v2/bin/modelscan -p poc_object_array.npy      # "No issues found!"  <-- fails open

python -m venv v1 && ./v1/bin/pip install modelscan==0.8.8 numpy==1.26.4
./v1/bin/modelscan -p poc_object_array.npy      # CRITICAL: unsafe operator 'system'
```

## Suggested fix

- Stop calling the private `np.lib.format._check_version`; gate on `numpy.__version__` or
  drop the check (`_read_array_header` already validates the version).
- **Fail closed:** a scanner that raised must not yield an affirmative "No issues found"
  summary. Surface aborted scans in the main verdict.

## Attribution

Cyfra Tech Solutions (Roman Arce Bran), Costa Rica.