Path: blob/master/lib/metasploit/framework/login_scanner/ldap.rb
19514 views
# frozen_string_literal: true12require 'metasploit/framework/login_scanner/base'3require 'metasploit/framework/ldap/client'45module Metasploit6module Framework7module LoginScanner8class LDAP9include Metasploit::Framework::LoginScanner::Base10include Metasploit::Framework::LDAP::Client11include Msf::Exploit::Remote::LDAP1213LIKELY_PORTS = [ 389, 636 ]14LIKELY_SERVICE_NAMES = [ 'ldap', 'ldaps', 'ldapssl' ]15PRIVATE_TYPES = [:password, :ntlm_hash]1617attr_accessor :opts, :realm_key18# @!attribute use_client_as_proof19# @return [Boolean] If a login is successful and this attribute is true - an LDAP::Client instance is used as proof20attr_accessor :use_client_as_proof2122# This method sets the sane defaults for things23# like timeouts and TCP evasion options24def set_sane_defaults25self.opts ||= {}26self.connection_timeout = 30 if self.connection_timeout.nil?27nil28end2930def attempt_login(credential)31result_opts = {32credential: credential,33status: Metasploit::Model::Login::Status::INCORRECT,34proof: nil,35host: host,36port: port,37protocol: 'tcp',38service_name: 'ldap'39}4041result_opts.merge!(do_login(credential))42Result.new(result_opts)43end4445def do_login(credential)46opts = {47username: credential.public,48password: credential.private,49framework_module: framework_module,50ldap_auth: 'auto'51}.merge(@opts)5253connect_opts = ldap_connect_opts(host, port, connection_timeout, ssl: opts[:ssl], opts: opts)54begin55ldap_client = ldap_open(connect_opts, keep_open: true)56return status_code(ldap_client)57rescue StandardError => e58{ status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }59end60end6162def status_code(ldap_client)63operation_result = ldap_client.get_operation_result.table[:code]64case operation_result65when 066result = { status: Metasploit::Model::Login::Status::SUCCESSFUL }67if use_client_as_proof68result[:proof] = ldap_client69result[:connection] = ldap_client.socket70end71result72else73{ status: Metasploit::Model::Login::Status::INCORRECT, proof: "Bind Result: #{operation_result}" }74end75end7677def each_credential78cred_details.each do |raw_cred|79# This could be a Credential object, or a Credential Core, or an Attempt object80# so make sure that whatever it is, we end up with a Credential.81credential = raw_cred.to_credential8283if opts[:ldap_auth] == Msf::Exploit::Remote::AuthOption::KERBEROS && opts[:ldap_krb5_cname]84# If we're using kerberos auth with a ccache then the password is irrelevant85# Remove it from the credential so we don't store it86credential.private = nil87elsif opts[:ldap_auth] == Msf::Exploit::Remote::AuthOption::SCHANNEL88# If we're using kerberos auth with schannel then the user/password is irrelevant89# Remove the password from the credential so we don't store it90# Note that the username is kept since it is needed for the certificate lookup.91credential.private = nil92end9394if credential.realm.present? && realm_key.present?95credential.realm_key = realm_key96elsif credential.realm.present? && realm_key.blank?97# This service has no realm key, so the realm will be98# meaningless. Strip it off.99credential.realm = nil100credential.realm_key = nil101end102103yield credential104105if opts[:append_domain] && credential.realm.nil?106credential.public = "#{credential.public}@#{opts[:domain]}"107yield credential108end109end110end111end112end113end114end115116117