Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/ldap/client.rb
19516 views
1
# frozen_string_literal: true
2
3
require 'rex/proto/ldap/auth_adapter'
4
5
module Metasploit
6
module Framework
7
module LDAP
8
9
module Client
10
def ldap_connect_opts(rhost, rport, connect_timeout, ssl: true, opts: {})
11
connect_opts = {
12
host: rhost,
13
port: rport,
14
connect_timeout: connect_timeout,
15
proxies: opts[:proxies]
16
}
17
18
if ssl
19
connect_opts[:encryption] = {
20
method: :simple_tls,
21
tls_options: {
22
verify_mode: OpenSSL::SSL::VERIFY_NONE
23
}
24
}
25
end
26
27
case opts[:ldap_auth]
28
when Msf::Exploit::Remote::AuthOption::SCHANNEL
29
connect_opts.merge!(ldap_auth_opts_schannel(opts, ssl))
30
when Msf::Exploit::Remote::AuthOption::KERBEROS
31
connect_opts.merge!(ldap_auth_opts_kerberos(opts, ssl))
32
when Msf::Exploit::Remote::AuthOption::NTLM
33
connect_opts.merge!(ldap_auth_opts_ntlm(opts, ssl))
34
when Msf::Exploit::Remote::AuthOption::PLAINTEXT
35
connect_opts.merge!(ldap_auth_opts_plaintext(opts))
36
when Msf::Exploit::Remote::AuthOption::AUTO
37
if opts[:username].present? && opts[:domain].present?
38
connect_opts.merge!(ldap_auth_opts_ntlm(opts, ssl))
39
elsif opts[:username].present?
40
connect_opts.merge!(ldap_auth_opts_plaintext(opts))
41
end
42
end
43
44
connect_opts
45
end
46
47
private
48
49
def ldap_auth_opts_kerberos(opts, ssl)
50
auth_opts = {}
51
raise Msf::ValidationError, 'The LDAP::Rhostname option is required when using Kerberos authentication.' if opts[:ldap_rhostname].blank?
52
raise Msf::ValidationError, 'The DOMAIN option is required when using Kerberos authentication.' if opts[:domain].blank?
53
raise Msf::ValidationError, 'The DomainControllerRhost is required when using Kerberos authentication.' if opts[:domain_controller_rhost].blank?
54
55
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(opts[:ldap_krb_offered_enc_types])
56
raise Msf::ValidationError, 'At least one encryption type is required when using Kerberos authentication.' if offered_etypes.empty?
57
58
sign_and_seal = opts.fetch(:sign_and_seal, !ssl)
59
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::LDAP.new(
60
host: opts[:domain_controller_rhost].blank? ? nil : opts[:domain_controller_rhost],
61
hostname: opts[:ldap_rhostname],
62
realm: opts[:domain],
63
username: opts[:username],
64
password: opts[:password],
65
framework: opts[:framework],
66
framework_module: opts[:framework_module],
67
cache_file: opts[:ldap_krb5_cname].blank? ? nil : opts[:ldap_krb5_cname],
68
ticket_storage: opts[:kerberos_ticket_storage],
69
offered_etypes: offered_etypes,
70
mutual_auth: true,
71
use_gss_checksum: sign_and_seal || ssl
72
)
73
74
auth_opts[:auth] = {
75
method: :rex_kerberos,
76
kerberos_authenticator: kerberos_authenticator,
77
sign_and_seal: sign_and_seal
78
}
79
80
auth_opts
81
end
82
83
def ldap_auth_opts_ntlm(opts, ssl)
84
auth_opts = {}
85
86
auth_opts[:auth] = {
87
# use the rex one provided by us to support TLS channel binding (see: ruby-ldap/ruby-net-ldap#407) and blank
88
# passwords (see: WinRb/rubyntlm#45)
89
method: :rex_ntlm,
90
username: opts[:username],
91
password: opts[:password],
92
domain: opts[:domain],
93
workstation: 'WORKSTATION',
94
sign_and_seal: opts.fetch(:sign_and_seal, !ssl)
95
}
96
97
auth_opts
98
end
99
100
def ldap_auth_opts_plaintext(opts)
101
auth_opts = {}
102
raise Msf::ValidationError, 'Can not sign and seal when using Plaintext authentication.' if opts.fetch(:sign_and_seal, false)
103
104
auth_opts[:auth] = {
105
method: :simple,
106
username: opts[:username],
107
password: opts[:password]
108
}
109
auth_opts
110
end
111
112
def ldap_auth_opts_schannel(opts, ssl)
113
auth_opts = {}
114
pfx_path = opts[:ldap_cert_file]
115
raise Msf::ValidationError, 'The SSL option must be enabled when using Schannel authentication.' unless ssl
116
raise Msf::ValidationError, 'Can not sign and seal when using Schannel authentication.' if opts.fetch(:sign_and_seal, false)
117
118
if pfx_path.present?
119
unless ::File.file?(pfx_path) && ::File.readable?(pfx_path)
120
raise Msf::ValidationError, 'Failed to load the PFX certificate file. The path was not a readable file.'
121
end
122
123
begin
124
pkcs = OpenSSL::PKCS12.new(File.binread(pfx_path), '')
125
rescue StandardError => e
126
raise Msf::ValidationError, "Failed to load the PFX file (#{e})"
127
end
128
else
129
pkcs12_storage = Msf::Exploit::Remote::Pkcs12::Storage.new(
130
framework: opts[:framework],
131
framework_module: opts[:framework_module]
132
)
133
pkcs12_results = pkcs12_storage.pkcs12(
134
username: opts[:username],
135
realm: opts[:domain],
136
tls_auth: true,
137
status: 'active'
138
)
139
if pkcs12_results.empty?
140
raise Msf::ValidationError, "Pkcs12 for #{opts[:username]}@#{opts[:domain]} not found in the database"
141
end
142
143
elog("Using stored certificate for #{opts[:username]}@#{opts[:domain]}")
144
pkcs = pkcs12_results.first.openssl_pkcs12
145
end
146
147
auth_opts[:auth] = {
148
method: :sasl,
149
mechanism: 'EXTERNAL',
150
initial_credential: '',
151
challenge_response: true
152
}
153
auth_opts[:encryption] = {
154
method: :start_tls,
155
tls_options: {
156
verify_mode: OpenSSL::SSL::VERIFY_NONE,
157
cert: pkcs.certificate,
158
key: pkcs.key
159
}
160
}
161
auth_opts
162
end
163
end
164
end
165
end
166
end
167
168