From b08a5f6ce3b6ad360f15ca1bb9b758c6a5b06f5b Mon Sep 17 00:00:00 2001 From: Mathieu Date: Wed, 12 Feb 2025 17:47:18 +0100 Subject: [PATCH] Change how ldap_login generate its specific credentials for SCHANNEL && KERBEROS auth --- .../framework/credential_collection.rb | 30 +----------- modules/auxiliary/scanner/ldap/ldap_login.rb | 37 +++++++++----- .../framework/credential_collection_spec.rb | 49 +------------------ 3 files changed, 27 insertions(+), 89 deletions(-) diff --git a/lib/metasploit/framework/credential_collection.rb b/lib/metasploit/framework/credential_collection.rb index 628b77569e15..de9135b93234 100644 --- a/lib/metasploit/framework/credential_collection.rb +++ b/lib/metasploit/framework/credential_collection.rb @@ -212,23 +212,6 @@ class CredentialCollection < PrivateCredentialCollection # @return [Boolean] attr_accessor :anonymous_login - # @!attribute ignore_private - # Whether to ignore private (password). This is usually set when Kerberos - # or Schannel authentication is requested and the credentials are - # retrieved from cache or from a file. This attribute should be true in - # these scenarios, otherwise validation will fail since the password is not - # provided. - # @return [Boolean] - attr_accessor :ignore_private - - # @!attribute ignore_public - # Whether to ignore public (username). This is usually set when Schannel - # authentication is requested and the credentials are retrieved from a - # file (certificate). This attribute should be true in this case, - # otherwise validation will fail since the password is not provided. - # @return [Boolean] - attr_accessor :ignore_public - # @option opts [Boolean] :blank_passwords See {#blank_passwords} # @option opts [String] :pass_file See {#pass_file} # @option opts [String] :password See {#password} @@ -267,15 +250,6 @@ def each_filtered alias each each_filtered def each_unfiltered(&block) - if ignore_private - if ignore_public - yield Metasploit::Framework::Credential.new(public: nil, private: nil, realm: realm) - else - yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm) - end - return - end - prepended_creds.each { |c| yield c } if anonymous_login @@ -468,14 +442,14 @@ def empty? # # @return [Boolean] def has_users? - username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? || !!ignore_public + username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? end # Returns true when there are any private values set # # @return [Boolean] def has_privates? - super || userpass_file.present? || user_as_pass || !!ignore_private + super || userpass_file.present? || user_as_pass end end diff --git a/modules/auxiliary/scanner/ldap/ldap_login.rb b/modules/auxiliary/scanner/ldap/ldap_login.rb index 47c6dac14db9..76a17e1a4372 100644 --- a/modules/auxiliary/scanner/ldap/ldap_login.rb +++ b/modules/auxiliary/scanner/ldap/ldap_login.rb @@ -89,19 +89,9 @@ def validate_connect_options! end def run_host(ip) - ignore_public = datastore['LDAP::Auth'] == Msf::Exploit::Remote::AuthOption::SCHANNEL - ignore_private = - datastore['LDAP::Auth'] == Msf::Exploit::Remote::AuthOption::SCHANNEL || - (Msf::Exploit::Remote::AuthOption::KERBEROS && !datastore['ANONYMOUS_LOGIN'] && !datastore['PASSWORD']) - - cred_collection = build_credential_collection( - username: datastore['USERNAME'], - password: datastore['PASSWORD'], - realm: datastore['DOMAIN'], - anonymous_login: datastore['ANONYMOUS_LOGIN'], - blank_passwords: false, - ignore_public: ignore_public, - ignore_private: ignore_private + cred_collection = build_specific_credential_collection( + void_login: datastore['LDAP::Auth'] == Msf::Exploit::Remote::AuthOption::SCHANNEL, + no_password_login: datastore['LDAP::Auth'] == Msf::Exploit::Remote::AuthOption::KERBEROS && !datastore['ANONYMOUS_LOGIN'] && !datastore['PASSWORD'] ) opts = { @@ -202,4 +192,25 @@ def session_setup(result) start_session(self, nil, merge_me, false, my_session.rstream, my_session) end + + def build_specific_credential_collection(void_login:, no_password_login:) + if void_login + Metasploit::Framework::PrivateCredentialCollection.new({ + nil_password: true + }) + elsif no_password_login + Metasploit::Framework::CredentialCollection.new({ + username: datastore['USERNAME'], + nil_password: true + }) + else + build_credential_collection( + username: datastore['USERNAME'], + password: datastore['PASSWORD'], + realm: datastore['DOMAIN'], + anonymous_login: datastore['ANONYMOUS_LOGIN'], + blank_passwords: false + ) + end + end end diff --git a/spec/lib/metasploit/framework/credential_collection_spec.rb b/spec/lib/metasploit/framework/credential_collection_spec.rb index b6ef2d637631..a4d3cc365012 100644 --- a/spec/lib/metasploit/framework/credential_collection_spec.rb +++ b/spec/lib/metasploit/framework/credential_collection_spec.rb @@ -16,9 +16,7 @@ prepended_creds: prepended_creds, additional_privates: additional_privates, additional_publics: additional_publics, - password_spray: password_spray, - ignore_public: ignore_public, - ignore_private: ignore_private + password_spray: password_spray ) end @@ -41,8 +39,6 @@ let(:additional_privates) { [] } let(:additional_publics) { [] } let(:password_spray) { false } - let(:ignore_public) { nil } - let(:ignore_private) { nil } describe "#each" do specify do @@ -596,34 +592,6 @@ ) end end - - context 'when :ignore_public is true and :username is nil' do - let(:ignore_public) { true } - let(:username) { nil } - specify do - expect { |b| collection.each(&b) }.to_not yield_control - end - end - - context 'when :ignore_private is true and password is nil' do - let(:ignore_private) { true } - let(:password) { nil } - specify do - expect { |b| collection.each(&b) }.to yield_successive_args( - Metasploit::Framework::Credential.new(public: username, private: nil) - ) - end - - context 'when :ignore_public is also true and username is nil' do - let(:ignore_public) { true } - let(:username) { nil } - specify do - expect { |b| collection.each(&b) }.to yield_successive_args( - Metasploit::Framework::Credential.new(public: nil, private: nil) - ) - end - end - end end describe "#empty?" do @@ -693,21 +661,6 @@ expect(collection.empty?).to eq true end end - - context "and :ignore_public is set" do - let(:ignore_public) { true } - specify do - expect(collection.empty?).to eq true - end - - context "and :ignore_private is also set" do - let(:ignore_private) { true } - specify do - expect(collection.empty?).to eq false - end - end - end - end end end