diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 468eb2e2afd9..67b3b43319f2 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -337,7 +337,7 @@ def creds_search(*args) set_rhosts = false truncate = true - cred_table_columns = [ 'host', 'origin' , 'service', 'public', 'private', 'realm', 'private_type', 'JtR Format' ] + cred_table_columns = [ 'host', 'origin' , 'service', 'public', 'private', 'realm', 'private_type', 'JtR Format', 'cracked_password' ] delete_count = 0 search_term = nil @@ -446,6 +446,7 @@ def creds_search(*args) opts[:workspace] = framework.db.workspace query = framework.db.creds(opts) matched_cred_ids = [] + cracked_cred_ids = [] if output_file&.ends_with?('.hcat') output_file = ::File.open(output_file, 'wb') @@ -458,8 +459,9 @@ def creds_search(*args) tbl = Rex::Text::Table.new(tbl_opts) end - filtered_query(query, opts, origin_ranges, host_ranges) do |core, service, origin| + filtered_query(query, opts, origin_ranges, host_ranges) do |core, service, origin, cracked_password_core| matched_cred_ids << core.id + cracked_cred_ids << cracked_password_core.id if cracked_password_core.present? if output_file && output_formatter formatted = output_formatter.call(core) @@ -493,7 +495,7 @@ def creds_search(*args) rhosts << host unless host.blank? service_info = build_service_info(service) end - + cracked_password_val = cracked_password_core&.private&.data.to_s tbl << [ host, origin, @@ -502,7 +504,8 @@ def creds_search(*args) private_val, realm_val, human_val, #private type - jtr_val + jtr_val, + cracked_password_val ] end end @@ -516,7 +519,7 @@ def creds_search(*args) end if mode == :delete - result = framework.db.delete_credentials(ids: matched_cred_ids.uniq) + result = framework.db.delete_credentials(ids: matched_cred_ids.concat(cracked_cred_ids).uniq) delete_count = result.size end @@ -549,6 +552,12 @@ def cmd_creds_tabs(str, words) def filtered_query(query, opts, origin_ranges, host_ranges) query.each do |core| + # we will not show the cracked password in a seperate row instead we will show in seperate column + # calling `find_by` against this result is a rails direct interaction that may fail in json database mode + if core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) && query.find_by(id: core.origin.metasploit_credential_core_id).present? + next + end + # Exclude non-blank username creds if that's what we're after if opts[:user] == '' && core.public && !(core.public.username.blank?) next @@ -572,11 +581,23 @@ def filtered_query(query, opts, origin_ranges, host_ranges) next end + # this is a direct rails interaction that cannot cross the json db service layer, access of origin objects may be problematic + cracked_password_core = nil + + if !core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) && core.public.cores.count > 1 + core.public.cores.each do |potential_cracked_core| + next unless potential_cracked_core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) + if potential_cracked_core.origin.originating_core == core + cracked_password_core = potential_cracked_core + end + end + end + if core.logins.empty? service = service_from_origin(core) next if service.nil? && host_ranges.present? # If we're filtering by login IP and we're here there's no associated login, so skip - yield core, service, origin + yield core, service, origin, cracked_password_core else core.logins.each do |login| service = framework.db.services(id: login.service_id).first @@ -588,7 +609,7 @@ def filtered_query(query, opts, origin_ranges, host_ranges) next end - yield core, service, origin + yield core, service, origin, cracked_password_core end end end diff --git a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb index 24e0a6feec8d..0b584f016bf4 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -71,9 +71,9 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------', - ' thisuser thispass Password ' + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------', + ' thisuser thispass Password ' ]) end @@ -83,8 +83,8 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------', + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------', ' thisuser thispass Password ' ]) end @@ -96,9 +96,9 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------', - ' nonblank_pass Password ' + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------', + ' nonblank_pass Password ' ]) end end @@ -109,9 +109,9 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------', - ' nonblank_user Password ' + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------', + ' nonblank_user Password ' ]) end end @@ -125,8 +125,8 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------' + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------' ]) end end @@ -137,8 +137,45 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------' + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------' + ]) + end + end + context 'showing new column of cracked_password for all the cracked passwords' do + it 'should show the cracked password in the new column named cracked_passwords' do + common_public = FactoryBot.create(:metasploit_credential_username, username: "this_username") + core = FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), + private: FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: "some_hash"), + public: common_public, + realm: nil, + workspace: framework.db.workspace) + cracked_core = FactoryBot.create(:metasploit_credential_core, + origin: Metasploit::Credential::Origin::CrackedPassword.create!(metasploit_credential_core_id: core.id), + private: FactoryBot.create(:metasploit_credential_password, data: "this_cracked_password"), + public: common_public, + realm: nil, + workspace: framework.db.workspace) + creds.cmd_creds('-u', 'this_username') + expect(@output).to eq([ + "Credentials", + "===========", + "", + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " this_username some_hash Nonreplayable hash this_cracked_password" + ]) + end + it "should show the user given passwords on private column instead of cracked_password column" do + creds.cmd_creds('-u', 'thisuser') + expect(@output).to eq([ + "Credentials", + "===========", + "", + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " thisuser thispass Password " ]) end end @@ -206,11 +243,36 @@ 'Credentials', '===========', '', - 'host origin service public private realm private_type JtR Format', - '---- ------ ------- ------ ------- ----- ------------ ----------', - ' thisuser thispass Password ' + 'host origin service public private realm private_type JtR Format cracked_password', + '---- ------ ------- ------ ------- ----- ------------ ---------- ----------------', + ' thisuser thispass Password ' ]) end + it 'should show all the cores whose private is either password or the private is cracked password' do + common_public = FactoryBot.create(:metasploit_credential_username, username: "this_username") + core = FactoryBot.create(:metasploit_credential_core, + origin: FactoryBot.create(:metasploit_credential_origin_import), + private: FactoryBot.create(:metasploit_credential_nonreplayable_hash, data: "some_hash"), + public: common_public, + realm: nil, + workspace: framework.db.workspace) + cracked_core = FactoryBot.create(:metasploit_credential_core, + origin: Metasploit::Credential::Origin::CrackedPassword.create!(metasploit_credential_core_id: core.id), + private: FactoryBot.create(:metasploit_credential_password, data: "this_cracked_password"), + public: common_public, + realm: nil, + workspace: framework.db.workspace) + creds.cmd_creds('-t', 'password') + expect(@output).to eq([ + "Credentials", + "===========", + "", + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " thisuser thispass Password ", + " this_username this_cracked_password Password " + ]) + end end context 'ntlm' do @@ -223,8 +285,8 @@ 'Credentials', '===========', '', - 'host service public private realm private_type JtR Format', - '---- ------- ------ ------- ----- ------------ ----------', + 'host service public private realm private_type JtR Format cracked_password', + '---- ------- ------ ------- ----- ------------ ---------- ----------------', " thisuser #{ntlm_hash} NTLM hash" ] end