From cae7f8c350577a75257ca36a4af68aea1366aadc Mon Sep 17 00:00:00 2001 From: manishkumarr1017 Date: Thu, 23 Feb 2023 15:01:51 +0530 Subject: [PATCH 1/7] adding a new column cracked password in creds command to show cracked passwords --- .../ui/console/command_dispatcher/creds.rb | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 05be68d0c74c..1e897238b9a0 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -323,7 +323,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 @@ -432,6 +432,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') @@ -471,6 +472,11 @@ def creds_search(*args) jtr_val = core.private.jtr_format ? core.private.jtr_format : '' end + cracked_hash_origin = Metasploit::Credential::Origin::CrackedPassword.find_by(metasploit_credential_core_id: core.id) + cracked_password_core = query.find_by(origin_id: cracked_hash_origin.id, origin_type: "Metasploit::Credential::Origin::CrackedPassword") if cracked_hash_origin.present? + cracked_cred_ids << cracked_password_core.id if cracked_password_core.present? + cracked_password_val = cracked_password_core.present? ? cracked_password_core.private.data : '' + if service.nil? host = '' service_info = '' @@ -488,7 +494,8 @@ def creds_search(*args) private_val, realm_val, human_val, #private type - jtr_val + jtr_val, + cracked_password_val ] end end @@ -502,8 +509,8 @@ def creds_search(*args) end if mode == :delete - result = framework.db.delete_credentials(ids: matched_cred_ids.uniq) - delete_count = result.size + delete_count = matched_cred_ids.size + result = framework.db.delete_credentials(ids: matched_cred_ids.concat(cracked_cred_ids).uniq) end # Finally, handle the case where the user wants the resulting list @@ -535,6 +542,11 @@ 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 + if core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) + next + end + # Exclude non-blank username creds if that's what we're after if opts[:user] == '' && core.public && !(core.public.username.blank?) next From 60113f74b71a4e3e3e43906337a5dd086f49aa42 Mon Sep 17 00:00:00 2001 From: manishkumarr1017 Date: Wed, 1 Mar 2023 23:27:18 +0530 Subject: [PATCH 2/7] fixing spec files for creds command new enhancements --- .../console/command_dispatcher/creds_spec.rb | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) 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..7e70299ba0de 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,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 @@ -206,9 +206,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 end @@ -223,8 +223,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 From 28a2bcf9d79c5e406dcf5aa5e5bbd8e3e188febf Mon Sep 17 00:00:00 2001 From: Grant Willcox Date: Mon, 6 Mar 2023 12:30:06 -0600 Subject: [PATCH 3/7] Fix calculation of delete_count size --- lib/msf/ui/console/command_dispatcher/creds.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 1e897238b9a0..a888838517c1 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -509,8 +509,8 @@ def creds_search(*args) end if mode == :delete - delete_count = matched_cred_ids.size result = framework.db.delete_credentials(ids: matched_cred_ids.concat(cracked_cred_ids).uniq) + delete_count = result.size end # Finally, handle the case where the user wants the resulting list From 02608a4e120ccda5404babf6fc2446f755064117 Mon Sep 17 00:00:00 2001 From: manishkumarr1017 Date: Sat, 18 Mar 2023 23:04:55 +0530 Subject: [PATCH 4/7] adding extra specs for the new enhancement and optimizing the queries --- .../ui/console/command_dispatcher/creds.rb | 20 +++--- .../console/command_dispatcher/creds_spec.rb | 67 +++++++++++++++++++ 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index a888838517c1..eb007c441d81 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -445,8 +445,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) @@ -472,11 +473,6 @@ def creds_search(*args) jtr_val = core.private.jtr_format ? core.private.jtr_format : '' end - cracked_hash_origin = Metasploit::Credential::Origin::CrackedPassword.find_by(metasploit_credential_core_id: core.id) - cracked_password_core = query.find_by(origin_id: cracked_hash_origin.id, origin_type: "Metasploit::Credential::Origin::CrackedPassword") if cracked_hash_origin.present? - cracked_cred_ids << cracked_password_core.id if cracked_password_core.present? - cracked_password_val = cracked_password_core.present? ? cracked_password_core.private.data : '' - if service.nil? host = '' service_info = '' @@ -495,7 +491,7 @@ def creds_search(*args) realm_val, human_val, #private type jtr_val, - cracked_password_val + cracked_password_core&.private&.data ] end end @@ -543,8 +539,10 @@ 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 - if core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) + if core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) && query.find_by(id: core.origin.metasploit_credential_core_id).present? next + elsif core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) + core = framework.db.creds.find_by(id: core.origin.metasploit_credential_core_id) end # Exclude non-blank username creds if that's what we're after @@ -570,11 +568,13 @@ def filtered_query(query, opts, origin_ranges, host_ranges) next end + cracked_password_core = core.public.cores.where(origin_type: "Metasploit::Credential::Origin::CrackedPassword").joins("LEFT JOIN metasploit_credential_origin_cracked_passwords ON metasploit_credential_origin_cracked_passwords.id = metasploit_credential_cores.origin_id").find_by("metasploit_credential_origin_cracked_passwords.metasploit_credential_core_id = (?)", core.id) + 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 @@ -586,7 +586,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 7e70299ba0de..484354f08fee 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -142,6 +142,48 @@ ]) 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() + expect(@output).to eq([ + "Credentials", + "===========", + "", + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " thisuser thispass Password ", + " nonblank_pass Password ", + " nonblank_user 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() + expect(@output).to eq([ + "Credentials", + "===========", + "", + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " thisuser thispass Password ", + " nonblank_pass Password ", + " nonblank_user Password " + ]) + end + end end end @@ -211,6 +253,31 @@ ' 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 some_hash Nonreplayable hash this_cracked_password" + ]) + end end context 'ntlm' do From 4aea945be369cf9ad295f82e991bbbef6542ff41 Mon Sep 17 00:00:00 2001 From: manishkumarr1017 Date: Sat, 18 Mar 2023 23:24:00 +0530 Subject: [PATCH 5/7] fixing the failed specs by keeping the exact output necessary --- .../console/command_dispatcher/creds_spec.rb | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) 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 484354f08fee..0d710185d8fc 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -157,30 +157,25 @@ public: common_public, realm: nil, workspace: framework.db.workspace) - creds.cmd_creds() + creds.cmd_creds('-u', 'this_username') expect(@output).to eq([ "Credentials", "===========", "", - "host origin service public private realm private_type JtR Format cracked_password", - "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", - " thisuser thispass Password ", - " nonblank_pass Password ", - " nonblank_user Password ", - " this_username some_hash Nonreplayable hash this_cracked_password" + "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() + 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 ", - " nonblank_pass Password ", - " nonblank_user Password " + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " thisuser thispass Password " ]) end end From bd9591f621ccfbe29e9f2037c986ee4a8ed2f221 Mon Sep 17 00:00:00 2001 From: manishkumarr1017 Date: Fri, 19 May 2023 19:11:06 +0530 Subject: [PATCH 6/7] changing nil datatype to string datatype for the column display --- lib/msf/ui/console/command_dispatcher/creds.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 94b3c4e4c3ea..30942c057867 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -495,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, @@ -505,7 +505,7 @@ def creds_search(*args) realm_val, human_val, #private type jtr_val, - cracked_password_core&.private&.data + cracked_password_val ] end end From 87582ee5c93f551eaa4d894ed78a9370842eb0ba Mon Sep 17 00:00:00 2001 From: manishkumarr1017 Date: Fri, 23 Jun 2023 13:12:12 +0530 Subject: [PATCH 7/7] PR Review changes --- lib/msf/ui/console/command_dispatcher/creds.rb | 15 ++++++++++++--- .../ui/console/command_dispatcher/creds_spec.rb | 8 ++++---- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/msf/ui/console/command_dispatcher/creds.rb b/lib/msf/ui/console/command_dispatcher/creds.rb index 30942c057867..67b3b43319f2 100644 --- a/lib/msf/ui/console/command_dispatcher/creds.rb +++ b/lib/msf/ui/console/command_dispatcher/creds.rb @@ -553,10 +553,9 @@ 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 - elsif core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) - core = framework.db.creds.find_by(id: core.origin.metasploit_credential_core_id) end # Exclude non-blank username creds if that's what we're after @@ -582,7 +581,17 @@ def filtered_query(query, opts, origin_ranges, host_ranges) next end - cracked_password_core = core.public.cores.where(origin_type: "Metasploit::Credential::Origin::CrackedPassword").joins("LEFT JOIN metasploit_credential_origin_cracked_passwords ON metasploit_credential_origin_cracked_passwords.id = metasploit_credential_cores.origin_id").find_by("metasploit_credential_origin_cracked_passwords.metasploit_credential_core_id = (?)", core.id) + # 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) 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 0d710185d8fc..0b584f016bf4 100644 --- a/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb +++ b/spec/lib/msf/ui/console/command_dispatcher/creds_spec.rb @@ -267,10 +267,10 @@ "Credentials", "===========", "", - "host origin service public private realm private_type JtR Format cracked_password", - "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", - " thisuser thispass Password ", - " this_username some_hash Nonreplayable hash this_cracked_password" + "host origin service public private realm private_type JtR Format cracked_password", + "---- ------ ------- ------ ------- ----- ------------ ---------- ----------------", + " thisuser thispass Password ", + " this_username this_cracked_password Password " ]) end end