GHSA-72r2-7mfr-5xr9: Medium severity pip/nltk vulnerability

Published Sep 8, 2026
·
Updated

Summary

There's a logic bug in FileSystemPathPointer.open() inside nltk/data.py that makes the sandbox check permanently inert. The guard condition is always False — meaning any file the process can read is accessible by passing a file:// URL to nltk.data.load().

---

Details

In nltk/data.py, FileSystemPathPointer.open() was patched at some point with a comment saying "SECURITY PATCH ENFORCING SANDBOX", but the check doesn't work: python def open(self, encoding=None): path = os.path.normpath(self.path)

# Block raw absolute reads such as "/" "C:\\Windows" etc. if os.path.isabs(path) and path != os.path.normpath(self.path): raise ValueError(f"Direct absolute file access blocked: {path}")

stream = open(self.path, "rb")

path is set to os.path.normpath(self.path) on line 1, then compared against os.path.normpath(self.path) again in the condition. They are always equal. The ValueError never fires.

On top of that, init already calls os.path.abspath() before storing self.path, so it's normalized before open() is even called. Running normpath on it again changes nothing.

The stream = open(self.path, "rb") line is always reached regardless of what path was passed in.

---

PoC

Tested on Python 3.11, NLTK 3.9.1, Ubuntu 22.04. python import nltk from nltk.data import FileSystemPathPointer

direct construction ptr = FileSystemPathPointer("/etc/passwd") with ptr.open() as f: print(f.read(300))

via load() using file:// URL data = nltk.data.load("file:///etc/passwd", format="raw") print(data[:300])

Both print file contents. No exception is raised.

---

Impact

Any app that lets users influence the string passed to nltk.data.load() or nltk.data.find() is exposed — web APIs, notebook servers, multi-tenant pipelines. An attacker can read any file the process user has access to: /etc/passwd, .env files, private keys, ~/.aws/credentials, etc.

Suggested Fix

File: nltk/data.py — FileSystemPathPointer.open() (lines 378–390)

What's wrong

Line 387 compares normpath(self.path) against itself — always equal, so the ValueError never fires. The check is dead code. init already calls abspath() on construction, so re-running normpath inside open() changes nothing either.

---

Fix

Validate against the actual list of permitted data directories instead: python def open(self, encoding=None): import nltk.data as d allowed = [os.path.abspath(p) for p in d.path if p] if allowed and not any( os.path.commonpath([self.path, r]) == r for r in allowed ): raise ValueError( f"Access outside nltkdata blocked: {self.path!r}" ) stream = open(self.path, "rb") if encoding is not None: stream = SeekableUnicodeStreamReader(stream, encoding) return stream

---

Why commonpath not startswith

startswith is bypassable by a path that shares a prefix: /tmp/nltkdataevil".startswith("/tmp/nltkdata") → True ✗ commonpath(["/tmp/nltkdataevil", "/tmp/nltkdata"]) → "/tmp" ✓

---

Diff diff - path = os.path.normpath(self.path) - if os.path.isabs(path) and path != os.path.normpath(self.path): - raise ValueError(f"Direct absolute file access blocked: {path}") - + import nltk.data as d + allowed = [os.path.abspath(p) for p in d.path if p] + if allowed and not any( + os.path.commonpath([self.path, r]) == r for r in allowed + ): + raise ValueError(f"Access outside nltkdata blocked: {self.path!r}") stream = open(self.path, "rb")

Affected Software

1 affected componentFixes available
pip/nltk<=3.9.3
3.10.0

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/nltk to a version that resolves this vulnerability.

    Fixed in 3.10.0

Event History

Sep 8, 2026
Advisory Published
via GitHub·03:27 PM
Data Sourced
via GitHub·03:27 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

What must an attacker be able to control to exploit this issue?

The attacker must be able to cause nltk.data.load() to receive a file:// URL. The target file must also be readable by the process running NLTK.

2

What is the practical impact of a successful exploit?

An attacker can read any file that the affected process is permitted to read. The supplied severity vector indicates high confidentiality impact, with no integrity or availability impact.

3

Does exploitation require user interaction or elevated privileges?

The supplied CVSS vector indicates no user interaction is required and that low privileges are required. It also identifies the attack vector as network-accessible.

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