GHSA-HJH7-R5W8-5872

Vulnerability from github – Published: 2026-04-22 20:51 – Updated: 2026-04-22 20:51
VLAI?
Summary
SiYuan: Path Traversal via Double URL Encoding in `/export/` Endpoint (Incomplete Fix Bypass for CVE-2026-30869)
Details

Summary

The fix for CVE-2026-30869 in SiYuan v3.5.10 only added a denylist check (IsSensitivePath) but did not address the root cause — a redundant url.PathUnescape() call in serveExport(). An authenticated attacker can use double URL encoding (%252e%252e) to traverse directories and read arbitrary workspace files including the full SQLite database (siyuan.db), kernel log, and all user documents.

Details

In kernel/server/serve.go, the serveExport() function (line 314-320) processes file paths as follows:

filePath := strings.TrimPrefix(c.Request.URL.Path, "/export/")
decodedPath, err := url.PathUnescape(filePath)     // second decode
fullPath := filepath.Join(exportBaseDir, decodedPath)

Go's HTTP server already decodes percent-encoded characters once during request parsing. The additional url.PathUnescape() call creates a double-decode vulnerability:

  1. Attacker sends: GET /export/%252e%252e/siyuan.db
  2. Go HTTP decodes %25%, result: URL.Path = /export/%2e%2e/siyuan.db
  3. Go's path cleaner sees %2e%2e as literal characters (not ..), no redirect occurs
  4. url.PathUnescape("%2e%2e") decodes to ..
  5. filepath.Join(exportBaseDir, "../siyuan.db") resolves to <workspace>/temp/siyuan.db

The CVE-2026-30869 fix added IsSensitivePath() which blocks <workspace>/conf/ and OS-level paths (/etc, /root, etc.). However, it does NOT block: - <workspace>/temp/siyuan.db — full document database - <workspace>/temp/blocktree.db — block tree database - <workspace>/temp/siyuan.log — kernel log - <workspace>/temp/asset_content.db — asset content database

Note: the /appearance/ handler in the same file correctly uses gulu.File.IsSubPath() to validate paths (line 447), but this check is missing from the /export/ handler.

PoC

poc.zip Please extract the uploaded compressed file before proceeding

  1. docker compose up -d --build
  2. sh poc.sh

스크린샷 2026-04-19 오후 5 08 30

Impact

  • Data exfiltration: An authenticated user (including low-privilege Publish/Reader users via the Publish service) can download the entire SQLite document database containing all blocks, documents, attributes, and full-text search indexes.
  • Information disclosure: Kernel log (siyuan.log) leaks internal server paths, versions, configuration details, and error messages.
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/siyuan-note/siyuan/kernel"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "3.6.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-22"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-22T20:51:22Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "### Summary\nThe fix for CVE-2026-30869 in SiYuan v3.5.10 only added a denylist check (`IsSensitivePath`) but did not address the root cause \u2014 a redundant `url.PathUnescape()` call in `serveExport()`. An authenticated attacker can use double URL encoding (`%252e%252e`) to traverse directories and read arbitrary workspace files including the full SQLite database (`siyuan.db`), kernel log, and all user documents.\n\n### Details\nIn `kernel/server/serve.go`, the `serveExport()` function (line 314-320) processes file paths as follows:\n\n```go\nfilePath := strings.TrimPrefix(c.Request.URL.Path, \"/export/\")\ndecodedPath, err := url.PathUnescape(filePath)     // second decode\nfullPath := filepath.Join(exportBaseDir, decodedPath)\n```\n\nGo\u0027s HTTP server already decodes percent-encoded characters once during request parsing. The additional `url.PathUnescape()` call creates a double-decode vulnerability:\n\n1. Attacker sends: `GET /export/%252e%252e/siyuan.db`\n2. Go HTTP decodes `%25` \u2192 `%`, result: `URL.Path = /export/%2e%2e/siyuan.db`\n3. Go\u0027s path cleaner sees `%2e%2e` as literal characters (not `..`), no redirect occurs\n4. `url.PathUnescape(\"%2e%2e\")` decodes to `..`\n5. `filepath.Join(exportBaseDir, \"../siyuan.db\")` resolves to `\u003cworkspace\u003e/temp/siyuan.db`\n\nThe CVE-2026-30869 fix added `IsSensitivePath()` which blocks `\u003cworkspace\u003e/conf/` and OS-level paths (`/etc`, `/root`, etc.). However, it does NOT block:\n- `\u003cworkspace\u003e/temp/siyuan.db` \u2014 full document database\n- `\u003cworkspace\u003e/temp/blocktree.db` \u2014 block tree database\n- `\u003cworkspace\u003e/temp/siyuan.log` \u2014 kernel log\n- `\u003cworkspace\u003e/temp/asset_content.db` \u2014 asset content database\n\nNote: the `/appearance/` handler in the same file correctly uses `gulu.File.IsSubPath()` to validate paths (line 447), but this check is missing from the `/export/` handler.\n\n### PoC\n[poc.zip](https://github.com/user-attachments/files/26866234/poc.zip)\nPlease extract the uploaded compressed file before proceeding\n\n1. docker compose up -d --build\n2. sh poc.sh\n\n\u003cimg width=\"550\" height=\"184\" alt=\"\u1109\u1173\u110f\u1173\u1105\u1175\u11ab\u1109\u1163\u11ba 2026-04-19 \u110b\u1169\u1112\u116e 5 08 30\" src=\"https://github.com/user-attachments/assets/6aea4334-0b5a-4f45-bd1f-ecfad61ba524\" /\u003e\n\n\n\n\n### Impact\n- Data exfiltration: An authenticated user (including low-privilege Publish/Reader users via the Publish service) can download the entire SQLite document database containing all blocks, documents, attributes, and full-text search indexes.\n- Information disclosure: Kernel log (`siyuan.log`) leaks internal server paths, versions, configuration details, and error messages.",
  "id": "GHSA-hjh7-r5w8-5872",
  "modified": "2026-04-22T20:51:22Z",
  "published": "2026-04-22T20:51:22Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-hjh7-r5w8-5872"
    },
    {
      "type": "WEB",
      "url": "https://github.com/siyuan-note/siyuan/commit/bb481e1290c4a34255652ede85a546504505d2a7"
    },
    {
      "type": "ADVISORY",
      "url": "https://github.com/advisories/GHSA-2h2p-mvfx-868w"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/siyuan-note/siyuan"
    },
    {
      "type": "WEB",
      "url": "https://github.com/siyuan-note/siyuan/releases/tag/v3.6.5"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "SiYuan: Path Traversal via Double URL Encoding in `/export/` Endpoint (Incomplete Fix Bypass for CVE-2026-30869)"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Sightings

Author Source Type Date

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.


Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…