NLTK: Downloader.download follows hardlinks and overwrites outside-root filesGHSA-f794-5jv7-7672
HIGHFix: nltk/nltk#3797GHSA-f794-5jv7-7672 is a high-severity (CVSS 7.1) CWE-59 vulnerability in nltk. A fix is available for nltk — see the affected versions and patch details below.
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for GHSA-f794-5jv7-7672.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-f794-5jv7-7672 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 385,738 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
nltkReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Summary
NLTK's downloader now blocks symlink escapes during ZIP extraction, but it still treats pre-existing hardlinks inside the install tree as ordinary in-root files. A normal package install can therefore overwrite an outside-root inode through that hardlink.
Details
- Vulnerability type: Filesystem containment bypass
- Affected component:
nltk.downloader.Downloader.download,nltk.downloader.Downloader.incr_download - Affected versions: Published
3.9.4and current sourcev3.10.0-rc2both reproduced for the extraction-stage overwrite. - Patched versions: 3.10.3
- Root cause: The downloader validates traversal and symlink conditions but does not reject pre-existing hardlink aliases inside the install tree.
The install flow correctly rejects a pre-existing symlink at an extraction target, yet it accepts a pre-existing hardlink at the same path. When the package is installed, extracted member data is written through the hardlink and mutates the outside inode.
PoC
Preconditions
- The attacker can plant files inside a writable shared downloader root on the same filesystem as the target file.
Steps
- Prepare a downloader root and create a hardlink inside it that points to an outside target file.
- Confirm a symlink at the same path is rejected as a negative control.
- Run a normal
Downloader.download()package install whose extracted member lands on the hardlink path. - Observe the outside target file is overwritten while the downloader still reports the package as installed.
Minimal reproducible excerpt
extract_hardlink_before ORIGINAL
extract_hardlink_after PWNED
extract_hardlink_status installed
Impact
A shared or attacker-influenced downloader directory can be turned into an overwrite primitive against same-filesystem files outside the intended install root.
Remediation
Treat pre-existing hardlinks as unsafe in extraction targets, verify that each write path stays within the intended install tree at the inode level, and add regression tests that pair hardlinks with existing symlink controls.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | nltk | all versions | 3.10.3pip install --upgrade 'nltk==3.10.3' |
Affected Products
nltknltkDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for nltk, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update nltk to 3.10.3 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-f794-5jv7-7672 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
The NLTK library is vulnerable to a local file overwrite due to a filesystem containment bypass. An attacker with write access to a shared NLTK downloader directory can exploit this by creating hardlinks, leading to the overwriting of arbitrary files outside the intended installation path during package extraction.…
To mitigate this issue, ensure that NLTK downloader directories are not shared among untrusted users or are configured with restrictive permissions to prevent unauthorized write access. If a shared downloader directory is essential, implement strict access controls to limit write permissions to only trusted accounts. This reduces the attack surface by preventing malicious local users from creating hardlinks to arbitrary files.Source: Red Hat security advisory for GHSA-f794-5jv7-7672 (CC BY 4.0)
| Product | Fixed in | Advisory |
|---|---|---|
| Red Hat OpenShift AI 3.3 | rhoai/odh-llama-stack-core-rhel9:1789121286 | RHSA-2026:73987 |
| Red Hat OpenShift AI 3.5 | rhoai/odh-ta-lmes-job-rhel9:1788935863 | RHSA-2026:69539 |
Frequently Asked Questions
Is GHSA-f794-5jv7-7672 in your dependencies?
Find it across PyPI, including transitive dependencies.