Jan 21, 2026·6 min read·20 visits
SiYuan Note's API endpoint for copying files failed to validate source paths. An attacker could request the server to copy `/etc/passwd` or `C:\Windows\win.ini` into a public asset directory, effectively turning a file management feature into a full system read primitive. The fix involves a blacklist, which... well, we'll get to that.
A critical logic flaw in SiYuan Note's file handling allows authenticated users to read arbitrary files from the underlying server filesystem. By abusing the 'globalCopyFiles' API, attackers can pull system secrets like SSH keys and password hashes directly into their workspace.
Personal Knowledge Management (PKM) systems like SiYuan are designed to be the 'second brain' for their users. They store your notes, your diagrams, and your deepest secrets. But in CVE-2026-23851, we find that SiYuan got a little too greedy. It didn't just want to store your secrets; it decided it was perfectly entitled to the server's secrets as well.
The vulnerability lies in a seemingly mundane feature: file management. Specifically, the ability to copy files into your workspace. In a local desktop app, this is harmless convenience. But SiYuan is often deployed as a self-hosted web service (Docker, etc.). When you blur the line between 'desktop app logic' and 'web server security', you usually end up with a disaster. In this case, that disaster is a high-severity Arbitrary File Read.
This isn't a complex memory corruption bug where we have to massage the heap. This is a logic flaw pure and simple. It's the digital equivalent of a bank teller who, when asked to transfer money from 'The Vault' to 'My Pocket', checks if the vault exists, nods, and hands you the cash without asking if you actually own the vault.
The root cause resides in the /api/file/globalCopyFiles endpoint. This API is intended to let users organize their assets—perhaps moving an image from a temporary folder into their main notes directory. The request body takes a list of source paths (srcs) and a destination directory.
Here is the logic failure: The application validated that the file existed (using filelock.IsExist), but it never validated where that file was located. It assumed that if a user provided a path, it must be a valid asset within the application's scope. There was no 'jail' or chroot logic enforcing that the source path started with the workspace directory.
Because SiYuan runs with the permissions of the user starting the process (often root in poorly configured Docker containers, or a standard user on desktops), it has read access to the entire OS filesystem. When the application receives a command to copy /etc/shadow, it obediently checks "Does /etc/shadow exist?" (Yes), and then proceeds to copy it to the user's web-accessible asset folder. From there, downloading the stolen file is trivial.
Let's look at the crime scene in kernel/api/file.go. The vulnerable code was startlingly trusting. It iterates through the user-provided list of paths and processes them without sanitization.
// The 'Before' Code
func globalCopyFiles(c *gin.Context) {
// ... argument parsing ...
for _, src := range srcs {
// The only check: Does the file exist?
if !filelock.IsExist(src) {
// handle error
}
// PROCEED TO COPY
// No check if 'src' is inside the workspace!
}
}The developers patched this in commits b2274ba and f8f4b51. Instead of implementing a strict whitelist (e.g., "Source must start with /opt/siyuan/workspace"), they opted for a blacklist approach. They introduced a utility util.IsSensitivePath.
// The 'After' Code
for i, src := range srcs {
absSrc, _ := filepath.Abs(src)
// The new security gate
if util.IsSensitivePath(absSrc) {
// Log error: "refuse to copy sensitive file"
return
}
// Proceed...
}And what does IsSensitivePath do? It checks the path against a hardcoded list of "naughty" strings: /etc/passwd, /root, .ssh, .env, etc. As any security researcher knows, blacklists are like playing Whac-A-Mole with a hydra.
Exploiting this is trivially easy once you have authentication. The attack vector is a standard HTTP POST request. You don't need special tools; curl or Burp Suite is enough.
/etc/passwd or /root/.ssh/id_rsa. On Windows, C:\Windows\win.ini.POST /api/file/globalCopyFiles HTTP/1.1
Host: vulnerable-siyuan.local
Content-Type: application/json
Cookie: session=...
{
"srcs": ["/etc/passwd", "/proc/self/environ"],
"destDir": "/public/assets/stolen_loot"
}The server responds with 200 OK. The file /etc/passwd is now sitting inside your SiYuan notebook assets. You simply browse to /assets/stolen_loot/passwd in your browser to download it. If you grab /proc/self/environ, you might find AWS keys, database credentials, or API secrets passed to the container.
> [!NOTE] > Researcher's View: The scariest part of this exploit is its reliability. There are no race conditions or memory offsets. It works 100% of the time on vulnerable versions.
The official fix in version 3.5.4 is to upgrade. The developers added the IsSensitivePath check. If you are running an older version, you are exposed.
/etc/shadow at the OS level, the application vulnerability matters less.I have to be critical of the patch method here. Blacklisting is historically fragile. The current patch blocks specific extensions (.pem, .key, .gpg) and specific directories (/etc, /root).
Re-exploitation Potential:
mysymlink -> /etc/passwd inside the workspace and ask to copy that, does it block it?/var/log? What about /home/user/.bash_history? If it's not on the blacklist, it's fair game./proc filesystem is a treasure trove of information that is hard to fully blacklist.The robust fix would have been to enforce a root directory (Chroot/Jail) and reject any path that resolves outside of the defined workspace. Until then, we trust the blacklist.
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:N/VA:N/SC:H/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
SiYuan siyuan-note | < 3.5.4 | 3.5.4 |
| Attribute | Detail |
|---|---|
| CWE | CWE-22 (Path Traversal) |
| CVSS v4.0 | 8.3 (High) |
| Attack Vector | Network (Authenticated) |
| Impact | High (Confidentiality) |
| Patch Status | Fixed in v3.5.4 (Blacklist implementation) |
| EPSS Score | 0.04% (Low immediate mass-exploit risk) |
The application does not properly validate input paths, allowing access to files outside the restricted directory.