Neutron, our AI engine, scored 97.1% on UC Berkeley's CyberGym benchmark. Learn more

Deep Dive

Deep Dive

Flowise RCE Exploited by the Greeter Framework (CVE-2024-58351)

Flowise before 2.1.4 lets overrideConfig replace the JavaScript of a Custom JS Function node; the Greeter framework exploited it, and 2.1.4 fixes it.

TL;DR

  • CVE-2024-58351 lets a prediction request replace the JavaScript of a Flowise Custom JS Function node, and the server runs that code. The flaw is a missing allow-list on the overrideConfig request field.
  • Every Flowise release before 2.1.4 is affected. Era counted 4,372 internet-facing Flowise instances, and 703 of the 3,277 whose version it read had no authentication.
  • VulnCheck rates the flaw CVSS v3.1 9.8 Critical. Greeter exploited it on honeypots from July to October 2026, and VulnCheck KEV lists it. CISA's KEV catalog does not list it as of November 8, 2026.
  • Upgrade to 2.1.4 or later, preferably to 3.1.4, which also carries the 2026 hardening of the same code path. See "How do I fix it?".

Am I affected?

You are affected if you run a Flowise release before 2.1.4 and untrusted users can reach its prediction API. All of these conditions must hold:

  • You run Flowise before 2.1.4, from the npm package flowise, an official Docker image or a product that bundles it.
  • The prediction endpoint POST /api/v1/prediction/<chatflow-id> is reachable by untrusted users, either directly or through an embedded chat widget.
  • A chatflow those users can reach contains a Custom JS Function node, or they can create one because app-level authentication is off.

App-level authentication in the affected 1.x and 2.0.x releases is the FLOWISE_USERNAME and FLOWISE_PASSWORD pair, and it is disabled when both variables are unset. Era's honeypots ran Flowise 1.8.2 with both variables undefined, and the recorded attacks used a chatflow with a Custom JS Function node.

The unauthenticated case is common in the exposed population. Era's census found 4,372 Flowise instances on the internet and read a version from 3,277. Of those, 703 had no authentication, and 46 of the 703 matched the vulnerable setup it had recreated.

Bar chart of 4,372 observed Flowise instances, 3,277 with a readable version, 703 without authentication, and 46 matching the vulnerable setup
Figure 1: The exposed Flowise fleet Era observed, and the unauthenticated subset that matches this flaw. Source: Era Security, November 3, 2026

Branch Affected versions Fixed version
1.x every release 2.1.4 (2024-11-13)
2.0.x every release 2.1.4 (2024-11-13)
2.1.x 2.1.0 through 2.1.3 2.1.4 (2024-11-13)
2.1.4 and later not affected by CVE-2024-58351 run 3.1.4 or later for the 2026 hardening

Check the package version on every install, including containers and copies bundled inside another product:

# Global npm install
npm ls -g flowise

# Project install
npm ls flowise

# Docker: read the version inside the running container
docker exec <container> cat /usr/local/lib/node_modules/flowise/package.json | grep '"version"'

# Running instance
curl -s http://localhost:3000/api/v1/version

The version endpoint reads the nearest package.json on the server, so it reports the package the process is actually running. A container image tag is not enough when the image was built from an older package.

How do I fix it?

Upgrade to 2.1.4 or later, and use the current 3.1.4 release because it includes two later hardening commits to the same function. Then confirm that configuration override is off for every chatflow.

  1. Upgrade the package. For a global install, run npm install -g flowise@3.1.4. For a project install, run npm install flowise@3.1.4. For Docker, pull the current flowiseai/flowise image and recreate the container, keeping its data volume.
  2. Open each chatflow and check Settings, then Configuration, then the Security tab. Override config is disabled by default after 2.1.4. Enable only the node parameters that must be overridable, because the allow-list is stored per chatflow and per node type.
  3. Put authentication in front of the instance. Set FLOWISE_USERNAME and FLOWISE_PASSWORD on the affected lines, or create the admin account on 3.x, and restrict the prediction API to the networks that need it.
  4. If you cannot upgrade yet, block POST /api/v1/prediction for untrusted clients at the reverse proxy. Disable public chatflows, and remove Custom JS Function nodes from any flow those clients can reach. These steps remove the entry points; they do not fix the code.

