GHSA-wg9g-w2j2-8pgr: MONAI: NumpyReader RCE via forced allow_pickle=True

GHSA-wg9g-w2j2-8pgrHIGH
Published August 18, 2026
CISO Take

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.

Sources:GitHub AdvisoryNVDOpenSSFATLAS

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?

Weaponize dataset artifact
Attacker crafts a .npy file embedding a pickle payload via __reduce__ and distributes it through a shared medical dataset, public repository, or MONAI tutorial/bundle.
AML.T0010.002
User loads pipeline
A researcher builds a standard MONAI pipeline (LoadImage, PersistentDataset, CacheDataset) that ingests the file, triggering automatic selection of NumpyReader by extension.
AML.T0011
Unsafe deserialization
NumpyReader calls np.load with hardcoded allow_pickle=True, deserializing the pickle payload and executing the attacker's __reduce__ code.
AML.T0018.002
Code execution / lateral movement
Arbitrary code runs on the researcher's or hospital's processing server, enabling compromise of systems that may handle protected health information.
AML.T0112

What systems are affected?

PackageEcosystemVulnerable RangePatched
MONAIpip< 1.6.01.6.0
8.8KOpenSSF 7.8128 dependentsPushed 4d ago41% patched~21d to patchFull package profile →

Do you use MONAI? You're affected.

How severe is it?

CVSS 3.1
7.8 / 10
EPSS
N/A
Exploitation Status
No known exploitation
Sophistication
Moderate

What is the attack surface?

AVACPRUISCIA
AVLocal
ACLow
PRNone
UIRequired
SUnchanged
CHigh
IHigh
AHigh

What should I do?

1 step
  1. 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.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 15-Accuracy, robustness and cybersecurity
ISO 42001
Annex A.6.2.4-AI system security
NIST AI RMF
MANAGE-4.1-Third-party risks and mechanisms for risk response

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

training pipelinesdata preprocessing pipelinesmedical imaging inference pipelines

MITRE ATLAS Techniques

AML.T0010.002Data
AML.T0011User Execution
AML.T0011.000Unsafe AI Artifacts
AML.T0018.002Embed Malware

Compliance Controls Affected

EU AI Act: Article 15
ISO 42001: Annex A.6.2.4
NIST AI RMF: MANAGE-4.1

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:H

Timeline

Published
August 18, 2026
Last Modified
August 18, 2026
First Seen
August 19, 2026

Related Vulnerabilities