Land #18446, Make DomainControllerRhost optional
This PR makes the DomainControllerRhosts option optional, even when auth is set to kerberos. This change requires rapid7/rex-socket#64 which was released in the rex-socket 1.5.5 gem.
This commit is contained in:
commit
9f126a4d24
|
@ -418,7 +418,7 @@ GEM
|
|||
metasm
|
||||
rex-core
|
||||
rex-text
|
||||
rex-socket (0.1.54)
|
||||
rex-socket (0.1.55)
|
||||
rex-core
|
||||
rex-sslscan (0.1.10)
|
||||
rex-core
|
||||
|
|
|
@ -130,11 +130,13 @@ Required options:
|
|||
* `${Prefix}::Rhostname` -- The hostname of the target system. This value should be either the hostname `WIN-MIJZ318SQH` or
|
||||
the FQDN like `WIN-MIJZ318SQH.msflab.local`. i.e. `Smb::Rhostname=WIN-MIJZ318SQH.msflab.local`
|
||||
* `${Prefix}Domain` -- The domain name of the target system, e.g. `msflab.local`. i.e. `SmbDomain=msflab.local`
|
||||
* `DomainControllerRhost` -- The IP address of the domain controller to use for kerberos authentication. i.e. `DomainControllerRhost=192.168.123.13`
|
||||
|
||||
Optional options:
|
||||
* `DomainControllerRhost` -- The IP address or hostname of the domain controller to use for Kerberos authentication.
|
||||
i.e. `DomainControllerRhost=192.168.123.13`. If this value is not specified, Metasploit will look it up via the
|
||||
realm's (the `${Prefix}Domain` option) SRV record in DNS.
|
||||
* `${Prefix}::Krb5Ccname` -- The path to a CCACHE file to use for authentication. This is comparable to setting the
|
||||
`KRB5CCNAME` environment variable for other tools. If specified, the tickets it contains will be used. i.e. `KRB5CCNAME=/path/to/Administrator.ccache`
|
||||
`KRB5CCNAME` environment variable for other tools. If specified, the tickets it contains will be used. i.e. `KRB5CCNAME=/path/to/Administrator.ccache`.
|
||||
* `KrbCacheMode` -- The cache storage mode to use, one of the following four options:
|
||||
* `none` -- No cache storage is used, new tickets are requested and no tickets are stored.
|
||||
* `read-only` -- Stored tickets from the cache will be used, but no new tickets are stored.
|
||||
|
|
|
@ -55,13 +55,12 @@ module Metasploit
|
|||
when Msf::Exploit::Remote::AuthOption::KERBEROS
|
||||
raise Msf::ValidationError, 'The Ldap::Rhostname option is required when using Kerberos authentication.' if opts[:ldap_rhostname].blank?
|
||||
raise Msf::ValidationError, 'The DOMAIN option is required when using Kerberos authentication.' if opts[:domain].blank?
|
||||
raise Msf::ValidationError, 'The DomainControllerRhost is required when using Kerberos authentication.' if opts[:domain_controller_rhost].blank?
|
||||
|
||||
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(opts[:ldap_krb_offered_enc_types])
|
||||
raise Msf::ValidationError, 'At least one encryption type is required when using Kerberos authentication.' if offered_etypes.empty?
|
||||
|
||||
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::LDAP.new(
|
||||
host: opts[:domain_controller_rhost],
|
||||
host: opts[:domain_controller_rhost].blank? ? nil : opts[:domain_controller_rhost],
|
||||
hostname: opts[:ldap_rhostname],
|
||||
realm: opts[:domain],
|
||||
username: opts[:username],
|
||||
|
|
|
@ -90,6 +90,7 @@ class Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base
|
|||
:print_status,
|
||||
:print_good,
|
||||
:vprint_error,
|
||||
:vprint_status,
|
||||
:workspace
|
||||
|
||||
# Flags - https://datatracker.ietf.org/doc/html/rfc4121#section-4.1.1.1
|
||||
|
@ -190,6 +191,23 @@ class Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base
|
|||
port
|
||||
end
|
||||
|
||||
def connect(options = {})
|
||||
unless options[:rhost]
|
||||
unless (host = @host)
|
||||
vprint_status("Using DNS to lookup the KDC for #{realm}...")
|
||||
host = ::Rex::Socket.getresources("_kerberos._tcp.#{realm}", :SRV)&.sample
|
||||
if host.nil?
|
||||
raise ::Rex::Proto::Kerberos::Model::Error::KerberosError.new("Failed to lookup the KDC")
|
||||
end
|
||||
print_status("Using KDC #{host} for realm #{realm}")
|
||||
@host = host
|
||||
end
|
||||
options[:rhost] = host
|
||||
end
|
||||
|
||||
super(options)
|
||||
end
|
||||
|
||||
# @param [Hash] options
|
||||
# @option options [String] :credential An explicit credential object to use for authentication.
|
||||
# @option options [Rex::Proto::Kerberos::Model::PrincipalName] :sname The target service principal name.
|
||||
|
@ -225,7 +243,7 @@ class Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base
|
|||
)
|
||||
end
|
||||
if options[:credential]
|
||||
print_status("#{peer} - Using cached credential for #{options[:credential].server} #{options[:credential].client}")
|
||||
print_status("Using cached credential for #{options[:credential].server} #{options[:credential].client}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -345,7 +363,7 @@ class Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base
|
|||
end
|
||||
|
||||
if credential
|
||||
print_status("#{peer} - Using cached credential for #{credential.server} #{credential.client}")
|
||||
print_status("Using cached credential for #{credential.server} #{credential.client}")
|
||||
return credential
|
||||
end
|
||||
|
||||
|
@ -363,7 +381,7 @@ class Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base
|
|||
def request_tgs_only(credential, options = {})
|
||||
# load a cached TGS
|
||||
if (ccache = get_cached_credential(options))
|
||||
print_status("#{peer} - Using cached credential for #{ccache.server} #{ccache.client}")
|
||||
print_status("Using cached credential for #{ccache.server} #{ccache.client}")
|
||||
return ccache
|
||||
end
|
||||
|
||||
|
|
|
@ -347,12 +347,11 @@ module Exploit::Remote::MSSQL
|
|||
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The Mssql::Rhostname option is required when using Kerberos authentication.') if datastore['Mssql::Rhostname'].blank?
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The DOMAIN option is required when using Kerberos authentication.') if datastore['DOMAIN'].blank?
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The DomainControllerRhost is required when using Kerberos authentication.') if datastore['DomainControllerRhost'].blank?
|
||||
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['MssqlKrbOfferedEncryptionTypes'])
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'At least one encryption type is required when using Kerberos authentication.') if offered_etypes.empty?
|
||||
|
||||
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::MSSQL.new(
|
||||
host: datastore['DomainControllerRhost'],
|
||||
host: datastore['DomainControllerRhost'].blank? ? nil : datastore['DomainControllerRhost'],
|
||||
hostname: datastore['Mssql::Rhostname'],
|
||||
proxies: datastore['Proxies'],
|
||||
mssql_port: rport,
|
||||
|
|
|
@ -153,12 +153,11 @@ module Msf
|
|||
if datastore['SMB::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The Smb::Rhostname option is required when using Kerberos authentication.') if datastore['Smb::Rhostname'].blank?
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The SMBDomain option is required when using Kerberos authentication.') if datastore['SMBDomain'].blank?
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The DomainControllerRhost is required when using Kerberos authentication.') if datastore['DomainControllerRhost'].blank?
|
||||
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Smb::KrbOfferedEncryptionTypes'])
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'At least one encryption type is required when using Kerberos authentication.') if offered_etypes.empty?
|
||||
|
||||
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::SMB.new(
|
||||
host: datastore['DomainControllerRhost'],
|
||||
host: datastore['DomainControllerRhost'].blank? ? nil : datastore['DomainControllerRhost'],
|
||||
hostname: datastore['Smb::Rhostname'],
|
||||
proxies: datastore['Proxies'],
|
||||
realm: datastore['SMBDomain'],
|
||||
|
|
|
@ -45,7 +45,6 @@ module Exploit::Remote::WinRM
|
|||
if datastore['Winrm::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The Winrm::Rhostname option is required when using Kerberos authentication.') if datastore['Winrm::Rhostname'].blank?
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The DOMAIN option is required when using Kerberos authentication.') if datastore['DOMAIN'].blank?
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'The DomainControllerRhost is required when using Kerberos authentication.') if datastore['DomainControllerRhost'].blank?
|
||||
offered_etypes = Msf::Exploit::Remote::AuthOption.as_default_offered_etypes(datastore['Winrm::KrbOfferedEncryptionTypes'])
|
||||
fail_with(Msf::Exploit::Failure::BadConfig, 'At least one encryption type is required when using Kerberos authentication.') if offered_etypes.empty?
|
||||
else
|
||||
|
@ -80,7 +79,7 @@ module Exploit::Remote::WinRM
|
|||
case datastore['Winrm::Auth']
|
||||
when Msf::Exploit::Remote::AuthOption::KERBEROS
|
||||
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::HTTP.new(
|
||||
host: datastore['DomainControllerRhost'],
|
||||
host: datastore['DomainControllerRhost'].blank? ? nil : datastore['DomainControllerRhost'],
|
||||
hostname: datastore['Winrm::Rhostname'],
|
||||
proxies: datastore['Proxies'],
|
||||
realm: datastore['DOMAIN'],
|
||||
|
|
|
@ -2,18 +2,45 @@
|
|||
require 'spec_helper'
|
||||
|
||||
RSpec.describe Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base do
|
||||
subject do
|
||||
described_class.new(
|
||||
let(:params) {
|
||||
{
|
||||
realm: 'demo.local',
|
||||
hostname: 'mock_hostname',
|
||||
username: 'mock_username',
|
||||
password: 'mock_password',
|
||||
host: 'mock_host',
|
||||
host: '127.0.0.1',
|
||||
port: 88,
|
||||
timeout: 25,
|
||||
framework: instance_double(::Msf::Framework),
|
||||
framework_module: instance_double(::Msf::Module)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
subject do
|
||||
described_class.new(**params)
|
||||
end
|
||||
|
||||
describe '#connect' do
|
||||
before(:each) do
|
||||
allow(params[:framework_module]).to receive(:framework)
|
||||
allow(params[:framework_module]).to receive(:print_status)
|
||||
allow(params[:framework_module]).to receive(:vprint_status)
|
||||
end
|
||||
|
||||
context 'when host is nil' do
|
||||
it 'resolves it to a hostname' do
|
||||
expect(::Rex::Socket).to receive(:getresources).with("_kerberos._tcp.#{params[:realm]}", :SRV).and_return(['mock_host'])
|
||||
instance = described_class.new(**params.merge(host: nil))
|
||||
instance.connect
|
||||
expect(instance.host).to eq('mock_host')
|
||||
end
|
||||
|
||||
it 'raises a KerberosError on failure' do
|
||||
expect(::Rex::Socket).to receive(:getresources).with("_kerberos._tcp.#{params[:realm]}", :SRV).and_return([])
|
||||
instance = described_class.new(**params.merge(host: nil))
|
||||
expect { instance.connect }.to raise_error(::Rex::Proto::Kerberos::Model::Error::KerberosError)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#validate_response!" do
|
||||
|
|
Loading…
Reference in New Issue