The workaround has limits. Authentication alone does not remove the flaw. Any account that can create a chatflow can add a Custom JS Function node. On the affected 1.x and 2.0.x lines, app authentication is a single administrator account. A leaked administrator password is therefore enough, which is why the upgrade is the fix and the proxy block is temporary.

What is the root cause?

The prediction API treats a request field as trusted configuration, and that configuration can replace executable code. Flowise chatflows are graphs of nodes, and the Custom JS Function node has an input named javascriptFunction whose value is JavaScript source. Before 2.1.4, the request body's overrideConfig was applied to every node input with no allow-list. This code is quoted from packages/server/src/utils/index.ts at Flowise 2.1.3:

export const replaceInputsWithConfig = (flowNodeData: INodeData, overrideConfig: ICommonObject) => {
    const types = 'inputs'

    const getParamValues = (inputsObj: ICommonObject) => {
        for (const config in overrideConfig) {
            // If overrideConfig[key] is object
            if (overrideConfig[config] && typeof overrideConfig[config] === 'object') {
                const nodeIds = Object.keys(overrideConfig[config])
                if (nodeIds.includes(flowNodeData.id)) {
                    inputsObj[config] = overrideConfig[config][flowNodeData.id]
                    continue
                } else if (nodeIds.some((nodeId) => nodeId.includes(flowNodeData.name))) {
                    /*
                     * "systemMessagePrompt": {
                     *   "chatPromptTemplate_0": "You are an assistant" <---- continue for loop if current node is chatPromptTemplate_1
                     * }
                     */
                    continue
                }
            }

            let paramValue = inputsObj[config]
            const overrideConfigValue = overrideConfig[config]
            if (overrideConfigValue) {
                if (typeof overrideConfigValue === 'object') {
                    switch (typeof paramValue) {
                        case 'string':
                            if (paramValue.startsWith('{') && paramValue.endsWith('}')) {
                                try {
                                    paramValue = Object.assign({}, JSON.parse(paramValue), overrideConfigValue)
                                    break
                                } catch (e) {
                                    // ignore
                                }
                            }
                            paramValue = overrideConfigValue
                            break
                        case 'object':
                            paramValue = Object.assign({}, paramValue, overrideConfigValue)
                            break
                        default:
                            paramValue = overrideConfigValue
                            break
                    }
                } else {
                    paramValue = overrideConfigValue
                }
            }
            // Check if boolean
            if (paramValue === 'true') paramValue = true
            else if (paramValue === 'false') paramValue = false
            inputsObj[config] = paramValue
        }
    }

    const inputsObj = flowNodeData[types] ?? {}

    getParamValues(inputsObj)

    return flowNodeData
}

The loop reads every key the caller sends. When the key is javascriptFunction, the assignment at the end replaces the node's JavaScript with the caller's string. The flow is built from the stored chatflow, the override is applied to the node data in memory, and the node then initializes with the attacker's code. Nothing is written back to the chatflow, which is why the stored definition can look harmless.

The Custom JS Function node wraps that string in an async function and runs it in a vm2 NodeVM, quoted from the node's source at Flowise 1.8.2:

const nodeVMOptions = {
    console: 'inherit',
    sandbox,
    require: {
        external: { modules: deps },
        builtin: builtinDeps
    }
} as any

const vm = new NodeVM(nodeVMOptions)
const response = await vm.run(`module.exports = async function() {${javascriptFunction}}()`, __dirname)

The built-in allow-list covers modules such as http, net and path, not child_process. The vendor advisory lists sandbox escape as an impact of the feature. Era's reconstruction shows the payload running shell commands through child_process.execSync(). vm2 is not a security boundary against hostile JavaScript. Its own maintainers discontinued it in 2023 because escapes kept appearing.

How is it exploited?

An attacker sends the exploit as a normal prediction request, so the stored chatflow never has to contain the malicious code. The request body carries a benign question and an overrideConfig object whose javascriptFunction key holds the stage to run:

{
  "question": "hello",
  "overrideConfig": {
    "javascriptFunction": "<JavaScript that returns the command output>"
  }
}

The steps, as Era reconstructed them from the honeypot traffic:

  1. Reach the instance and, when app authentication is off, use or create a chatflow that contains a Custom JS Function node. The framework's flow was named ghsa-5cph-wvm9-45gj-poc.
  2. Send POST /api/v1/prediction/<chatflow-id> with this request body, using hello as the question.
  3. The server applies the override to the node's javascriptFunction input before the node initializes.
  4. The node runs the caller's JavaScript in the vm2 sandbox. The stage escapes the sandbox and reaches child_process.execSync().
  5. The command output comes back in the prediction response. The framework sends the next command about 3.5 seconds later, again as hello, and repeats the cycle.
  6. Later stages enumerate the host, read environment variables and credential files, query cloud metadata, check for the Docker socket, and prepare persistence and memory-only payloads.

Era counted 4,972 of these messages over two and a half months, from July to October 2026, against seven Flowise 1.8.2 honeypots in four regions. The source address it recorded was the TOR exit node 185.246.188[.]74.

The fix had been available since November 2024, 20 months before the campaign began. The gap is the story: deployments that never applied the 2.1.4 update remained exploitable throughout.

Timeline from the 2.1.4 fix in November 2024 to the Greeter attacks in 2026 and Era's report
Figure 2: The fix shipped in November 2024, 20 months before the Greeter attacks began. Source: Flowise, VulnCheck and Era Security, checked November 8, 2026

How does the attack flow?

A single prediction request carries the attacker's JavaScript, and the server substitutes it into the node without an allow-list. The code then escapes the sandbox and runs commands as the Flowise user.

