GCVE Workshop - 22 September 2026 (14:00-18:00), Luxembourg Before The Vulnopticon Conference - Registration

GHSA-P3M8-78J2-G5P3

Vulnerability from github – Published: 2026-09-02 15:40 – Updated: 2026-09-02 15:40
VLAI
Summary
NLTK: Default ENFORCE=False Disables All pathsec Security Controls
Details

NLTK's pathsec.py security module defaults to ENFORCE=False (line 24), which means all 8 security validation functions only emit RuntimeWarning instead of raising exceptions when violations are detected.

The pathsec module was introduced as the fix for CVE-2024-39705 (arbitrary code execution via pickle) and CVE-2026-0846 (path traversal). However, with ENFORCE=False as the default:

  1. pathsec.open('/etc/passwd') succeeds (reads the file, emits warning)
  2. pathsec.validate_network_url('http://169.254.169.254/...') succeeds (warning only)
  3. pickle.loads() via nltk.data.load() proceeds despite unsafe source (warning only)

Every security gate follows the same pattern:

ENFORCE = os.environ.get('NLTK_PATHSEC_ENFORCE', '').lower() in ('1', 'true', 'yes')

def validate_something(path):
    if is_violation(path):
        if ENFORCE:
            raise SecurityError('...')  # Only raised when env var is set
        else:
            warnings.warn('...', RuntimeWarning)  # Default: warning only
    # Execution continues regardless

This means the security remediations for CVE-2024-39705 and CVE-2026-0846 are effectively disabled by default. Any user who installed NLTK 3.9.x expecting the security fixes to be active is still vulnerable unless they manually set NLTK_PATHSEC_ENFORCE=1.

PoC:

import nltk.pathsec
import warnings

# Show that ENFORCE is False by default
print(f'ENFORCE = {nltk.pathsec.ENFORCE}')  # False

# Attempt to read /etc/passwd through pathsec -- should be blocked
with warnings.catch_warnings(record=True) as w:
    warnings.simplefilter('always')
    result = nltk.pathsec.open('/etc/passwd', 'r')
    print(f'File opened: {result.name}')  # /etc/passwd
    print(f'Warning emitted: {w[0].message}')  # RuntimeWarning (not an exception)
    # Attack succeeds -- file is readable

The correct default is fail-secure: ENFORCE should be True unless explicitly disabled. The current default makes the security module opt-in rather than opt-out, defeating its purpose.

Suggested fix: Change default to ENFORCE=True. Users who need backwards compatibility can set NLTK_PATHSEC_ENFORCE=0 to explicitly disable.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 3.9.4"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "nltk"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.10.0"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-62388"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-1188"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-09-02T15:40:33Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "NLTK\u0027s pathsec.py security module defaults to ENFORCE=False (line 24), which means all 8 security validation functions only emit RuntimeWarning instead of raising exceptions when violations are detected.\n\nThe pathsec module was introduced as the fix for CVE-2024-39705 (arbitrary code execution via pickle) and CVE-2026-0846 (path traversal). However, with ENFORCE=False as the default:\n\n1. pathsec.open(\u0027/etc/passwd\u0027) succeeds (reads the file, emits warning)\n2. pathsec.validate_network_url(\u0027http://169.254.169.254/...\u0027) succeeds (warning only)\n3. pickle.loads() via nltk.data.load() proceeds despite unsafe source (warning only)\n\nEvery security gate follows the same pattern:\n```python\nENFORCE = os.environ.get(\u0027NLTK_PATHSEC_ENFORCE\u0027, \u0027\u0027).lower() in (\u00271\u0027, \u0027true\u0027, \u0027yes\u0027)\n\ndef validate_something(path):\n    if is_violation(path):\n        if ENFORCE:\n            raise SecurityError(\u0027...\u0027)  # Only raised when env var is set\n        else:\n            warnings.warn(\u0027...\u0027, RuntimeWarning)  # Default: warning only\n    # Execution continues regardless\n```\n\nThis means the security remediations for CVE-2024-39705 and CVE-2026-0846 are effectively disabled by default. Any user who installed NLTK 3.9.x expecting the security fixes to be active is still vulnerable unless they manually set NLTK_PATHSEC_ENFORCE=1.\n\nPoC:\n```python\nimport nltk.pathsec\nimport warnings\n\n# Show that ENFORCE is False by default\nprint(f\u0027ENFORCE = {nltk.pathsec.ENFORCE}\u0027)  # False\n\n# Attempt to read /etc/passwd through pathsec -- should be blocked\nwith warnings.catch_warnings(record=True) as w:\n    warnings.simplefilter(\u0027always\u0027)\n    result = nltk.pathsec.open(\u0027/etc/passwd\u0027, \u0027r\u0027)\n    print(f\u0027File opened: {result.name}\u0027)  # /etc/passwd\n    print(f\u0027Warning emitted: {w[0].message}\u0027)  # RuntimeWarning (not an exception)\n    # Attack succeeds -- file is readable\n```\n\nThe correct default is fail-secure: ENFORCE should be True unless explicitly disabled. The current default makes the security module opt-in rather than opt-out, defeating its purpose.\n\nSuggested fix: Change default to ENFORCE=True. Users who need backwards compatibility can set NLTK_PATHSEC_ENFORCE=0 to explicitly disable.",
  "id": "GHSA-p3m8-78j2-g5p3",
  "modified": "2026-09-02T15:40:33Z",
  "published": "2026-09-02T15:40:33Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/security/advisories/GHSA-p3m8-78j2-g5p3"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-62388"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/pull/3593"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/commit/155e40343cff0bf50d233e274a12e04d1428b1d9"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/nltk/nltk"
    },
    {
      "type": "WEB",
      "url": "https://github.com/nltk/nltk/releases/tag/v3.10.0"
    },
    {
      "type": "WEB",
      "url": "https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-3722.yaml"
    },
    {
      "type": "WEB",
      "url": "https://www.vulncheck.com/advisories/nltk-before-insecure-default-configuration-pathsec"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "NLTK: Default ENFORCE=False Disables All pathsec Security Controls"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…