{"slug": "cruising-for-shells-in-flowise-6-rces-that-rode-flowise-low-and-slow", "title": "Cruising for Shells in Flowise – 6 RCEs That Rode Flowise Low and Slow", "summary": "Researchers identified six new remote code execution (RCE) vulnerabilities in Flowise v3.1.1 and v3.1.2, a generative AI development platform, adding to a series of high and critical security flaws disclosed in its advisories. The findings include account takeover via password reset token exposure (CVE-2025-58434), multiple JavaScript injection points (CVE-2025-59434, CVE-2025-59528, GHSA-7944-7c6r-55vv), and insecure password/email change features. The researchers also noted that a previously patched RCE in the CSVAgent node (CVE-2026-41264) was insufficiently fixed, allowing additional attack vectors.", "body_md": "With the plethora of LLMs and AI products available today, it is not uncommon for developers to be subscribed to multiple services at once. Although these services can be very powerful individually, stringing them together into a single, coherent workflow is often anything but straightforward. So it came as no surprise to us that [Flowise](https://flowiseai.com/) has become one of the top GitHub repositories in this space.\n\nFlowise advertises itself as a \"generative AI development platform for building AI Agents and LLM workflows\".It offers a self-hosted option, as well as a cloud/enterprise plan where users can pay for support and additional enterprise features such as multiple workspaces.\n\nImagine our surprise when we navigated to Flowise's [security advisories](https://github.com/FlowiseAI/Flowise/security) on GitHub and saw that it was full of high and critical vulnerabilities.\n\nAs we reviewed these advisories, our curiosity was piqued even further, and we decided to spend some time reviewing the codebase as well.\n\nMost of the patched issues were of high or critical severity, and the technical details behind them were alarming.\n\nFor example, [CVE-2025-58434](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-wgpv-6j63-x5ph) described how the password reset flow allowed account takeovers.This was due to its original implementation sending the password reset token in the response when requesting a password reset token for an email address of a registered user.\n\nThen there are also multiple instances where user input was executed as pure JavaScript, as seen in: [CVE-2025-59434](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-435c-mg9p-fv22), [CVE-2025-59528](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-3gcm-f6qx-ff7p), [GHSA-7944-7c6r-55vv](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-7944-7c6r-55vv), and many more.\n\nThere were also account-related issues, which can be used as part of an exploit chain. For example, the [password change feature](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-fjh6-8679-9pch) did not require the user to re-enter their password.The [email change feature](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-x39m-3393-3qp4) also had a similar issue.\n\nFlowise's custom Model Context Protocol (MCP) node has also been associated with multiple prior Remote Code Execution (RCE) vulnerabilities, including [CVE-2026-40933](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-c9gw-hvqq-f33r), [CVE-2026-41268](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-cvrr-qhgw-2mm6), [CVE-2025-59528](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-3gcm-f6qx-ff7p), and [GHSA-6933-jpx5-q87q](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-6933-jpx5-q87q). These issues reflect a broader, systemic problem across the AI industry involving the insecure use of [ stdio MCP servers](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio), as discussed in\n\nHaving reviewed all the low-hanging fruit covered thus far, we were determined to sweep the codebase for further vulnerabilities, with a particular focus on identifying Remote Code Execution (RCE) issues. After diving into this massive codebase, we were able to identify 6 more ways to achieve RCE in Flowise ** v3.1.1** and\n\n`v3.1.2`\n\nAs we were writing up this post after having our submissions accepted, Flowise [published](https://github.com/FlowiseAI/Flowise/security/advisories?state=published) a batch of vulnerabilities that were reported by ZDI and other researchers. These were vulnerabilities that affected versions prior to `3.1.0`\n\n. Interestingly, [CVE-2026-41264](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-3hjv-c53m-58jj) was an RCE vulnerability in the `CSVAgent`\n\nnode, which was what we reported as well. This meant that the patch was insufficient, and we were able to find additional vectors to exploit the issue in the patched version.\n\n[CVE-2025-26319](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-5w3r-f6gm-c25w) describes a sandbox escape vulnerability regarding the use of [Flowise's nodevm](https://github.com/FlowiseAI/nodevm), a fork of the insecure\n\n`vm2`\n\nsandbox`puppeteer`\n\nand `playwright`\n\nmodules that were permitted within the sandbox. `node-fetch`\n\n, `axios`\n\n, and `moment`\n\nby default`moment`\n\nvulnerability `CVE-2022-24785`\n\n[CVE-2026-41268](https://github.com/FlowiseAI/Flowise/security/advisories/GHSA-cvrr-qhgw-2mm6) exploited the configuration of a Flowise Custom MCP node to inject a `NODE_OPTIONS`\n\nenvironment variable for a spawned `node`\n\nprocess. [A part of Flowise's patch was to include NODE_OPTIONS in a denylist validation check](https://github.com/FlowiseAI/Flowise/pull/5667), but from our\n\nThe other three RCE vulnerabilities we reported were novel and did not have previously documented variants. We identified multiple instances where Flowise permitted users to supply arbitrary options when initialising the [TypeORM DataSource class](https://typeorm.io/docs/data-source/data-source), enabling exploitation of parameters such as\n\n`entities`\n\nto load and execute arbitrary JavaScript code. The SQL Database Chain and SQLite Record Manager nodes also allowed users to write a SQLite database to an arbitrary file path. We exploited this capability to create a polyglot shell script that was subsequently executed by `chromium`\n\nwhen launched via `puppeteer`\n\n.The following sections provide a technical analysis of all RCE vulnerabilities that we identified in Flowise.\n\nYou might be wondering what [pandas](https://pandas.pydata.org/) has to do with this Node.js codebase.\n\nAs it turns out, there were a few occurrences where [pyodide](https://pyodide.org/en/stable/) was used to run Python code. This is because Flowise allows users to write their own `pandas`\n\ncode to process CSV files, if they choose to.\n\nOne example is the `CSVAgent`\n\nnode, which can be added to a Chatflow:\n\nWe observed that when creating a `CSVAgent`\n\nnode in Flowise, there are 2 sources that we can influence:\n\n`csvFileBase64`\n\n- the uploaded CSV file that gets processed.`customReadCSVFunc`\n\n- the `pandas`\n\nPython code.The first source is `csvFileBase64`\n\n, which comes from the uploaded CSV file:\n\nThe second source is `customReadCSVFunc`\n\n, which is entered through the `Additional Parameters`\n\nwindow:\n\nThe maintainers know how dangerous allowing users to execute Python code is, so in order to mitigate against RCEs, the `validatePythonCodeForDataFrame()`\n\nfunction was used to validate user input:\n\nEventually, both sources end up in this Python code that gets executed via `pyodide.runPythonAsync()`\n\n:\n\nThe `base64String`\n\nvariable was not useful to us as the input was base64-encoded before it reached this sink. This encoded string then gets decoded by the Python code.\n\nSince this was a dead-end, we explored `customReadCSVFunc`\n\ninstead.\n\nOne way to exploit this sink would be to look for a bypass in the denylist. The good thing is, if we find a bypass, exploitation should be straightforward since the code is run directly on the server.\n\nAlternatively, we can go for a clean exploit by leveraging `pandas`\n\nitself.`pd.read_pickle()`\n\nis a prime candidate since we can control the functions called from `pd`\n\n.`read_pickle()`\n\nis somewhat of a wrapper that calls `pickle.load()`\n\n, so in theory we can achieve RCE since we can specify what gets unpickled.\n\nFirst, we generate the base64-encoded pickled RCE payload that sends a reverse shell to our specified host and port:\n\nWe are using a base64-encoded payload because the raw byte string contains null bytes, which will break the exploit later.\n\nBefore using the payload directly in the `customReadCSVFunc`\n\nvariable to perform `pd.read_pickle()`\n\n, we need to take care of a few constraints:\n\n`read_pickle()`\n\nonly `pickle.load()`\n\nexpects a file handler as well.`import`\n\nor other related reserved words to use `BytesIO`\n\nfor feeding an object into `read_pickle()`\n\n.`open()`\n\nor other related functions to write the payload to disk to obtain a file handler either.`pyodide`\n\nsandbox does not have raw socket capabilities.`pyodide.http.pyfetch`\n\n, which we are unable to due to the need for `import`\n\n.So, one way to overcome this is to create a custom class that simulates `BytesIO`\n\n.The main functions called by `read_pickle()`\n\nare `read()`\n\nand `readline()`\n\n, so we just need to make sure they exist:\n\nCombining this `MiniBytesIO`\n\nclass with the `read_pickle()`\n\npayload gives us the final PoC.\n\n**PoC**\n\nOver at Flowise, authenticate and create a new Chatflow:\n\nDrag a `CSV Agent`\n\nnode onto the canvas:\n\nClick on `Additional Parameters` and fill the PoC in:\n\nClose the window and click the `Save`\n\nicon on the top right. Then, note the UUID in the current URL, which will be used to trigger the Chatflow later.\n\nStart a listening shell, then, in another terminal, send a `curl`\n\ncommand to the following URL (replacing `<UUID>`\n\nwith your UUID) to start the Chatflow and trigger the RCE:\n\nAfter discovering this vulnerability through manual analysis, we fed this information into Claude to look for variants. It flagged another source (`AirtableAgent`\n\n) that also utilised Pyodide to execute Python code, but in that case, user input was passed as an encoded base64 string (similar to the `base64String`\n\nvariable we previously saw):\n\nUnfortunately, as we determined earlier, this source is not exploitable, since we would not be able to break out of the quotes.\n\nBesides looking for variants, Claude also pointed us to an alternative PoC that can be used to exploit the `CSVAgent`\n\nsink. Instead of using `read_pickle()`\n\n, we can simply \"import\" the `os`\n\nmodule from `pandas.io.common.os`\n\nand this will let us execute `os.system()`\n\nwithout hitting the denylist:\n\nThe first [patch](https://github.com/FlowiseAI/Flowise/pull/6257/changes) implemented by the developers was flawed, as it only added to the denylist:\n\nAlso ensuring that the input starts with `read_csv()`\n\n:\n\nThe new constraints were thus:\n\n`read_csv(`\n\n`read_pickle`\n\nHowever, this patch was bypassed by using the following payload:\n\nThis payload satisfied the constraints, and also did not violate the `os.`\n\nchecks.\n\nSubsequently, the developers pushed a separate [patch](https://github.com/FlowiseAI/Flowise/pull/6313) which heavily restricted the input to ensure that `read_csv`\n\nis the only call the user is allowed to invoke.\n\nEventually, the entire CSVAgent and AirtableAgent files were [removed](https://github.com/FlowiseAI/Flowise/pull/6499), as there was an [issue](https://github.com/FlowiseAI/Flowise/pull/6476) with NFKC normalization.\n\nAs mentioned earlier in this article, Flowise supports the execution of custom JavaScript code using the `POST /api/v1/node-custom-function`\n\nendpoint, as demonstrated in the following request and response.\n\nResponse for the above request\n\nThis custom JavaScript code was executed in a sandbox environment, defaulting to a [fork of ](https://github.com/FlowiseAI/nodevm). The\n\n`patriksimek/vm2`\n\nversion `3.9.25`\n\n`patriksimek/vm2`\n\n`The library contains critical security issues and should not be used in production. Maintenance has been discontinued. Consider migrating to ``isolated-vm`\n\n.\n\nFlowise's fork of `vm2`\n\nwas outdated and vulnerable to [ CVE-2026-22709](https://github.com/patriksimek/vm2/security/advisories/GHSA-99p7-6v5w-7xg8). The following proof-of-concept demonstrates exploiting\n\n`CVE-2026-22709`\n\nto achieve RCE on Flowise version `3.1.1`\n\n.However, we decided to try and identify a sandbox escape specific to Flowise to demonstrate the inherent risk of using the `vm2`\n\nsandbox in a production context. Early into our investigation, we discovered the `axios`\n\n, `moment`\n\nand `node-fetch`\n\nmodules were allowed by default within the `vm2`\n\nsandbox.\n\n*https://github.com/FlowiseAI/Flowise/blob/flowise%403.1.1/packages/components/src/utils.ts#L124*\n\n<1> Allows custom JavaScript code to use the `axios`\n\n, `moment`\n\nand `node-fetch`\n\ndependencies by default inside the `vm2`\n\nsandbox.\n\n<2> If `useSandbox=false`\n\nor the [E2B api key](https://e2b.dev/docs/api-key) were not set, then it defaults to using the `vm2`\n\nsandbox.\n\nIncluding these external dependencies introduces a potential bypass of the `vm2`\n\nsandbox. The `vm2`\n\nsandbox relies on [JavaScript proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy) to intercept interactions between the sandbox and the host environment. However, built-in functions within imported external dependencies are not proxied, which could allow code execution outside the `vm2`\n\nsandbox if a code execution sink exists.\n\nNotably, the `moment`\n\ndependency had a previously reported path traversal vulnerability ([ CVE-2022-24785](https://github.com/moment/moment/security/advisories/GHSA-8hfj-j24r-96c4)) that could lead to RCE when user input is passed to the\n\n`locale`\n\nfunction. The patch for `CVE-2022-24785`\n\nimplemented a regex check to disallow `/`\n\nor `\\`\n\ncharacters within a locale name, as shown in the code snippet below.*The vulnerable snippet and patch for CVE-2022-24785 in moment*\n\n<1> Performs a regex check to disallow `/`\n\nor `\\`\n\ncharacters within the provided locale name.\n\n<2> The vulnerable sink that introduced `CVE-2022-24785`\n\n.\n\nFlowise used `moment`\n\nversion `v2.29.3`\n\n, which had the `CVE-2022-24785`\n\npatch applied. However, the patch is ineffective in preventing directory traversal in a sandbox context. The validation function uses the `match`\n\nfunction from the provided object, so an object with a `match`\n\nfunction that always returns `true`\n\nwould bypass the validation check, as shown in the following proof-of-concept script.\n\n<1> Bypasses the validation check for `CVE-2022-24785`\n\n.\n\nSince we had achieved access to a `require`\n\nsink within the `vm2`\n\nsandbox, the next goal was to discover a method to save our payload to the local file system. Of note was the File Uploader for a Datastore, where we found that the uploaded file was saved to `/root/.flowise/storage/{organisation_id}/docustore/{store_id}/{filename}`\n\non our Docker deployment using the default `STORAGE_TYPE=local`\n\nstorage type, as shown below along with the uploaded JavaScript payload.\n\n<1> The uploaded JavaScript file using the File Uploader.\n\n*The contents of rce.js that contained a reverse shell payload (nc is installed by default on the Docker deployment).*\n\nWe could retrieve the organisation ID after authentication and viewing the response from the `POST /api/v1/auth/login`\n\nendpoint and the store ID after uploading the file from the `POST /api/v1/document-store/loader/process/{loader_id}`\n\nendpoint, as shown in the responses below.\n\n*The response from POST /api/v1/auth/login after a successful authentication attempt.*\n\n<1> The organisation ID.\n\n*The response from POST /api/v1/document-store/loader/process/{loader_id} after uploading the rce.js payload using the File Uploader on the UI*\n\n<1> The store ID.\n\nThe GIF below demonstrates exploiting this sandbox escape by creating a Custom Function node in an Agentflow, which calls the vulnerable `POST /api/v1/node-custom-function`\n\nendpoin\n\nThe sandbox escape vulnerability was initially reported to Flowise on 10 April 2026. The Flowise team originally attributed the root cause to the outdated `vm2`\n\nsandbox and believed that updating to the latest version resolved it, as shown in the screenshot below.\n\nAs demonstrated in the previous section, the root cause was allowing the `moment`\n\ndependency in the sandboxed environment. We updated Flowise to [commit dddfb3c90eec900d747790a439bd362a764039cd](https://github.com/FlowiseAI/Flowise/tree/dddfb3c90eec900d747790a439bd362a764039cd) (the latest commit on the\n\n`main`\n\nbranch at the time) to verify the sandbox escape and discovered that the `vm2`\n\nsandbox was disabled by default due to changes in This breaking change complicated the process of reconfirming the sandbox escape vulnerability. However, we identified that the following files invoke the `executeJavaScriptCode`\n\nfunction with the `useSandbox=false`\n\noption that executed code using the `vm2`\n\nsandbox:\n\nDuring the investigation of the above files, an injection issue into the sandboxed code was identified. This was caused by insufficient URL validation of the `baseURL`\n\ninput, as demonstrated in the following code snippets.\n\n<1> Use of the broken `isValidURL`\n\nvalidation function, which is shown below.\n\n<2> Injection via the `baseURL`\n\nsetting into the sandboxed code.\n\n<3> Uses the insecure `vm2`\n\nsandbox.\n\n<1> The JavaScript `URL`\n\nclass does not validate characters in the URL hash fragment.\n\nWe exploited this insufficient URL validation to inject our original sandbox escape code (as shown below), confirming that the sandbox escape vulnerability persisted in [commit dddfb3c90eec900d747790a439bd362a764039cd](https://github.com/FlowiseAI/Flowise/tree/dddfb3c90eec900d747790a439bd362a764039cd), which the following GIF confirms.\n\nThis alternative method for exploiting the sandbox escape vulnerability was reported to Flowise on 11 April 2026.\n\nWe recommended that Flowise migrate to a more secure JavaScript sandbox, such as [ isolated-vm](https://github.com/laverdet/isolated-vm), which the\n\n`vm2`\n\nmaintainers themselves recommend as a more robust alternative`vm2`\n\nand `moment`\n\nfrom the list of allowed sandbox dependencies`axios`\n\nand `node-fetch`\n\ndependencies, we continue to discourage reliance on `vm2`\n\n, given how Flowise supports connecting to custom Model Context Protocol (MCP) servers via the \"Custom MCP\" node, which leverages the [ @modelcontextprotocol/sdk](https://github.com/modelcontextprotocol/typescript-sdk) dependency. By default, the\n\n`CUSTOM_MCP_PROTOCOL=stdio`\n\nenvironment variable enables the use of the `StdioClientTransport`\n\nMCP client<1> Disallows absolute UNIX paths for the input script file.\n\n<2> Inadequate denylist of dangerous environment variables.\n\n<3> Allows the use of the `node`\n\nand `python3`\n\ncommands.\n\nFrom [our previous research on exploiting environment variables](https://www.elttam.com/blog/env), it was evident that the environment variable denylist was insufficient to prevent remote execution of arbitrary code. However, `perl`\n\n— our original method for achieving RCE when users could control `python`\n\nenvironment variables — was not installed on the [ flowiseai/flowise:3.1.2 Docker image](https://hub.docker.com/layers/flowiseai/flowise/3.1.2/images/sha256-ddba104d8e50fbc1e72c6fe021d012be83e66d78d26816e1a6a3fddab4212eff). Since our prior research,\n\n`@joern`\n\nimproved upon our findings, identifying a method that does not require `perl`\n\nWe utilised `@joern`\n\n's method in the following MCP configuration payload; the GIF below demonstrates achieving RCE.\n\nAlternatively, we observed that the spawned MCP server process on the [ flowiseai/flowise:3.1.2 Docker image](https://hub.docker.com/layers/flowiseai/flowise/3.1.2/images/sha256-ddba104d8e50fbc1e72c6fe021d012be83e66d78d26816e1a6a3fddab4212eff) did not set the\n\n`WORKDIR`\n\nand defaulted to `/`\n\nas the working directory, allowing the use of relative paths to access arbitrary files on the filesystem and bypass Flowise’s absolute path validation checks. We exploited this by setting the input script for the `node`\n\ncommand to `proc/self/environ`\n\nand overwriting the `HOME`\n\nenvironment variable, transforming `/proc/self/environ`\n\ninto a valid JavaScript file. This technique is demonstrated in the MCP configuration and GIF below.This issue was patched in [PR #6471](https://github.com/FlowiseAI/Flowise/pull/6471), which introduced an allowlist for permitted environment variables and [changed the default transport mode](https://github.com/FlowiseAI/Flowise/pull/6471/changes#diff-82430a299b44908325143d6e577e38f2505f6a7ceccb57e41e68eaad98850b21R186) from the insecure `stdio`\n\nto `sse`\n\n. We consider this sufficient to resolve the issue, as users must now explicitly opt into the insecure transport by setting `CUSTOM_MCP_PROTOCOL=stdio`\n\n.\n\nHowever, we found a way to bypass the new environment variable allowlist when `CUSTOM_MCP_PROTOCOL=stdio`\n\nwas set.\n\nAs noted earlier, the [Dockerfile](https://github.com/FlowiseAI/Flowise/blob/ba6a089d2bac19f5c07e8378e78bfd1af5d66019/docker/Dockerfile) published to Flowise's Docker registry does not set a `WORKDIR`\n\n, leaving it at the default of `/`\n\n. This let us bypass the allowlist by reusing the file upload technique from the `vm2`\n\nsandbox escape section to execute an uploaded JavaScript file, as shown in the following payload.\n\nThe following nodes permitted users to specify arbitrary options for the [TypeORM DataSource](https://typeorm.io/docs/data-source/data-source) class via the\n\n`additionalConfig`\n\nnode input:[Reviewing the documentation for DataSource options](https://typeorm.io/docs/data-source/data-source-options) revealed that the\n\n`entities`\n\n, `subscribers`\n\n, and `migrations`\n\noptions could be exploited to achieve RCE by reading a local JavaScript file. We then applied the same local file-saving technique described in our `vm2`\n\nsandbox escape vulnerability to exploit the insecure usage of the `DataSource`\n\nclass`additionalConfig`\n\npayload and GIF.This issue was resolved in [PR #6464](https://github.com/FlowiseAI/Flowise/pull/6464), which introduced a denylist blocking dangerous TypeORM `DataSource`\n\noptions such as `entities`\n\n, `subscribers`\n\n, and `migrations`\n\n. While this mitigation prevents our reported payloads, the RCE could resurface if a future TypeORM release introduces a new dangerous option not covered by the denylist.\n\nFlowise enables the creation of database agents by leveraging [LangChain's SqlDatabaseChain](https://github.com/langchain-ai/langchainjs/blob/589f29ce844eb252c2d5e6b0f8d26de37763a0d7/libs/langchain-classic/src/chains/sql_db/sql_db_chain.ts#L68) through its\n\n<1> Allows connecting to a local SQLite database.\n\n<2> Allows the user to provide a file path without validation.\n\n<3> Initialises an instance of [LangChain's SqlDatabaseChain](https://github.com/langchain-ai/langchainjs/blob/589f29ce844eb252c2d5e6b0f8d26de37763a0d7/libs/langchain-classic/src/chains/sql_db/sql_db_chain.ts#L68).\n\nAllowing connections to a local SQLite database without path validation introduced a critical security risk, as an attacker could write a malicious SQLite database to arbitrary file system locations. Furthermore, the [ flowiseai/flowise:3.1.2 Docker image](https://hub.docker.com/layers/flowiseai/flowise/3.1.2/images/sha256-ddba104d8e50fbc1e72c6fe021d012be83e66d78d26816e1a6a3fddab4212eff) runs as\n\n`root`\n\n, as shown in the `Dockerfile`\n\nbelow, granting write access to the entire file system.[https://github.com/FlowiseAI/Flowise/blob/flowise-components@3.1.2/docker/Dockerfile](https://github.com/FlowiseAI/Flowise/blob/flowise-components%403.1.2/docker/Dockerfile)\n\n<1> Default user for the `node:20-alpine`\n\nimage was `root`\n\nand the current user was not changed to a low-privileged user.\n\nHowever, the following caveats made exploiting the arbitrary file write of SQLite databases more complex:\n\n`BaseLanguageModel`\n\ninput to analyse user prompts and generate SQL queries for execution on the connected database. While Large Language Models (LLMs) could potentially generate malicious SQL queries, most include built-in moderation controls that complicate exploitation.`writefile`\n\nand `load_extension`\n\nSQLite functions were not enabled, which could have been leveraged to achieve RCE.`SQLite format 3`\n\nmagic byte header, which can corrupt most other file types.`PATH`\n\nenvironment variable.To bypass LLM moderation controls and execute arbitrary SQL queries on the connected SQLite database, we leveraged the `basepath`\n\ninput on an OpenAI node to connect to a web server hosting the below Python code that echoed the SQL query from the input prompt.\n\nOur initial method to demonstrate impact involved directly connecting to `/root/.flowise/database.sqlite`\n\n, but this only applied to default Docker deployments that had not modified the [ DATABASE_TYPE environment variable](https://docs.flowiseai.com/configuration/databases). Alternatively, we demonstrated stored Cross-Site Scripting by writing the SQLite database as a\n\n`.html`\n\nfile to `/usr/local/lib/node_modules/flowise/node_modules/flowise-ui/build/`\n\nOur next approach focused on writing a malicious [Embedded JavaScript ( ejs) template](https://ejs.co/), as\n\n`ejs`\n\ntemplates are not affected by the `SQLite format 3`\n\nmagic byte header and the `ejs`\n\nmodule was included as a transitive dependency`@bull-board/express`\n\n`MODE=queue`\n\nand `ENABLE_BULLMQ_DASHBOARD=true`\n\nare set. It used the `ejs`\n\ntemplate engine`@bull-board/ui/index.ejs`\n\nviewSince the `ejs`\n\nattack vector was not viable, we shifted our investigation to identify directories in the Flowise container that loaded shell scripts via `source`\n\n, which does not require execute file permissions. This led to the discovery of the `/etc/chromium/chromium.conf`\n\nfile that is shown below, which is sourced when the Chromium browser is launched.\n\nFurther review of the `/usr/bin/chromium-browser`\n\nexecutable revealed it was a symbolic link to `/usr/lib/chromium/chromium-launcher.sh`\n\n(shown below), which sourced all `/etc/chromium/*.conf`\n\nfiles.\n\n<1> Uses `source`\n\nto load all `.conf`\n\nfiles in the `/etc/chromium/`\n\nfolder.\n\nWe then discovered there was a [Puppeteer Web Scraper node on Flowise](https://github.com/FlowiseAI/Flowise/blob/0c6924bb08a2156513b447d0e600651f29ea5aa8/packages/components/nodes/documentloaders/Puppeteer/Puppeteer.ts), where [Puppeteer](https://pptr.dev/) was configured to launch `/usr/bin/chromium-browser`\n\nvia the `PUPPETEER_EXECUTABLE_PATH`\n\nenvironment variable.\n\nThe next challenge was identifying a method to craft a SQLite database containing a reverse shell payload that would execute when sourced by `chromium-launcher.sh`\n\n. We addressed this by embedding command substitution within a SQLite table name, ensuring the payload executes before `sh`\n\nencounters syntax errors while parsing the remaining database content. The following SQL demonstrates how to create the SQLite database and shell script polyglot file.\n\nTo chain the full exploit together, we first created a Chatflow that leveraged the SQL Database Chain node to write a crafted SQLite database to `/etc/chromium/exploit.conf`\n\n. The RCE payload was subsequently triggered when `/usr/bin/chromium-browser`\n\nwas executed by a Puppeteer Web Scraper node in a separate Chatflow, as demonstrated in the following GIF.\n\nWe were able to bypass Flowise's patch ([PR #6464](https://github.com/FlowiseAI/Flowise/pull/6464)) for this RCE vulnerability. Unfortunately, Flowise had opted to defer patching the bypass, and no fix had been deployed at the time of publishing.\n\n*As this bypass remains unpatched, we have withheld the technical details from this article and left it as an exercise for the reader.*\n\nAfter demonstrating the RCE impact in the SQL Database Chain node, we observed that the SQLite Record Manager node contained a similar weakness: the `database`\n\nproperty could be overwritten via the `additionalConfig`\n\ninput, as shown in the following code snippet.\n\n<1> The `additionalConfig`\n\ninput was user controllable.\n\n<2> The intended SQLite database path.\n\n<3> Keyword argument expansion of the `additionalConfiguration`\n\nvariable was performed after the `database`\n\nvariable, which allows overwriting the preceding `database`\n\nsetting.\n\nOnce again, we were able to write a SQLite database file to an arbitrary location on the file system. However, the payload used for the SQL Database Chain node could not be applied to the SQLite Record Manager node, as we did not have direct control over the executed SQL statements and the `tableName`\n\ninput was restricted by the `/^[a-zA-Z0-9_]+$/`\n\nvalidation pattern, as shown in the code snippet below.\n\n<1> Validates the `tableName`\n\ninput matches the regex pattern `/^[a-zA-Z0-9_]+$/`\n\n.\n\n<2> The SQL command creating the database table, which is not user controllable.\n\n<3> The `this.namespace`\n\nis a user controllable input for the node.\n\nThis presents a challenge, as the `CREATE`\n\nSQL statement embedded within the SQLite database includes `()`\n\ncharacters, which result in syntax errors when the file is interpreted as a shell script. The hexdump output below shows this for a SQLite database created using the default `upsertion_records`\n\ntable name for the SQLite Record Manager node.\n\nThe original SQLite payload mitigated this by constructing the `CREATE`\n\nstatement as a single line and embedding a `#`\n\ncomment within the table name to neutralize the problematic `()`\n\ncharacters. However, this technique is not viable for the SQLite Record Manager node due to regex table name validation.\n\nWe further investigated the raw structure of SQLite database files and used Claude to summarise the cell structure of the `doc_id_index`\n\nentry containing the problematic `()`\n\ncharacters that is shown below.\n\n<1> `\\x2f`\n\nserial type corresponds to a `TEXT`\n\nvalue that is 17 bytes long.\n\nOf particular interest was the length of the header for the table name, where `\\x2f`\n\nis a [varint](https://sqlite.org/fileformat2.html#varint) that decodes to the integer `47`\n\n. In SQLite, these varints are referred to as [serial types](https://sqlite.org/fileformat2.html#serialtype), which encode both the data type and, for `TEXT`\n\nand `BLOB`\n\nvalues, the byte length. Since `47`\n\nis odd and greater than 13, it is a `TEXT`\n\ntype with a decoded byte length of 47−132=17\\frac{47 - 13}{2} = 17247−13=17 (the length of `upsertion_records`\n\n). We identified that the character `'`\n\n(`\\x27`\n\n) decodes to a valid `TEXT`\n\nserial type with a corresponding length of 13 bytes, as demonstrated by the following script.\n\nBy setting the `tableName`\n\ninput to a 13-byte string, we are able to inject a `'`\n\ncharacter into the record header, effectively wrapping the problematic section containing the `()`\n\ncharacters. The quote is then closed using the `namespace`\n\ninput. This allows a reverse shell payload to be injected via `namespace`\n\nafter the closing quote, enabling arbitrary command execution when the SQLite database is interpreted as a shell script during Puppeteer startup, as described in the previous section. The following GIF demonstrates this full exploit chain.\n\nThis vulnerability was resolved by the following `validateSQLitePath`\n\nfunction, introduced in [PR #6464](https://github.com/FlowiseAI/Flowise/pull/6464) to mitigate arbitrary file write against SQLite databases.\n\n<1> Always allow writing the database to the `$HOME/.flowise`\n\nfolder.\n\nWhile we were unable to find a bypass, we remain concerned about allowing users to write SQLite databases to the `$HOME/.flowise`\n\nfolder, and we still recommend that SQLite operations be disabled by default in Flowise.\n\nIn this post, we walked through six RCE vulnerabilities we identified in Flowise, along with several bypasses of existing patches for previously disclosed issues. A recurring theme throughout this research was that fixes relying on denylists, module allowlists, or narrow input validation were repeatedly insufficient. As Flowise and similar AI workflow platforms continue to expand their feature sets, we expect this pattern of incomplete remediation to keep surfacing, particularly around sandboxing, environment configuration, and file handling primitives.\n\nAs part of this research, we also used Claude's publicly available AI security review capabilities to compare its results against our own human-led analysis. Claude identified some genuine security concerns, but the only RCE vulnerability it raised was the outdated `vm2`\n\ndependency, rather than the Flowise-specific sandbox escape we discovered. That said, Claude proved valuable in assisting our testing: it identified variants and explained complex concepts quickly, which helped us uncover alternative exploit methods, as shown in the *RCE via pandas (CSVAgent)* and *RCE via the SQLite Record Manager Node* sections above. This underscores a broader distinction between AI-driven and AI-assisted discovery: in our experience, the latter consistently surfaced the more nuanced security issues.\n\nThanks for reading, and we hope you enjoyed the post.", "url": "https://wpnews.pro/news/cruising-for-shells-in-flowise-6-rces-that-rode-flowise-low-and-slow", "canonical_source": "https://www.elttam.com/blog/cruising-for-shells-in-flowise", "published_at": "2026-08-04 01:21:41+00:00", "updated_at": "2026-08-04 01:58:07.559119+00:00", "lang": "en", "topics": ["ai-safety", "ai-policy"], "entities": ["Flowise", "CVE-2025-58434", "CVE-2025-59434", "CVE-2025-59528", "CVE-2026-41264", "GHSA-7944-7c6r-55vv", "ZDI"], "alternates": {"html": "https://wpnews.pro/news/cruising-for-shells-in-flowise-6-rces-that-rode-flowise-low-and-slow", "markdown": "https://wpnews.pro/news/cruising-for-shells-in-flowise-6-rces-that-rode-flowise-low-and-slow.md", "text": "https://wpnews.pro/news/cruising-for-shells-in-flowise-6-rces-that-rode-flowise-low-and-slow.txt", "jsonld": "https://wpnews.pro/news/cruising-for-shells-in-flowise-6-rces-that-rode-flowise-low-and-slow.jsonld"}}