flowchart LR A["POST /api/v1/prediction
with overrideConfig.javascriptFunction"] --> B["replaceInputsWithConfig applies
the override to the node input
no allow-list"] B --> C["Custom JS Function node
runs the caller's JavaScript
inside vm2"] C --> D["vm2 sandbox escape"] D --> E["child_process.execSync
runs as the Flowise user"] E --> F["Recon, .env and SSH keys,
cloud metadata credentials"] F --> G["Persistence and memory-only
payload staging"]

What can an attacker do?

An attacker who reaches the prediction API runs commands as the Flowise process user and can read every secret that process can reach. Era's honeypot evidence shows:

  • Command execution as the service account. In Era's honeypot the process ran as root with unrestricted sudo, which is a deployment choice, not a property of the flaw.
  • Host reconnaissance: identity, kernel, distribution, processes, listening ports, routes, environment, container identifiers and Linux capabilities.
  • Credential discovery: .env files, SSH private keys and authorized_keys, TLS client certificates, SQL and SQLite files, and the Flowise encryption key at /root/.flowise/encryption.key.
  • Cloud credential theft through the instance metadata service, followed by whatever the workload's cloud identity is allowed to do.
  • Use of a mounted Docker socket, which can lead to control of the host and of other containers.
  • Persistence through cron and systemd units, and staging of payloads that run from memory through memfd_create().

The conditions are those in "Am I affected?". The impact after that depends on the process user, the mounted volumes, the Docker socket and the cloud identity attached to the workload.

No successful compromise of a production Flowise deployment is published in Era's report. The exploitation evidence comes from its honeypots.

Why does the fix work?

The fix makes every override opt-in per chatflow and per node parameter, so a request can no longer replace javascriptFunction unless the operator enabled it. Flowise 2.1.4 adds getAPIOverrideConfig(), which reads the chatflow's API configuration:

export const getAPIOverrideConfig = (chatflow: IChatFlow) => {
    try {
        const apiConfig = chatflow.apiConfig ? JSON.parse(chatflow.apiConfig) : {}
        const nodeOverrides = apiConfig.overrideConfig && apiConfig.overrideConfig.nodes ? apiConfig.overrideConfig.nodes : {}
        const variableOverrides = apiConfig.overrideConfig && apiConfig.overrideConfig.variables ? apiConfig.overrideConfig.variables : []
        const apiOverrideStatus = apiConfig.overrideConfig && apiConfig.overrideConfig.status ? apiConfig.overrideConfig.status : false

        return { nodeOverrides, variableOverrides, apiOverrideStatus }
    } catch (error) {
        return { nodeOverrides: {}, variableOverrides: [], apiOverrideStatus: false }
    }
}

replaceInputsWithConfig() now takes the per-node allow-list and checks it before writing any input:

const isParameterEnabled = (nodeType: string, paramName: string): boolean => {
    if (!nodeOverrides[nodeType]) return false
    const parameter = nodeOverrides[nodeType].find((param: any) => param.name === paramName)
    return parameter?.enabled ?? false
}

// ...
if (!isParameterEnabled(flowNodeData.label, config)) {
    continue
}

An override now needs two opt-ins: overrideConfig.status must be true on the chatflow, and the node type must list the parameter as enabled. The default is false, and javascriptFunction is not on the list unless an operator adds it. The same guard is applied in the prediction, agent-graph and upsert paths.

The first allow-list was not the end of the story. Commit c8282c97fb (2026-02-06) removed a FILE-STORAGE:: exception that let a value skip the enable check. It also added NODE_OPTIONS to the environment variables the MCP tool refuses. Commit 23b997ee5e (2026-05-07) stopped spreading the raw overrideConfig object into the flow data, which had left a second path for caller-controlled keys. Flowise 3.1.4, published 2026-07-29, contains both, which is why it is the release to run.

How do I check if I was compromised?

Search the Flowise request logs and the host for the framework's markers; the campaign leaves distinctive strings even though its chat messages read only hello. Era published these indicators:

  • Prediction requests whose JSON body contains overrideConfig and javascriptFunction.
  • A chatflow named ghsa-5cph-wvm9-45gj-poc.
  • Chat messages containing VP_DOCKER, VP_MEMFD, VP_W_OK or VP_INIT_OK, beacon replies matching vp-alive-[0-9a-f]{8}, and exit reports matching __VP_EXIT_[0-9]+.
  • Process names polld-keeper-{8-hex} and varrun-watcher-{8-hex}.
  • A cron file at /etc/cron.d/syslog-cronrun-{8-hex} or a systemd unit at /etc/systemd/system/taskd-sysrun-{8-hex}.service.
  • The configuration hash 07f8464533b3446e918feff1ea14c4ae, shared by the two persistence components.
  • Connections from the TOR exit node 185.246.188[.]74.

Run these checks on the Flowise host and on any host whose Docker socket the container can reach:

# Flowise request logs (Docker example; adjust the container name)
docker logs <container> 2>&1 | grep -Ei 'overrideConfig|javascriptFunction|ghsa-5cph-wvm9-45gj'

# Persistence artifacts
sudo find /etc/cron.d /etc/systemd/system -maxdepth 1 \( -name 'syslog-cronrun-*' -o -name 'taskd-sysrun-*.service' \)

# Framework processes
ps -eo pid,user,args | grep -E 'polld-keeper-|varrun-watcher-'

A hit is a lead, not proof. Flowise itself uses overrideConfig in its documentation and SDKs, so a request that mentions the field may be legitimate. The combination of the field with javascriptFunction, or with the chatflow name and the VP_ markers, is the stronger signal. Era's evidence comes from honeypots; no production victim's indicators are published.

Timeline

  • 2024-11-13: Flowise 2.1.4 is published with the per-chatflow override allow-list.
  • 2024-11-21: The Flowise advisory GHSA-5cph-wvm9-45gj is published, rating the issue High with a CVSS v4.0 score of 8.6.
  • 2026-02-06: Commit c8282c97fb removes a FILE-STORAGE:: bypass of the allow-list.
  • 2026-05-07: Commit 23b997ee5e stops spreading the raw overrideConfig object into the flow data.
  • 2026-06-20: VulnCheck publishes CVE-2024-58351 with a CVSS v3.1 score of 9.8.
  • 2026-11-03: Era publishes its analysis of the Greeter framework, whose honeypots recorded 4,972 prediction requests from July to October 2026.
  • 2026-11-08: This post is published.

FAQ

Is Flowise 2.1.4 enough?

Yes for CVE-2024-58351, because 2.1.4 adds the allow-list that the flaw bypasses. Run 3.1.4 or later anyway, because two 2026 commits closed bypasses in the first allow-list and removed a second path for caller-controlled keys.

Does authentication stop the attack?

Not by itself. Authentication removes the unauthenticated case, but any account that can create a chatflow can add a Custom JS Function node and send the override. On the affected 1.x and 2.0.x lines app authentication is a single administrator account, so one leaked password is enough.

Do I need a Custom JS Function node in the chatflow?

Yes. The override replaces the value of an input, and javascriptFunction is the Custom JS Function node's input. An unauthenticated attacker can create a flow with that node; the flow observed in Era's honeypot was named ghsa-5cph-wvm9-45gj-poc.

Was this exploited against real organizations?

Era observed the Greeter framework against its own honeypots, not against named victims. It also found 4,372 internet-facing Flowise instances and 46 unauthenticated ones that matched the vulnerable setup it had recreated. That is exposure evidence, not proof of compromise.

Why does Flowise rate the issue High when VulnCheck rates it Critical?

The Flowise advisory is rated High, and its CVSS v4.0 vector uses PR:H, which assumes the attacker already holds high privileges, with a score of 8.6. VulnCheck, the CVE Numbering Authority, uses PR:N and scores it 9.8 on CVSS v3.1 and 9.3 on CVSS v4.0. The exploitation path in this post needs only network access when app authentication is off, which matches the VulnCheck vector.

What should I do if I cannot upgrade this week?

Block POST /api/v1/prediction for untrusted clients at the reverse proxy, turn off public chatflows, and remove Custom JS Function nodes from every flow those clients can reach. Then plan the upgrade, and rotate the credentials the Flowise process can read if the host was exposed.

Find exposed Flowise instances with Ostorlab

Ostorlab's attack surface discovery inventories the internet-facing services in your estate, so you can find the exposed hosts that run Flowise and prioritize the upgrade.

References

  • Flowise advisory GHSA-5cph-wvm9-45gj, published 2024-11-21, with the overrideConfig impact list and the 2.1.4 fix: https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-5cph-wvm9-45gj (checked November 8, 2026)
  • CVE-2024-58351 record, published 2026-06-20, with the CVSS v3.1 9.8 and v4.0 9.3 scores and the CISA ADP SSVC block: https://www.cve.org/CVERecord?id=CVE-2024-58351 (checked November 8, 2026)
  • VulnCheck advisory, "Flowise - Remote Code Execution via overrideConfig Parameter", published 2026-06-20: https://www.vulncheck.com/advisories/flowise-remote-code-execution-via-overrideconfig-parameter (checked November 8, 2026)
  • Flowise 2.1.4 fix commit, "Enable/disable variables in override configuration", 2024-11-13: https://github.com/FlowiseAI/Flowise/commit/537aa51ef8 (checked November 8, 2026)
  • Flowise npm package, version 2.1.4 published 2024-11-13 and the current 3.1.4 published 2026-07-29: https://www.npmjs.com/package/flowise (checked November 8, 2026)
  • Flowise commit c8282c97fb, "Fix Paramater Override Bypass", 2026-02-06: https://github.com/FlowiseAI/Flowise/commit/c8282c97fb (checked November 8, 2026)
  • Flowise commit 23b997ee5e, "remove ungated overrideConfig spread", 2026-05-07: https://github.com/FlowiseAI/Flowise/commit/23b997ee5e (checked November 8, 2026)
  • Flowise 1.8.2 Custom JS Function node source, showing the NodeVM wrapper: https://github.com/FlowiseAI/Flowise/blob/5e4d640ed7/packages/components/nodes/utilities/CustomFunction/CustomFunction.ts (checked November 8, 2026)
  • Era Security, "You Had Me at Hello: Inside The Greeter, A Chinese-Language Framework Targeting AI Infrastructure", 2026-11-03, with the attack stages, exposure counts and indicators: https://era.security/blog/greeter-flowise-ai-infrastructure-attack (checked November 8, 2026)
  • Flowise documentation, "Prediction", showing the endpoint, the overrideConfig field and the default-off configuration override: https://docs.flowiseai.com/using-flowise/prediction (checked November 8, 2026)
  • Flowise documentation, "Application" authentication, showing the FLOWISE_USERNAME and FLOWISE_PASSWORD variables: https://docs.flowiseai.com/configuration/authorization/app-level (checked November 8, 2026)
  • CISA Known Exploited Vulnerabilities catalog, checked for CVE-2024-58351: https://www.cisa.gov/known-exploited-vulnerabilities-catalog (checked November 8, 2026)