CVE-2026-54066

highpriority · track · 1OT risk · elevatedEPSS 0.022 · 82 %ileEUUS
grimoire priorityP2 · Scheduled
Exploitable?
—
no known exploitation
Likely / severe?
2.17% (82nd) EPSS
CVSS 7.5 · high
Is it mine?
2 products
Check your exposure
Who says so?
3 sources
cnnvd · enisa_euvd · osv

EPSS · scored 2026-10-27

SiYuan is an open-source personal knowledge management system. Prior to 3.7.0, the patch for CVE-2026-41894 ("Path Traversal via Double URL Encoding") sanitized the /export/ route but the identical root cause remains in the /assets/*path route. In publish mode (anonymous read-only HTTP endpoint, default port 6808), an unauthenticated remote attacker can read arbitrary files inside WorkspaceDir — including conf/conf.json (which contains the AccessAuthCode SHA256 hash, API token, and sync keys), temp/siyuan.db, temp/blocktree.db, and siyuan.log — by double-URL-encoding .. segments. This vulnerability is fixed in 3.7.0.

01

cvss

Technical severity, 0–10. One row per scoring source.

7.5
010

base score 0–10 · higher = more severe · hover a vector to decode

v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:Nsecondary · ENISA EU
v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:Nsecondary · security-advisories@github.comexploitability 3.9 · impact 3.6
v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:Nsecondary · OSV
v3.17.5highCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N/E:Hsecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: yes · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (2)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-p4m3-mgmm-c664SiYuan: Path Traversal via Double URL Encoding in /assets/*path (publish mode arbitrary file─read), Incomplete fix of CVE-2026-41894 2026-07-10ghsa · osvUS

    ## Summary The patch for CVE-2026-41894 ("Path Traversal via Double URL Encoding") sanitized the `/export/` route but the **identical root cause remains in the `/assets/*path` route**. In publish mode (anonymous read-only HTTP endpoint, default port 6808), an unauthenticated remote attacker can read arbitrary files inside `WorkspaceDir` — including `conf/conf.json` (which contains the `AccessAuthCode` SHA256 hash, API token, and sync keys), `temp/siyuan.db`, `temp/blocktree.db`, and `siyuan.log` — by double-URL-encoding `..` segments. Verified against siyuan v3.6.5: - `GET /assets/%252e%252e/%252e%252e/conf/conf.json` → **HTTP 200, 10349 bytes (conf.json served)** - `GET /export/%252e%252e/%252e%252e/conf/conf.json` → HTTP 401 (patched) - `GET /assets/%2e%2e/conf/conf.json` → HTTP 404 (single-decode handled correctly) ## Vulnerable Code **Step 1 — route & first decode** (`kernel/server/serve.go:587-626`): The router registers `GET /assets/*path` for the publish listener. Gin performs one URL decoding pass on `URL.Path`, so a request for `/assets/%252e%252e/...` yields `context.Param("path") == "/%2e%2e/%2e%2e/conf/conf.json"` — literal `%2e%2e` strings, which `path.Clean` cannot collapse. **Step 2 — second decode via fallback** (`kernel/model/assets.go:536-563`, `GetAssetAbsPath`): ```go p, err := getAssetAbsPath(relativePath) if nil != err { // fallback decoded, e := url.PathUnescape(relativePath) // ← line 548, second decode if nil == e { p, err = getAssetAbsPath(decoded) } } ``` After the fallback decodes `%2e%2e` to `..`, `filepath.Join(DataDir, "../../conf/conf.json")` is `Clean`-ed to `WorkspaceDir/conf/conf.json`, an existing file. **Step 3 — publish-mode access gate fall-through** (`kernel/model/publish_access.go:288`, `CheckAbsPathAccessableByPublishAccess`): ```go if !filelock.IsSubPath(util.DataDir, absPath) { return true // ← fall-through allows anything outside DataDir but inside WorkspaceDir } ``` Because the resolved file is *outside* `DataDir` (it's in `WorkspaceDir`), the gate returns `true` and `IsSensitivePath()` is never invoked — `.db` / `.log` / `conf/` denylists do not apply to the `/assets/` route at all (unlike the patched `/export/` route, which additionally checks `IsSubPath(exportBaseDir, ...)`). **Step 4 — file served** (`http.ServeFile`): the request `URL.Path` contains literal `%2e%2e`, not `..`, so Go's `containsDotDot` guard passes and the file is sent. ## PoC Preconditions: siyuan kernel running with publish mode enabled (`conf.publish.enable = true`). Publish mode is the documented anonymous read-only endpoint for sharing notebooks. ``` $ curl -i "http://victim:6808/assets/%252e%252e/%252e%252e/conf/conf.json" HTTP/1.1 200 OK Content-Length: 10349 Content-Type: application/json ... {"appearance":{...},"editor":{...},"system":{...},"accessAuthCode":"<sha256>","api":{"token":"<api token>"}, ...} ``` Compared with the patched route: ``` $ curl -i "http://victim:6808/export/%252e%252e/%252e%252e/conf/conf.json" HTTP/1.1 401 Unauthorized ``` ## Root Cause Three independent flaws combine: 1. `GetAssetAbsPath` performs a second `url.PathUnescape` as a "compatibility" fallback, re-introducing the double-decode primitive that the CVE-2026-41894 patch eliminated on `/export/`. 2. `CheckAbsPathAccessableByPublishAccess` returns `true` for any path outside `DataDir`, even when that path is still inside `WorkspaceDir` (which contains `conf/conf.json`, `temp/*.db`, `siyuan.log`). 3. The `IsSensitivePath()` denylist applied to `/export/` is not called from the `/assets/` handler. ## Impact Unauthenticated remote arbitrary file read inside `WorkspaceDir`. Confirmed-readable files include: - `conf/conf.json` — `accessAuthCode` SHA256 (offline crackable), API token, S3/WebDAV sync credentials. - `temp/siyuan.db`, `temp/blocktree.db`, `temp/asset_content.db` — full notebook content (SQLite). - `siyuan.log` — internal paths, OS username, plugin info. Compromise of `accessAuthCode` / API token escalates to authenticated kernel API access (full read/write of all notebooks). Compromise of sync credentials escalates beyond the host. ## Fix 1. Remove the `url.PathUnescape` fallback in `GetAssetAbsPath` (assets.go:548), matching the `/export/` patch. 2. In `CheckAbsPathAccessableByPublishAccess`, replace the `IsSubPath(DataDir, ...)` fall-through with an explicit allowlist (only `DataDir` and its publishable subtree) and **always** call `IsSensitivePath()`. 3. Apply `IsSensitivePath()` inside the `/assets/*path` handler in `serve.go` as defense-in-depth. ## Status Privately reported via GitHub Security Advisory. PoC reproduced locally against v3.6.5 (publish port 6808): `GET /assets/%252e%252e/%252e%252e/conf/conf.json` returned HTTP 200 / 10349 bytes.

  • GO-2026-5964SiYuan: Path Traversal via Double URL Encoding in /assets/*path (publish mode arbitrary file─read), Incomplete fix of CVE-2026-41894 in github.com/siyuan-note/siyuan/kernel2026-07-17vendor · osvUS

    SiYuan: Path Traversal via Double URL Encoding in /assets/*path (publish mode arbitrary file─read), Incomplete fix of CVE-2026-41894 in github.com/siyuan-note/siyuan/kernel

06

also known as (5)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2026-06-24modified 2026-10-25
08

references (3)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan