CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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