Skip to content

Commit c163cb3

Browse files
authored
Land #20490, adds the HTTP::Auth option to HTTP modules
This adds the `HTTP::Auth` Option to HTTP Modules
2 parents 3d809fa + 196e198 commit c163cb3

File tree

2 files changed

+62
-5
lines changed

2 files changed

+62
-5
lines changed

lib/msf/core/exploit/remote/http_client.rb

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ module Exploit::Remote::HttpClient
1515

1616
include Msf::Auxiliary::Report
1717
include Msf::Auxiliary::LoginScanner
18+
include Msf::Exploit::Remote::Kerberos::Ticket::Storage
19+
include Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Options
1820

1921
#
2022
# Initializes an exploit module that exploits a vulnerability in an HTTP
@@ -35,6 +37,8 @@ def initialize(info = {})
3537

3638
register_advanced_options(
3739
[
40+
*kerberos_storage_options(protocol: 'HTTP'),
41+
*kerberos_auth_options(protocol: 'HTTP', auth_methods: Msf::Exploit::Remote::AuthOption::HTTP_OPTIONS),
3842
OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests',
3943
Rex::UserAgent.session_agent
4044
]),
@@ -155,6 +159,30 @@ def connect(opts={})
155159

156160
http_logger_subscriber = Rex::Proto::Http::HttpLoggerSubscriber.new(logger: self)
157161

162+
kerberos_authenticator = nil
163+
if datastore['HTTP::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS
164+
fail_with(Msf::Exploit::Failure::BadConfig, 'The HTTP::Rhostname option is required when using Kerberos authentication.') if datastore['HTTP::Rhostname'].blank?
165+
fail_with(Msf::Exploit::Failure::BadConfig, 'The DOMAIN option is required when using Kerberos authentication.') if datastore['DOMAIN'].blank?
166+
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['HTTP::KrbOfferedEncryptionTypes'])
167+
fail_with(Msf::Exploit::Failure::BadConfig, 'At least one encryption type is required when using Kerberos authentication.') if offered_etypes.empty?
168+
169+
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::HTTP.new(
170+
host: datastore['DomainControllerRhost'],
171+
hostname: datastore['HTTP::Rhostname'],
172+
proxies: datastore['Proxies'],
173+
realm: datastore['DOMAIN'],
174+
username: datastore['HttpUsername'],
175+
password: datastore['HttpPassword'],
176+
framework: framework,
177+
framework_module: self,
178+
cache_file: datastore['HTTP::Krb5Ccname'].blank? ? nil : datastore['HTTP::Krb5Ccname'],
179+
mutual_auth: true,
180+
use_gss_checksum: true,
181+
ticket_storage: kerberos_ticket_storage,
182+
offered_etypes: offered_etypes
183+
)
184+
end
185+
158186
nclient = Rex::Proto::Http::Client.new(
159187
opts['rhost'] || rhost,
160188
(opts['rport'] || rport).to_i,
@@ -167,6 +195,7 @@ def connect(opts={})
167195
proxies,
168196
client_username,
169197
client_password,
198+
kerberos_authenticator: kerberos_authenticator,
170199
comm: opts['comm'],
171200
subscriber: http_logger_subscriber,
172201
sslkeylogfile: sslkeylogfile
@@ -375,6 +404,22 @@ def send_request_raw(opts = {}, timeout = 20, disconnect = false)
375404
actual_timeout = opts[:timeout] || timeout
376405
end
377406

407+
unless opts.key?('preferred_auth')
408+
case datastore['HTTP::Auth']
409+
when Msf::Exploit::Remote::AuthOption::AUTO
410+
opts['preferred_auth'] = nil
411+
when Msf::Exploit::Remote::AuthOption::KERBEROS
412+
opts['preferred_auth'] = 'Kerberos'
413+
when Msf::Exploit::Remote::AuthOption::NTLM
414+
opts['preferred_auth'] = 'NTLM'
415+
when Msf::Exploit::Remote::AuthOption::PLAINTEXT
416+
# Basic auth might as well be plaintext right?
417+
opts['preferred_auth'] = 'Basic'
418+
when Msf::Exploit::Remote::AuthOption::NONE
419+
opts['preferred_auth'] = 'None'
420+
end
421+
end
422+
378423
c = opts['client'] || connect(opts)
379424
r = opts['cgi'] ? c.request_cgi(opts) : c.request_raw(opts)
380425

lib/rex/proto/http/client.rb

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -314,16 +314,23 @@ def send_auth(res, opts, t, persist)
314314
res = temp_response
315315
end
316316
return res
317+
elsif supported_auths.include?('Kerberos') && (preferred_auth.nil? || preferred_auth == 'Kerberos') && kerberos_authenticator
318+
opts['provider'] = 'Kerberos'
319+
temp_response = kerberos_auth(opts, mechanism: Rex::Proto::Gss::Mechanism::KERBEROS)
320+
if temp_response.is_a? Rex::Proto::Http::Response
321+
res = temp_response
322+
end
323+
return res
317324
elsif supported_auths.include?('Negotiate') && (preferred_auth.nil? || preferred_auth == 'Negotiate')
318325
opts['provider'] = 'Negotiate'
319326
temp_response = negotiate_auth(opts)
320327
if temp_response.is_a? Rex::Proto::Http::Response
321328
res = temp_response
322329
end
323330
return res
324-
elsif supported_auths.include?('Negotiate') && (preferred_auth.nil? || preferred_auth == 'Kerberos')
331+
elsif supported_auths.include?('Negotiate') && (preferred_auth.nil? || preferred_auth == 'Kerberos') && kerberos_authenticator
325332
opts['provider'] = 'Negotiate'
326-
temp_response = kerberos_auth(opts)
333+
temp_response = kerberos_auth(opts, mechanism: Rex::Proto::Gss::Mechanism::SPNEGO)
327334
if temp_response.is_a? Rex::Proto::Http::Response
328335
res = temp_response
329336
end
@@ -411,16 +418,21 @@ def digest_auth(opts = {})
411418
end
412419
end
413420

414-
def kerberos_auth(opts = {})
421+
def kerberos_auth(opts = {}, mechanism: Rex::Proto::Gss::Mechanism::KERBEROS)
415422
to = opts['timeout'] || 20
416-
auth_result = kerberos_authenticator.authenticate(mechanism: Rex::Proto::Gss::Mechanism::KERBEROS)
423+
auth_result = kerberos_authenticator.authenticate(mechanism: mechanism)
417424
gss_data = auth_result[:security_blob]
418425
gss_data_b64 = Rex::Text.encode_base64(gss_data)
419426

420427
# Separate options for the auth requests
421428
auth_opts = opts.clone
422429
auth_opts['headers'] = opts['headers'].clone
423-
auth_opts['headers']['Authorization'] = "Kerberos #{gss_data_b64}"
430+
case mechanism
431+
when Rex::Proto::Gss::Mechanism::KERBEROS
432+
auth_opts['headers']['Authorization'] = "Kerberos #{gss_data_b64}"
433+
when Rex::Proto::Gss::Mechanism::SPNEGO
434+
auth_opts['headers']['Authorization'] = "Negotiate #{gss_data_b64}"
435+
end
424436

425437
if auth_opts['no_body_for_auth']
426438
auth_opts.delete('data')

0 commit comments

Comments
 (0)