630c2c03bc
- use the `status`, certificate's `not_before`/`not_after` and check if the TLS OID is present to filter pkcs12 before using them with PKInit - add the `activate`, `deactivate` and `export` capabilities to the certs command - add specs
167 lines
6.1 KiB
Ruby
167 lines
6.1 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'rex/proto/ldap/auth_adapter'
|
|
|
|
module Metasploit
|
|
module Framework
|
|
module LDAP
|
|
|
|
module Client
|
|
def ldap_connect_opts(rhost, rport, connect_timeout, ssl: true, opts: {})
|
|
connect_opts = {
|
|
host: rhost,
|
|
port: rport,
|
|
connect_timeout: connect_timeout,
|
|
proxies: opts[:proxies]
|
|
}
|
|
|
|
if ssl
|
|
connect_opts[:encryption] = {
|
|
method: :simple_tls,
|
|
tls_options: {
|
|
verify_mode: OpenSSL::SSL::VERIFY_NONE
|
|
}
|
|
}
|
|
end
|
|
|
|
case opts[:ldap_auth]
|
|
when Msf::Exploit::Remote::AuthOption::SCHANNEL
|
|
connect_opts.merge!(ldap_auth_opts_schannel(opts, ssl))
|
|
when Msf::Exploit::Remote::AuthOption::KERBEROS
|
|
connect_opts.merge!(ldap_auth_opts_kerberos(opts, ssl))
|
|
when Msf::Exploit::Remote::AuthOption::NTLM
|
|
connect_opts.merge!(ldap_auth_opts_ntlm(opts, ssl))
|
|
when Msf::Exploit::Remote::AuthOption::PLAINTEXT
|
|
connect_opts.merge!(ldap_auth_opts_plaintext(opts))
|
|
when Msf::Exploit::Remote::AuthOption::AUTO
|
|
if opts[:username].present? && opts[:domain].present?
|
|
connect_opts.merge!(ldap_auth_opts_ntlm(opts, ssl))
|
|
elsif opts[:username].present?
|
|
connect_opts.merge!(ldap_auth_opts_plaintext(opts))
|
|
end
|
|
end
|
|
|
|
connect_opts
|
|
end
|
|
|
|
private
|
|
|
|
def ldap_auth_opts_kerberos(opts, ssl)
|
|
auth_opts = {}
|
|
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?
|
|
|
|
sign_and_seal = opts.fetch(:sign_and_seal, !ssl)
|
|
kerberos_authenticator = Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::LDAP.new(
|
|
host: opts[:domain_controller_rhost].blank? ? nil : opts[:domain_controller_rhost],
|
|
hostname: opts[:ldap_rhostname],
|
|
realm: opts[:domain],
|
|
username: opts[:username],
|
|
password: opts[:password],
|
|
framework: opts[:framework],
|
|
framework_module: opts[:framework_module],
|
|
cache_file: opts[:ldap_krb5_cname].blank? ? nil : opts[:ldap_krb5_cname],
|
|
ticket_storage: opts[:kerberos_ticket_storage],
|
|
offered_etypes: offered_etypes,
|
|
mutual_auth: true,
|
|
use_gss_checksum: sign_and_seal || ssl
|
|
)
|
|
|
|
auth_opts[:auth] = {
|
|
method: :rex_kerberos,
|
|
kerberos_authenticator: kerberos_authenticator,
|
|
sign_and_seal: sign_and_seal
|
|
}
|
|
|
|
auth_opts
|
|
end
|
|
|
|
def ldap_auth_opts_ntlm(opts, ssl)
|
|
auth_opts = {}
|
|
|
|
auth_opts[:auth] = {
|
|
# use the rex one provided by us to support TLS channel binding (see: ruby-ldap/ruby-net-ldap#407) and blank
|
|
# passwords (see: WinRb/rubyntlm#45)
|
|
method: :rex_ntlm,
|
|
username: opts[:username],
|
|
password: opts[:password],
|
|
domain: opts[:domain],
|
|
workstation: 'WORKSTATION',
|
|
sign_and_seal: opts.fetch(:sign_and_seal, !ssl)
|
|
}
|
|
|
|
auth_opts
|
|
end
|
|
|
|
def ldap_auth_opts_plaintext(opts)
|
|
auth_opts = {}
|
|
raise Msf::ValidationError, 'Can not sign and seal when using Plaintext authentication.' if opts.fetch(:sign_and_seal, false)
|
|
|
|
auth_opts[:auth] = {
|
|
method: :simple,
|
|
username: opts[:username],
|
|
password: opts[:password]
|
|
}
|
|
auth_opts
|
|
end
|
|
|
|
def ldap_auth_opts_schannel(opts, ssl)
|
|
auth_opts = {}
|
|
pfx_path = opts[:ldap_cert_file]
|
|
raise Msf::ValidationError, 'The SSL option must be enabled when using Schannel authentication.' unless ssl
|
|
raise Msf::ValidationError, 'Can not sign and seal when using Schannel authentication.' if opts.fetch(:sign_and_seal, false)
|
|
|
|
if pfx_path.present?
|
|
unless ::File.file?(pfx_path) && ::File.readable?(pfx_path)
|
|
raise Msf::ValidationError, 'Failed to load the PFX certificate file. The path was not a readable file.'
|
|
end
|
|
|
|
begin
|
|
pkcs = OpenSSL::PKCS12.new(File.binread(pfx_path), '')
|
|
rescue StandardError => e
|
|
raise Msf::ValidationError, "Failed to load the PFX file (#{e})"
|
|
end
|
|
else
|
|
pkcs12_storage = Msf::Exploit::Remote::Pkcs12::Storage.new(
|
|
framework: opts[:framework],
|
|
framework_module: opts[:framework_module]
|
|
)
|
|
pkcs12_results = pkcs12_storage.pkcs12(
|
|
username: opts[:username],
|
|
realm: opts[:domain],
|
|
tls_auth: true,
|
|
status: 'active'
|
|
)
|
|
if pkcs12_results.empty?
|
|
raise Msf::ValidationError, "Pkcs12 for #{opts[:username]}@#{opts[:domain]} not found in the database"
|
|
end
|
|
|
|
elog("Using stored certificate for #{opts[:username]}@#{opts[:domain]}")
|
|
pkcs = pkcs12_results.first.openssl_pkcs12
|
|
end
|
|
|
|
auth_opts[:auth] = {
|
|
method: :sasl,
|
|
mechanism: 'EXTERNAL',
|
|
initial_credential: '',
|
|
challenge_response: true
|
|
}
|
|
auth_opts[:encryption] = {
|
|
method: :start_tls,
|
|
tls_options: {
|
|
verify_mode: OpenSSL::SSL::VERIFY_NONE,
|
|
cert: pkcs.certificate,
|
|
key: pkcs.key
|
|
}
|
|
}
|
|
auth_opts
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|