CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/lib/metasploit/framework/login_scanner/http.rb
Views: 1904
require 'metasploit/framework/login_scanner/base'1require 'metasploit/framework/login_scanner/rex_socket'23module Metasploit4module Framework5module LoginScanner6#7# HTTP-specific login scanner.8#9class HTTP10include Metasploit::Framework::LoginScanner::Base11include Metasploit::Framework::LoginScanner::RexSocket1213AUTHORIZATION_HEADER = 'WWW-Authenticate'.freeze14DEFAULT_REALM = nil15DEFAULT_PORT = 8016DEFAULT_SSL_PORT = 44317DEFAULT_HTTP_SUCCESS_CODES = [200, 201].append(*(300..309))18DEFAULT_HTTP_NOT_AUTHED_CODES = [401]19LIKELY_PORTS = [80, 443, 8000, 8080]20LIKELY_SERVICE_NAMES = %w[http https]21PRIVATE_TYPES = [:password]22REALM_KEY = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN2324# @!attribute uri25# @return [String] The path and query string on the server to26# authenticate to.27attr_accessor :uri2829# @!attribute uri30# @return [String] HTTP method, e.g. "GET", "POST"31attr_accessor :method3233# @!attribute user_agent34# @return [String] the User-Agent to use for the HTTP requests35attr_accessor :user_agent3637# @!attribute vhost38# @return [String] the Virtual Host name for the target Web Server39attr_accessor :vhost4041# @!attribute evade_uri_encode_mode42# @return [String] The type of URI encoding to use43attr_accessor :evade_uri_encode_mode4445# @!attribute evade_uri_full_url46# @return [Boolean] Whether to use the full URL for all HTTP requests47attr_accessor :evade_uri_full_url4849# @!attribute evade_pad_method_uri_count50# @return [Integer] How many whitespace characters to use between the method and uri51attr_accessor :evade_pad_method_uri_count5253# @!attribute evade_pad_uri_version_count54# @return [Integer] How many whitespace characters to use between the uri and version55attr_accessor :evade_pad_uri_version_count5657# @!attribute evade_pad_method_uri_type58# @return [String] What type of whitespace to use between the method and uri59attr_accessor :evade_pad_method_uri_type6061# @!attribute evade_pad_uri_version_type62# @return [String] What type of whitespace to use between the uri and version63attr_accessor :evade_pad_uri_version_type6465# @!attribute evade_method_random_valid66# @return [Boolean] Whether to use a random, but valid, HTTP method for request67attr_accessor :evade_method_random_valid6869# @!attribute evade_method_random_invalid70# @return [Boolean] Whether to use a random invalid, HTTP method for request71attr_accessor :evade_method_random_invalid7273# @!attribute evade_method_random_case74# @return [Boolean] Whether to use random casing for the HTTP method75attr_accessor :evade_method_random_case7677# @!attribute evade_version_random_valid78# @return [Boolean] Whether to use a random, but valid, HTTP version for request79attr_accessor :evade_version_random_valid8081# @!attribute evade_version_random_invalid82# @return [Boolean] Whether to use a random invalid, HTTP version for request83attr_accessor :evade_version_random_invalid8485# @!attribute evade_uri_dir_self_reference86# @return [Boolean] Whether to insert self-referential directories into the uri87attr_accessor :evade_uri_dir_self_reference8889# @!attribute evade_uri_dir_fake_relative90# @return [Boolean] Whether to insert fake relative directories into the uri91attr_accessor :evade_uri_dir_fake_relative9293# @!attribute evade_uri_use_backslashes94# @return [Boolean] Whether to use back slashes instead of forward slashes in the uri95attr_accessor :evade_uri_use_backslashes9697# @!attribute evade_pad_fake_headers98# @return [Boolean] Whether to insert random, fake headers into the HTTP request99attr_accessor :evade_pad_fake_headers100101# @!attribute evade_pad_fake_headers_count102# @return [Integer] How many fake headers to insert into the HTTP request103attr_accessor :evade_pad_fake_headers_count104105# @!attribute evade_pad_get_params106# @return [Boolean] Whether to insert random, fake query string variables into the request107attr_accessor :evade_pad_get_params108109# @!attribute evade_pad_get_params_count110# @return [Integer] How many fake query string variables to insert into the request111attr_accessor :evade_pad_get_params_count112113# @!attribute evade_pad_post_params114# @return [Boolean] Whether to insert random, fake post variables into the request115attr_accessor :evade_pad_post_params116117# @!attribute evade_pad_post_params_count118# @return [Integer] How many fake post variables to insert into the request119attr_accessor :evade_pad_post_params_count120121# @!attribute evade_shuffle_get_params122# @return [Boolean] Randomize order of GET parameters123attr_accessor :evade_shuffle_get_params124125# @!attribute evade_shuffle_post_params126# @return [Boolean] Randomize order of POST parameters127attr_accessor :evade_shuffle_post_params128129# @!attribute evade_uri_fake_end130# @return [Boolean] Whether to add a fake end of URI (eg: /%20HTTP/1.0/../../)131attr_accessor :evade_uri_fake_end132133# @!attribute evade_uri_fake_params_start134# @return [Boolean] Whether to add a fake start of params to the URI (eg: /%3fa=b/../)135attr_accessor :evade_uri_fake_params_start136137# @!attribute evade_header_folding138# @return [Boolean] Whether to enable folding of HTTP headers139attr_accessor :evade_header_folding140141# @!attribute ntlm_use_ntlmv2_session142# @return [Boolean] Whether to activate the 'Negotiate NTLM2 key' flag, forcing the use of a NTLMv2_session143attr_accessor :ntlm_use_ntlmv2_session144145# @!attribute ntlm_use_ntlmv2146# @return [Boolean] Whether to use NTLMv2 instead of NTLM2_session when 'Negotiate NTLM2' is enabled147attr_accessor :ntlm_use_ntlmv2148149# @!attribute ntlm_send_lm150# @return [Boolean] Whether to always send the LANMAN response (except when NTLMv2_session is specified)151attr_accessor :ntlm_send_lm152153# @!attribute ntlm_send_ntlm154# @return [Boolean] Whether to activate the 'Negotiate NTLM key' flag, indicating the use of NTLM responses155attr_accessor :ntlm_send_ntlm156157# @!attribute ntlm_send_spn158# @return [Boolean] Whether to send an avp of type SPN in the NTLMv2 client blob.159attr_accessor :ntlm_send_spn160161# @!attribute ntlm_use_lm_key162# @return [Boolean] Activate the 'Negotiate Lan Manager Key' flag, using the LM key when the LM response is sent163attr_accessor :ntlm_use_lm_key164165# @!attribute ntlm_domain166# @return [String] The NTLM domain to use during authentication167attr_accessor :ntlm_domain168169# @!attribute digest_auth_iis170# @return [Boolean] Whether to conform to IIS digest authentication mode.171attr_accessor :digest_auth_iis172173# @!attribute http_username174# @return [String]175attr_accessor :http_username176177# @!attribute http_password178# @return [String]179attr_accessor :http_password180181# @!attribute kerberos_authenticator_factory182# @return [Func<username, password, realm> : Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::HTTP] A factory method for creating a kerberos authenticator183attr_accessor :kerberos_authenticator_factory184185# @!attribute keep_connection_alive186# @return [Boolean] Whether to keep the connection open after a successful login187attr_accessor :keep_connection_alive188189# @!attribute http_success_codes190# @return [Array][Int] list of valid http response codes191attr_accessor :http_success_codes192193validate :validate_http_codes194195validates :uri, presence: true, length: { minimum: 1 }196197validates :method,198presence: true,199length: { minimum: 1 }200201# (see Base#check_setup)202def check_setup203http_client = Rex::Proto::Http::Client.new(204host, port, {'Msf' => framework, 'MsfExploit' => framework_module}, ssl, ssl_version, proxies, http_username, http_password205)206request = http_client.request_cgi(207'uri' => uri,208'method' => method209)210211begin212# Use _send_recv instead of send_recv to skip automatic213# authentication214response = http_client._send_recv(request)215rescue ::EOFError, Errno::ETIMEDOUT, OpenSSL::SSL::SSLError, Rex::ConnectionError, ::Timeout::Error216return 'Unable to connect to target'217end218219if authentication_required?(response)220return false221end222223'No authentication required'224end225226# Sends a HTTP request with Rex227#228# @param [Hash] opts native support includes the following (also see Rex::Proto::Http::Request#request_cgi)229# @option opts [String] 'host' The remote host230# @option opts [Integer] 'port' The remote port231# @option opts [Boolean] 'ssl' The SSL setting, TrueClass or FalseClass232# @option opts [String] 'proxies' The proxies setting233# @option opts [Credential] 'credential' A credential object234# @option opts [Rex::Proto::Http::Client] 'http_client' object that can be used by the function235# @option opts ['Hash'] 'context' A context236# @raise [Rex::ConnectionError] One of these errors has occurred: EOFError, Errno::ETIMEDOUT, Rex::ConnectionError, ::Timeout::Error237# @return [Rex::Proto::Http::Response] The HTTP response238# @return [NilClass] An error has occurred while reading the response (see #Rex::Proto::Http::Client#read_response)239def send_request(opts)240close_client = !opts.key?(:http_client)241cli = opts.fetch(:http_client) { create_client(opts) }242243begin244cli.connect245req = cli.request_cgi(opts)246247# Authenticate by default248res = if opts['authenticate'].nil? || opts['authenticate']249cli.send_recv(req)250else251cli._send_recv(req)252end253rescue ::EOFError, Errno::ETIMEDOUT, Errno::ECONNRESET, Rex::ConnectionError, OpenSSL::SSL::SSLError, ::Timeout::Error => e254raise Rex::ConnectionError, e.message255ensure256# If we didn't create the client, don't close it257if close_client258cli.close259end260end261262res263end264265266# Attempt a single login with a single credential against the target.267#268# @param credential [Credential] The credential object to attempt to269# login with.270#271# @return [Result] A Result object indicating success or failure272def attempt_login(credential)273result_opts = {274credential: credential,275status: Metasploit::Model::Login::Status::INCORRECT,276proof: nil,277host: host,278port: port,279protocol: 'tcp'280}281282if ssl283result_opts[:service_name] = 'https'284else285result_opts[:service_name] = 'http'286end287288request_opts = {'credential'=>credential, 'uri'=>uri, 'method'=>method}289if keep_connection_alive290request_opts[:http_client] = create_client(request_opts)291end292293begin294response = send_request(request_opts)295if response && http_success_codes.include?(response.code)296result_opts.merge!(status: Metasploit::Model::Login::Status::SUCCESSFUL, proof: response.headers)297end298rescue Rex::ConnectionError => e299result_opts.merge!(status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e)300rescue ::Rex::Proto::Kerberos::Model::Error::KerberosError => e301mapped_err = Metasploit::Framework::LoginScanner::Kerberos.login_status_for_kerberos_error(e)302result_opts.merge!(status: mapped_err, proof: e)303ensure304if request_opts.key?(:http_client)305if result_opts[:status] == Metasploit::Model::Login::Status::SUCCESSFUL306result_opts[:connection] = request_opts[:http_client]307else308request_opts[:http_client].close309end310end311end312313Result.new(result_opts)314end315316protected317318# Returns a boolean value indicating whether the request requires authentication or not.319#320# @param [Rex::Proto::Http::Response] response The response received from the HTTP endpoint321# @return [Boolean] True if the request required authentication; otherwise false.322def authentication_required?(response)323return false unless response324325self.class::DEFAULT_HTTP_NOT_AUTHED_CODES.include?(response.code) &&326response.headers[self.class::AUTHORIZATION_HEADER]327end328329private330331def create_client(opts)332rhost = opts['host'] || host333rport = opts['rport'] || port334cli_ssl = opts['ssl'] || ssl335cli_ssl_version = opts['ssl_version'] || ssl_version336cli_proxies = opts['proxies'] || proxies337username = opts['credential'] ? opts['credential'].public : http_username338password = opts['credential'] ? opts['credential'].private : http_password339realm = opts['credential'] ? opts['credential'].realm : nil340context = opts['context'] || { 'Msf' => framework, 'MsfExploit' => framework_module}341342kerberos_authenticator = nil343if kerberos_authenticator_factory344kerberos_authenticator = kerberos_authenticator_factory.call(username, password, realm)345end346347http_logger_subscriber = framework_module.nil? ? nil : Rex::Proto::Http::HttpLoggerSubscriber.new(logger: framework_module)348res = nil349cli = Rex::Proto::Http::Client.new(350rhost,351rport,352context,353cli_ssl,354cli_ssl_version,355cli_proxies,356username,357password,358kerberos_authenticator: kerberos_authenticator,359subscriber: http_logger_subscriber360)361configure_http_client(cli)362363if realm364cli.set_config('domain' => realm)365end366367cli368end369370# This method is responsible for mapping the caller's datastore options to the371# Rex::Proto::Http::Client configuration parameters.372def configure_http_client(http_client)373http_client.set_config(374'vhost' => vhost || host,375'agent' => user_agent376)377378possible_params = {379'uri_encode_mode' => evade_uri_encode_mode,380'uri_full_url' => evade_uri_full_url,381'pad_method_uri_count' => evade_pad_method_uri_count,382'pad_uri_version_count' => evade_pad_uri_version_count,383'pad_method_uri_type' => evade_pad_method_uri_type,384'pad_uri_version_type' => evade_pad_uri_version_type,385'method_random_valid' => evade_method_random_valid,386'method_random_invalid' => evade_method_random_invalid,387'method_random_case' => evade_method_random_case,388'version_random_valid' => evade_version_random_valid,389'version_random_invalid' => evade_version_random_invalid,390'uri_dir_self_reference' => evade_uri_dir_self_reference,391'uri_dir_fake_relative' => evade_uri_dir_fake_relative,392'uri_use_backslashes' => evade_uri_use_backslashes,393'pad_fake_headers' => evade_pad_fake_headers,394'pad_fake_headers_count' => evade_pad_fake_headers_count,395'pad_get_params' => evade_pad_get_params,396'pad_get_params_count' => evade_pad_get_params_count,397'pad_post_params' => evade_pad_post_params,398'pad_post_params_count' => evade_pad_post_params_count,399'shuffle_get_params' => evade_shuffle_get_params,400'shuffle_post_params' => evade_shuffle_post_params,401'uri_fake_end' => evade_uri_fake_end,402'uri_fake_params_start' => evade_uri_fake_params_start,403'header_folding' => evade_header_folding,404'usentlm2_session' => ntlm_use_ntlmv2_session,405'use_ntlmv2' => ntlm_use_ntlmv2,406'send_lm' => ntlm_send_lm,407'send_ntlm' => ntlm_send_ntlm,408'SendSPN' => ntlm_send_spn,409'UseLMKey' => ntlm_use_lm_key,410'domain' => ntlm_domain,411'DigestAuthIIS' => digest_auth_iis412}413414# Set the parameter only if it is not nil415possible_params.each_pair do |k,v|416next if v.nil?417http_client.set_config(k => v)418end419420http_client421end422423# This method sets the sane defaults for things424# like timeouts and TCP evasion options425def set_sane_defaults426self.connection_timeout ||= 20427self.uri = '/' if self.uri.blank?428self.method = 'GET' if self.method.blank?429self.http_success_codes = DEFAULT_HTTP_SUCCESS_CODES if self.http_success_codes.nil?430431# Note that this doesn't cover the case where ssl is unset and432# port is something other than a default. In that situation,433# we don't know what the user has in mind so we have to trust434# that they're going to do something sane.435if !(self.ssl) && self.port.nil?436self.port = self.class::DEFAULT_PORT437self.ssl = false438elsif self.ssl && self.port.nil?439self.port = self.class::DEFAULT_SSL_PORT440elsif self.ssl.nil? && self.port == self.class::DEFAULT_PORT441self.ssl = false442elsif self.ssl.nil? && self.port == self.class::DEFAULT_SSL_PORT443self.ssl = true444end445446if self.ssl.nil?447self.ssl = false448end449450nil451end452453# Combine the base URI with the target URI in a sane fashion454#455# @param [Array<String>] target_uri the target URL456# @return [String] the final URL mapped against the base457def normalize_uri(*target_uri)458if target_uri.count == 1459(uri.to_s + '/' + target_uri.first.to_s).gsub(%r{/+}, '/')460else461new_str = target_uri * '/'462new_str = new_str.gsub!('//', '/') while new_str.index('//')463464# Makes sure there's a starting slash465unless new_str[0,1] == '/'466new_str = '/' + new_str467end468469new_str470end471end472473private474475def validate_http_codes476errors.add(:http_success_codes, "HTTP codes must be an Array") unless @http_success_codes.is_a?(Array)477@http_success_codes.each do |code|478next if code >= 200 && code < 400479errors.add(:http_success_codes, "Invalid HTTP code provided #{code}")480end481end482end483end484end485end486487488489