Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/login_scanner/ldap.rb
19514 views
1
# frozen_string_literal: true
2
3
require 'metasploit/framework/login_scanner/base'
4
require 'metasploit/framework/ldap/client'
5
6
module Metasploit
7
module Framework
8
module LoginScanner
9
class LDAP
10
include Metasploit::Framework::LoginScanner::Base
11
include Metasploit::Framework::LDAP::Client
12
include Msf::Exploit::Remote::LDAP
13
14
LIKELY_PORTS = [ 389, 636 ]
15
LIKELY_SERVICE_NAMES = [ 'ldap', 'ldaps', 'ldapssl' ]
16
PRIVATE_TYPES = [:password, :ntlm_hash]
17
18
attr_accessor :opts, :realm_key
19
# @!attribute use_client_as_proof
20
# @return [Boolean] If a login is successful and this attribute is true - an LDAP::Client instance is used as proof
21
attr_accessor :use_client_as_proof
22
23
# This method sets the sane defaults for things
24
# like timeouts and TCP evasion options
25
def set_sane_defaults
26
self.opts ||= {}
27
self.connection_timeout = 30 if self.connection_timeout.nil?
28
nil
29
end
30
31
def attempt_login(credential)
32
result_opts = {
33
credential: credential,
34
status: Metasploit::Model::Login::Status::INCORRECT,
35
proof: nil,
36
host: host,
37
port: port,
38
protocol: 'tcp',
39
service_name: 'ldap'
40
}
41
42
result_opts.merge!(do_login(credential))
43
Result.new(result_opts)
44
end
45
46
def do_login(credential)
47
opts = {
48
username: credential.public,
49
password: credential.private,
50
framework_module: framework_module,
51
ldap_auth: 'auto'
52
}.merge(@opts)
53
54
connect_opts = ldap_connect_opts(host, port, connection_timeout, ssl: opts[:ssl], opts: opts)
55
begin
56
ldap_client = ldap_open(connect_opts, keep_open: true)
57
return status_code(ldap_client)
58
rescue StandardError => e
59
{ status: Metasploit::Model::Login::Status::UNABLE_TO_CONNECT, proof: e }
60
end
61
end
62
63
def status_code(ldap_client)
64
operation_result = ldap_client.get_operation_result.table[:code]
65
case operation_result
66
when 0
67
result = { status: Metasploit::Model::Login::Status::SUCCESSFUL }
68
if use_client_as_proof
69
result[:proof] = ldap_client
70
result[:connection] = ldap_client.socket
71
end
72
result
73
else
74
{ status: Metasploit::Model::Login::Status::INCORRECT, proof: "Bind Result: #{operation_result}" }
75
end
76
end
77
78
def each_credential
79
cred_details.each do |raw_cred|
80
# This could be a Credential object, or a Credential Core, or an Attempt object
81
# so make sure that whatever it is, we end up with a Credential.
82
credential = raw_cred.to_credential
83
84
if opts[:ldap_auth] == Msf::Exploit::Remote::AuthOption::KERBEROS && opts[:ldap_krb5_cname]
85
# If we're using kerberos auth with a ccache then the password is irrelevant
86
# Remove it from the credential so we don't store it
87
credential.private = nil
88
elsif opts[:ldap_auth] == Msf::Exploit::Remote::AuthOption::SCHANNEL
89
# If we're using kerberos auth with schannel then the user/password is irrelevant
90
# Remove the password from the credential so we don't store it
91
# Note that the username is kept since it is needed for the certificate lookup.
92
credential.private = nil
93
end
94
95
if credential.realm.present? && realm_key.present?
96
credential.realm_key = realm_key
97
elsif credential.realm.present? && realm_key.blank?
98
# This service has no realm key, so the realm will be
99
# meaningless. Strip it off.
100
credential.realm = nil
101
credential.realm_key = nil
102
end
103
104
yield credential
105
106
if opts[:append_domain] && credential.realm.nil?
107
credential.public = "#{credential.public}@#{opts[:domain]}"
108
yield credential
109
end
110
end
111
end
112
end
113
end
114
end
115
end
116
117