CVE-2025-71408 Overview
CVE-2025-71408 is an eval injection vulnerability in the Natural Language Toolkit (NLTK) affecting versions before 3.9.3. The flaw resides in the nltk.collocations module, where the __main__ block passes command-line arguments directly to Python's eval() function. An attacker who controls the command-line arguments passed to collocations.py can execute arbitrary Python code, including operating system commands invoked through the os module. The vulnerability is classified under CWE-95 (Improper Neutralization of Directives in Dynamically Evaluated Code).
Critical Impact
Arbitrary Python code execution in the context of the user running python -m nltk.collocations, enabling full local command execution via eval() misuse.
Affected Products
- NLTK (Natural Language Toolkit) versions prior to 3.9.3
- The nltk.collocations module when invoked directly from the command line
- Python applications or workflows that wrap python -m nltk.collocations with untrusted arguments
Discovery Timeline
- 2026-07-24 - CVE-2025-71408 published to NVD
- 2026-07-30 - Last updated in NVD database
Technical Details for CVE-2025-71408
Vulnerability Analysis
The defect lives in nltk/collocations.py. When the module runs as __main__, it constructs a scorer attribute lookup by concatenating "BigramAssocMeasures." with the first command-line argument and passing the result to eval(). Because eval() interprets any valid Python expression, an attacker can escape the intended attribute reference and execute arbitrary code. The same pattern is repeated for the second argument used to select a comparison scorer. No allowlist validation or input sanitization is performed against the user-supplied strings before evaluation.
Root Cause
The root cause is unsafe use of eval() on attacker-controlled input. The original code assumed command-line arguments would be simple attribute names such as likelihood_ratio or raw_freq. Nothing in the code path enforces that assumption. Any Python expression, such as __import__('os').system('id'), resolves and executes when passed to eval().
Attack Vector
Exploitation requires local access and the ability to control arguments passed to python -m nltk.collocations or a script that imports and executes the module's __main__ logic. Common exposure paths include CI/CD pipelines, batch processing jobs, or wrapper scripts that forward user-supplied parameters. Successful exploitation runs code with the privileges of the invoking user.
# Patch from NLTK 3.9.3 - nltk/collocations.py
# Source: https://github.com/nltk/nltk/commit/66f14096d952ec8f04934f515e027534bd4eb0ac
if __name__ == "__main__":
import sys
from nltk.metrics import BigramAssocMeasures
try:
- scorer = eval("BigramAssocMeasures." + sys.argv[1])
+ scorer = getattr(BigramAssocMeasures, sys.argv[1], None)
except IndexError:
scorer = None
try:
- compare_scorer = eval("BigramAssocMeasures." + sys.argv[2])
+ compare_scorer = getattr(BigramAssocMeasures, sys.argv[2], None)
except IndexError:
compare_scorer = None
The patch replaces eval() with getattr(), restricting resolution to real attributes on the BigramAssocMeasures class and eliminating expression evaluation.
Detection Methods for CVE-2025-71408
Indicators of Compromise
- Process execution of python -m nltk.collocations with argument strings containing characters such as (, ), ., __, or quotes rather than simple scorer names
- Child processes spawned from a Python interpreter running nltk.collocations, especially shells (sh, bash), os.system invocations, or network utilities
- Unexpected outbound network connections initiated by Python processes tied to NLTK workflows
- Presence of NLTK versions earlier than 3.9.3 in pip freeze or requirements.txt inventories
Detection Strategies
- Inspect command-line telemetry for nltk.collocations invocations where argv[1] or argv[2] does not match the allowlist of scorer names (chi_sq, dice, fisher, jaccard, likelihood_ratio, mi_like, phi_sq, pmi, poisson_stirling, raw_freq, student_t)
- Alert on Python interpreter processes that spawn shell or system-utility child processes when the parent command line references nltk
- Run software composition analysis to flag repositories and containers pinning nltk<3.9.3
Monitoring Recommendations
- Ingest endpoint process telemetry into a centralized data lake and query for the nltk.collocations command-line pattern with anomalous arguments
- Baseline expected Python child-process behavior in data science and NLP workloads to identify deviations
- Monitor CI/CD job logs for nltk invocations that receive parameters from user-facing or webhook inputs
How to Mitigate CVE-2025-71408
Immediate Actions Required
- Upgrade NLTK to version 3.9.3 or later using pip install --upgrade nltk
- Audit all scripts, containers, and pipelines that invoke python -m nltk.collocations and remove any pathway that forwards untrusted input
- Revoke or rotate credentials accessible to hosts where vulnerable NLTK versions processed untrusted arguments
Patch Information
The fix is delivered in NLTK release 3.9.3 via pull request #3465 and commit 66f14096. The patch replaces the eval() calls in the __main__ block of collocations.py with getattr(), restricting scorer selection to legitimate BigramAssocMeasures attributes. Additional analysis is available in the VulnCheck advisory and the command injection write-up.
Workarounds
- Do not invoke python -m nltk.collocations with arguments derived from untrusted sources until the upgrade is applied
- Wrap invocations with a validator that rejects any argument outside the fixed scorer allowlist
- Restrict execution of NLTK command-line tools to non-privileged service accounts to limit blast radius
# Upgrade NLTK to the patched release
pip install --upgrade 'nltk>=3.9.3'
# Verify the installed version
python -c "import nltk; print(nltk.__version__)"
# Confirm the fix is present (should show getattr, not eval)
python -c "import inspect, nltk.collocations as c; print(inspect.getsource(c).splitlines()[-15:])"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

