@@ -444,7 +444,8 @@ def creds_search(*args)
444444 }
445445
446446 opts [ :workspace ] = framework . db . workspace
447- query = framework . db . creds ( opts )
447+ cred_cores = framework . db . creds ( opts ) . to_a
448+ cred_cores . sort_by! ( &:id )
448449 matched_cred_ids = [ ]
449450 cracked_cred_ids = [ ]
450451
@@ -459,7 +460,7 @@ def creds_search(*args)
459460 tbl = Rex ::Text ::Table . new ( tbl_opts )
460461 end
461462
462- filtered_query ( query , opts , origin_ranges , host_ranges ) do |core , service , origin , cracked_password_core |
463+ filter_cred_cores ( cred_cores , opts , origin_ranges , host_ranges ) do |core , service , origin , cracked_password_core |
463464 matched_cred_ids << core . id
464465 cracked_cred_ids << cracked_password_core . id if cracked_password_core . present?
465466
@@ -550,13 +551,30 @@ def cmd_creds_tabs(str, words)
550551
551552 protected
552553
553- def filtered_query ( query , opts , origin_ranges , host_ranges )
554- query . each do |core |
555- # we will not show the cracked password in a seperate row instead we will show in seperate column
556- # calling `find_by` against this result is a rails direct interaction that may fail in json database mode
557- if core . origin . kind_of? ( Metasploit ::Credential ::Origin ::CrackedPassword ) && query . find_by ( id : core . origin . metasploit_credential_core_id ) . present?
558- next
559- end
554+ # @param [Array<Metasploit::Credential::Core>] cores The list of cores to filter
555+ # @param [Hash] opts
556+ # @param [Array<Rex::Socket::RangeWalker>] origin_ranges
557+ # @param [Array<Rex::Socket::RangeWalker>] host_ranges
558+ # @yieldparam [Metasploit::Credential::Core] core
559+ # @yieldparam [Mdm::Service] service
560+ # @yieldparam [Metasploit::Credential::Origin] origin
561+ # @yieldparam [Metasploit::Credential::Origin::CrackedPassword] cracked_password_core
562+ def filter_cred_cores ( cores , opts , origin_ranges , host_ranges )
563+ # Some creds may have been cracked that exist outside of the filtered cores list, let's resolve them all to show the cracked value
564+ cores_by_id = cores . each_with_object ( { } ) { |core , hash | hash [ core . id ] = core }
565+ # Map of any originating core ids that have been cracked; The value is cracked core value
566+ cracked_core_id_to_cracked_value = cores . each_with_object ( { } ) do |core , hash |
567+ next unless core . origin . kind_of? ( Metasploit ::Credential ::Origin ::CrackedPassword )
568+ hash [ core . origin . metasploit_credential_core_id ] = core
569+ end
570+
571+ cores . each do |core |
572+ # Skip the cracked password if it's planned to be shown on the originating core row in a separate column
573+ is_duplicate_cracked_password_row = core . origin . kind_of? ( Metasploit ::Credential ::Origin ::CrackedPassword ) &&
574+ cracked_core_id_to_cracked_value . key? ( core . origin . metasploit_credential_core_id ) &&
575+ # The core might exist outside of the currently available cores to render
576+ cores_by_id . key? ( core . origin . metasploit_credential_core_id )
577+ next if is_duplicate_cracked_password_row
560578
561579 # Exclude non-blank username creds if that's what we're after
562580 if opts [ :user ] == '' && core . public && !( core . public . username . blank? )
@@ -581,18 +599,7 @@ def filtered_query(query, opts, origin_ranges, host_ranges)
581599 next
582600 end
583601
584- # this is a direct rails interaction that cannot cross the json db service layer, access of origin objects may be problematic
585- cracked_password_core = nil
586-
587- if !core . origin . kind_of? ( Metasploit ::Credential ::Origin ::CrackedPassword ) && core . public . cores . count > 1
588- core . public . cores . each do |potential_cracked_core |
589- next unless potential_cracked_core . origin . kind_of? ( Metasploit ::Credential ::Origin ::CrackedPassword )
590- if potential_cracked_core . origin . originating_core == core
591- cracked_password_core = potential_cracked_core
592- end
593- end
594- end
595-
602+ cracked_password_core = cracked_core_id_to_cracked_value . fetch ( core . id , nil )
596603 if core . logins . empty?
597604 service = service_from_origin ( core )
598605 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
0 commit comments