ca562a95d8
Truncating at 87 was the exact length to trim the last byte of an AES256 kerberos key. Furthermore, adding the (TRUNCATED) string to the end caused the resuting value to be larger than the original trucated value.
628 lines
22 KiB
Ruby
628 lines
22 KiB
Ruby
# -*- coding: binary -*-
|
|
|
|
require 'rexml/document'
|
|
require 'metasploit/framework/password_crackers/hashcat/formatter'
|
|
require 'metasploit/framework/password_crackers/jtr/formatter'
|
|
|
|
module Msf
|
|
module Ui
|
|
module Console
|
|
module CommandDispatcher
|
|
|
|
class Creds
|
|
require 'tempfile'
|
|
|
|
include Msf::Ui::Console::CommandDispatcher
|
|
include Metasploit::Credential::Creation
|
|
include Msf::Ui::Console::CommandDispatcher::Common
|
|
|
|
#
|
|
# The dispatcher's name.
|
|
#
|
|
def name
|
|
"Credentials Backend"
|
|
end
|
|
|
|
#
|
|
# Returns the hash of commands supported by this dispatcher.
|
|
#
|
|
def commands
|
|
{
|
|
"creds" => "List all credentials in the database"
|
|
}
|
|
end
|
|
|
|
def allowed_cred_types
|
|
%w(password ntlm hash KrbEncKey) + Metasploit::Credential::NonreplayableHash::VALID_JTR_FORMATS
|
|
end
|
|
|
|
#
|
|
# Returns true if the db is connected, prints an error and returns
|
|
# false if not.
|
|
#
|
|
# All commands that require an active database should call this before
|
|
# doing anything.
|
|
# TODO: abstract the db methods to a mixin that can be used by both dispatchers
|
|
#
|
|
def active?
|
|
if not framework.db.active
|
|
print_error("Database not connected")
|
|
return false
|
|
end
|
|
true
|
|
end
|
|
|
|
#
|
|
# Miscellaneous option helpers
|
|
#
|
|
|
|
#
|
|
# Can return return active or all, on a certain host or range, on a
|
|
# certain port or range, and/or on a service name.
|
|
#
|
|
def cmd_creds(*args)
|
|
return unless active?
|
|
|
|
# Short-circuit help
|
|
if args.delete("-h") || args.delete("--help")
|
|
cmd_creds_help
|
|
return
|
|
end
|
|
|
|
subcommand = args.shift
|
|
|
|
case subcommand
|
|
when 'help'
|
|
cmd_creds_help
|
|
when 'add'
|
|
creds_add(*args)
|
|
else
|
|
# then it's not actually a subcommand
|
|
args.unshift(subcommand) if subcommand
|
|
creds_search(*args)
|
|
end
|
|
|
|
end
|
|
|
|
#
|
|
# TODO: this needs to be cleaned up to use the new syntax
|
|
#
|
|
def cmd_creds_help
|
|
print_line
|
|
print_line "With no sub-command, list credentials. If an address range is"
|
|
print_line "given, show only credentials with logins on hosts within that"
|
|
print_line "range."
|
|
|
|
print_line
|
|
print_line "Usage - Listing credentials:"
|
|
print_line " creds [filter options] [address range]"
|
|
print_line
|
|
print_line "Usage - Adding credentials:"
|
|
print_line " creds add uses the following named parameters."
|
|
{
|
|
user: 'Public, usually a username',
|
|
password: 'Private, private_type Password.',
|
|
ntlm: 'Private, private_type NTLM Hash.',
|
|
postgres: 'Private, private_type postgres MD5',
|
|
pkcs12: 'Private, private_type pkcs12 archive file, must be a file path.',
|
|
'ssh-key' => 'Private, private_type SSH key, must be a file path.',
|
|
hash: 'Private, private_type Nonreplayable hash',
|
|
jtr: 'Private, private_type John the Ripper hash type.',
|
|
realm: 'Realm, ',
|
|
'realm-type'=>"Realm, realm_type (#{Metasploit::Model::Realm::Key::SHORT_NAMES.keys.join(' ')}), defaults to domain."
|
|
}.each_pair do |keyword, description|
|
|
print_line " #{keyword.to_s.ljust 10}: #{description}"
|
|
end
|
|
print_line
|
|
print_line "Examples: Adding"
|
|
print_line " # Add a user, password and realm"
|
|
print_line " creds add user:admin password:notpassword realm:workgroup"
|
|
print_line " # Add a user and password"
|
|
print_line " creds add user:guest password:'guest password'"
|
|
print_line " # Add a password"
|
|
print_line " creds add password:'password without username'"
|
|
print_line " # Add a user with an NTLMHash"
|
|
print_line " creds add user:admin ntlm:E2FC15074BF7751DD408E6B105741864:A1074A69B1BDE45403AB680504BBDD1A"
|
|
print_line " # Add a NTLMHash"
|
|
print_line " creds add ntlm:E2FC15074BF7751DD408E6B105741864:A1074A69B1BDE45403AB680504BBDD1A"
|
|
print_line " # Add a Postgres MD5"
|
|
print_line " creds add user:postgres postgres:md5be86a79bf2043622d58d5453c47d4860"
|
|
print_line " # Add a user with a PKCS12 file archive"
|
|
print_line " creds add user:alice pkcs12:/path/to/certificate.pfx"
|
|
print_line " # Add a user with an SSH key"
|
|
print_line " creds add user:sshadmin ssh-key:/path/to/id_rsa"
|
|
print_line " # Add a user and a NonReplayableHash"
|
|
print_line " creds add user:other hash:d19c32489b870735b5f587d76b934283 jtr:md5"
|
|
print_line " # Add a NonReplayableHash"
|
|
print_line " creds add hash:d19c32489b870735b5f587d76b934283"
|
|
|
|
print_line
|
|
print_line "General options"
|
|
print_line " -h,--help Show this help information"
|
|
print_line " -o <file> Send output to a file in csv/jtr (john the ripper) format."
|
|
print_line " If file name ends in '.jtr', that format will be used."
|
|
print_line " If file name ends in '.hcat', the hashcat format will be used."
|
|
print_line " csv by default."
|
|
print_line " -d,--delete Delete one or more credentials"
|
|
print_line
|
|
print_line "Filter options for listing"
|
|
print_line " -P,--password <text> List passwords that match this text"
|
|
print_line " -p,--port <portspec> List creds with logins on services matching this port spec"
|
|
print_line " -s <svc names> List creds matching comma-separated service names"
|
|
print_line " -u,--user <text> List users that match this text"
|
|
print_line " -t,--type <type> List creds of the specified type: password, ntlm, hash or any valid JtR format"
|
|
print_line " -O,--origins <IP> List creds that match these origins"
|
|
print_line " -r,--realm <realm> List creds that match this realm"
|
|
print_line " -R,--rhosts Set RHOSTS from the results of the search"
|
|
print_line " -v,--verbose Don't truncate long password hashes"
|
|
|
|
print_line
|
|
print_line "Examples, John the Ripper hash types:"
|
|
print_line " Operating Systems (starts with)"
|
|
print_line " Blowfish ($2a$) : bf"
|
|
print_line " BSDi (_) : bsdi"
|
|
print_line " DES : des,crypt"
|
|
print_line " MD5 ($1$) : md5"
|
|
print_line " SHA256 ($5$) : sha256,crypt"
|
|
print_line " SHA512 ($6$) : sha512,crypt"
|
|
print_line " Databases"
|
|
print_line " MSSQL : mssql"
|
|
print_line " MSSQL 2005 : mssql05"
|
|
print_line " MSSQL 2012/2014 : mssql12"
|
|
print_line " MySQL < 4.1 : mysql"
|
|
print_line " MySQL >= 4.1 : mysql-sha1"
|
|
print_line " Oracle : des,oracle"
|
|
print_line " Oracle 11 : raw-sha1,oracle11"
|
|
print_line " Oracle 11 (H type): dynamic_1506"
|
|
print_line " Oracle 12c : oracle12c"
|
|
print_line " Postgres : postgres,raw-md5"
|
|
|
|
print_line
|
|
print_line "Examples, listing:"
|
|
print_line " creds # Default, returns all credentials"
|
|
print_line " creds 1.2.3.4/24 # Return credentials with logins in this range"
|
|
print_line " creds -O 1.2.3.4/24 # Return credentials with origins in this range"
|
|
print_line " creds -p 22-25,445 # nmap port specification"
|
|
print_line " creds -s ssh,smb # All creds associated with a login on SSH or SMB services"
|
|
print_line " creds -t ntlm # All NTLM creds"
|
|
print_line
|
|
|
|
print_line "Example, deleting:"
|
|
print_line " # Delete all SMB credentials"
|
|
print_line " creds -d -s smb"
|
|
print_line
|
|
end
|
|
|
|
# @param private_type [Symbol] See `Metasploit::Credential::Creation#create_credential`
|
|
# @param username [String]
|
|
# @param password [String]
|
|
# @param realm [String]
|
|
# @param realm_type [String] A key in `Metasploit::Model::Realm::Key::SHORT_NAMES`
|
|
def creds_add(*args)
|
|
params = args.inject({}) do |hsh, n|
|
|
opt = n.split(':') # Splitting the string on colons.
|
|
hsh[opt[0]] = opt[1..-1].join(':') # everything before the first : is the key, reasembling everything after the colon. why ntlm hashes
|
|
hsh
|
|
end
|
|
|
|
begin
|
|
params.assert_valid_keys('user','password','realm','realm-type','ntlm','ssh-key','hash','address','port','protocol', 'service-name', 'jtr', 'pkcs12', 'postgres')
|
|
rescue ArgumentError => e
|
|
print_error(e.message)
|
|
end
|
|
|
|
# Verify we only have one type of private
|
|
if params.slice('password','ntlm','ssh-key','hash', 'pkcs12', 'postgres').length > 1
|
|
private_keys = params.slice('password','ntlm','ssh-key','hash', 'pkcs12', 'postgres').keys
|
|
print_error("You can only specify a single Private type. Private types given: #{private_keys.join(', ')}")
|
|
return
|
|
end
|
|
|
|
login_keys = params.slice('address','port','protocol','service-name')
|
|
if login_keys.any? and login_keys.length < 3
|
|
missing_login_keys = ['host','port','proto','service-name'] - login_keys.keys
|
|
print_error("Creating a login requires a address, a port, and a protocol. Missing params: #{missing_login_keys}")
|
|
return
|
|
end
|
|
|
|
data = {
|
|
workspace_id: framework.db.workspace.id,
|
|
origin_type: :import,
|
|
filename: 'msfconsole'
|
|
}
|
|
|
|
data[:username] = params['user'] if params.key? 'user'
|
|
|
|
if params.key? 'realm'
|
|
if params.key? 'realm-type'
|
|
if Metasploit::Model::Realm::Key::SHORT_NAMES.key? params['realm-type']
|
|
data[:realm_key] = Metasploit::Model::Realm::Key::SHORT_NAMES[params['realm-type']]
|
|
else
|
|
valid = Metasploit::Model::Realm::Key::SHORT_NAMES.keys.map{|n|"'#{n}'"}.join(", ")
|
|
print_error("Invalid realm type: #{params['realm_type']}. Valid Values: #{valid}")
|
|
end
|
|
else
|
|
data[:realm_key] = Metasploit::Model::Realm::Key::ACTIVE_DIRECTORY_DOMAIN
|
|
end
|
|
data[:realm_value] = params['realm']
|
|
end
|
|
|
|
if params.key? 'password'
|
|
data[:private_type] = :password
|
|
data[:private_data] = params['password']
|
|
end
|
|
|
|
if params.key? 'ntlm'
|
|
data[:private_type] = :ntlm_hash
|
|
data[:private_data] = params['ntlm']
|
|
end
|
|
|
|
if params.key? 'ssh-key'
|
|
begin
|
|
key_data = File.read(params['ssh-key'])
|
|
rescue ::Errno::EACCES, ::Errno::ENOENT => e
|
|
print_error("Failed to add ssh key: #{e}")
|
|
end
|
|
data[:private_type] = :ssh_key
|
|
data[:private_data] = key_data
|
|
end
|
|
|
|
if params.key? 'pkcs12'
|
|
begin
|
|
# pkcs12 is a binary format, but for persisting we Base64 encode it
|
|
pkcs12_data = Base64.strict_encode64(File.binread(params['pkcs12']))
|
|
rescue ::Errno::EACCES, ::Errno::ENOENT => e
|
|
print_error("Failed to add pkcs12 archive: #{e}")
|
|
end
|
|
data[:private_type] = :pkcs12
|
|
data[:private_data] = pkcs12_data
|
|
end
|
|
|
|
if params.key? 'hash'
|
|
data[:private_type] = :nonreplayable_hash
|
|
data[:private_data] = params['hash']
|
|
data[:jtr_format] = params['jtr'] if params.key? 'jtr'
|
|
end
|
|
|
|
if params.key? 'postgres'
|
|
data[:private_type] = :postgres_md5
|
|
if params['postgres'].downcase.start_with?('md5')
|
|
data[:private_data] = params['postgres']
|
|
data[:jtr_format] = 'postgres'
|
|
else
|
|
print_error("Postgres MD5 hashes should start with 'md5'")
|
|
end
|
|
end
|
|
|
|
begin
|
|
if login_keys.any?
|
|
data[:address] = params['address']
|
|
data[:port] = params['port']
|
|
data[:protocol] = params['protocol']
|
|
data[:service_name] = params['service-name']
|
|
framework.db.create_credential_and_login(data)
|
|
else
|
|
framework.db.create_credential(data)
|
|
end
|
|
rescue ActiveRecord::RecordInvalid => e
|
|
print_error("Failed to add #{data['private_type']}: #{e}")
|
|
end
|
|
end
|
|
|
|
def service_from_origin(core)
|
|
# Depending on the origin of the cred, there may or may not be a way to retrieve the associated service
|
|
case core.origin
|
|
when Metasploit::Credential::Origin::Service
|
|
return core.origin.service
|
|
end
|
|
end
|
|
|
|
def build_service_info(service)
|
|
if service.name.present?
|
|
info = "#{service.port}/#{service.proto} (#{service.name})"
|
|
else
|
|
info = "#{service.port}/#{service.proto}"
|
|
end
|
|
info
|
|
end
|
|
|
|
def creds_search(*args)
|
|
host_ranges = []
|
|
origin_ranges = []
|
|
port_ranges = []
|
|
svcs = []
|
|
rhosts = []
|
|
opts = {}
|
|
|
|
set_rhosts = false
|
|
truncate = true
|
|
|
|
cred_table_columns = [ 'host', 'origin' , 'service', 'public', 'private', 'realm', 'private_type', 'JtR Format', 'cracked_password' ]
|
|
delete_count = 0
|
|
search_term = nil
|
|
|
|
while (arg = args.shift)
|
|
case arg
|
|
when '-o'
|
|
output_file = args.shift
|
|
if (!output_file)
|
|
print_error('Invalid output filename')
|
|
return
|
|
end
|
|
output_file = ::File.expand_path(output_file)
|
|
truncate = false
|
|
when '-p', '--port'
|
|
unless (arg_port_range(args.shift, port_ranges, true))
|
|
return
|
|
end
|
|
when '-t', '--type'
|
|
ptype = args.shift
|
|
opts[:ptype] = ptype
|
|
if (!ptype)
|
|
print_error('Argument required for -t')
|
|
return
|
|
end
|
|
when '-s', '--service'
|
|
service = args.shift
|
|
if (!service)
|
|
print_error('Argument required for -s')
|
|
return
|
|
end
|
|
svcs = service.split(/[\s]*,[\s]*/)
|
|
opts[:svcs] = svcs
|
|
when '-P', '--password'
|
|
if !(opts[:pass] = args.shift)
|
|
print_error('Argument required for -P')
|
|
return
|
|
end
|
|
when '-u', '--user'
|
|
if !(opts[:user] = args.shift)
|
|
print_error('Argument required for -u')
|
|
return
|
|
end
|
|
when '-d', '--delete'
|
|
mode = :delete
|
|
when '-R', '--rhosts'
|
|
set_rhosts = true
|
|
when '-O', '--origins'
|
|
hosts = args.shift
|
|
opts[:hosts] = hosts
|
|
if !hosts
|
|
print_error('Argument required for -O')
|
|
return
|
|
end
|
|
arg_host_range(hosts, origin_ranges)
|
|
when '-S', '--search-term'
|
|
search_term = args.shift
|
|
opts[:search_term] = search_term
|
|
when '-v', '--verbose'
|
|
truncate = false
|
|
when '-r', '--realm'
|
|
opts[:realm] = args.shift
|
|
else
|
|
# Anything that wasn't an option is a host to search for
|
|
unless (arg_host_range(arg, host_ranges))
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
# If we get here, we're searching. Delete implies search
|
|
|
|
if ptype
|
|
type = case ptype.downcase
|
|
when 'password'
|
|
Metasploit::Credential::Password
|
|
when 'hash'
|
|
Metasploit::Credential::PasswordHash
|
|
when 'ntlm'
|
|
Metasploit::Credential::NTLMHash
|
|
when 'KrbEncKey'.downcase
|
|
Metasploit::Credential::KrbEncKey
|
|
when *Metasploit::Credential::NonreplayableHash::VALID_JTR_FORMATS
|
|
opts[:jtr_format] = ptype
|
|
Metasploit::Credential::NonreplayableHash
|
|
else
|
|
print_error("Unrecognized credential type #{ptype} -- must be one of #{allowed_cred_types.join(',')}")
|
|
return
|
|
end
|
|
end
|
|
|
|
opts[:type] = type if type
|
|
|
|
# normalize
|
|
ports = port_ranges.flatten.uniq
|
|
opts[:ports] = ports unless ports.empty?
|
|
svcs.flatten!
|
|
tbl_opts = {
|
|
'Header' => "Credentials",
|
|
# For now, don't perform any word wrapping on the cred table as it breaks the workflow of
|
|
# copying credentials and pasting them into applications
|
|
'WordWrap' => false,
|
|
'Columns' => cred_table_columns,
|
|
'SearchTerm' => search_term
|
|
}
|
|
|
|
opts[:workspace] = framework.db.workspace
|
|
cred_cores = framework.db.creds(opts).to_a
|
|
cred_cores.sort_by!(&:id)
|
|
matched_cred_ids = []
|
|
cracked_cred_ids = []
|
|
|
|
if output_file&.ends_with?('.hcat')
|
|
output_file = ::File.open(output_file, 'wb')
|
|
output_formatter = Metasploit::Framework::PasswordCracker::Hashcat::Formatter.method(:hash_to_hashcat)
|
|
elsif output_file&.ends_with?('.jtr')
|
|
output_file = ::File.open(output_file, 'wb')
|
|
output_formatter = Metasploit::Framework::PasswordCracker::JtR::Formatter.method(:hash_to_jtr)
|
|
else
|
|
output_file = ::File.open(output_file, 'wb') unless output_file.blank?
|
|
tbl = Rex::Text::Table.new(tbl_opts)
|
|
end
|
|
|
|
filter_cred_cores(cred_cores, 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)
|
|
output_file.puts(formatted) unless formatted.blank?
|
|
end
|
|
|
|
unless tbl.nil?
|
|
public_val = core.public ? core.public.username : ''
|
|
if core.private
|
|
# Show the human readable description by default, unless the user ran with `--verbose` and wants to see the cred data
|
|
private_val = truncate ? core.private.to_s : core.private.data
|
|
else
|
|
private_val = ''
|
|
end
|
|
if truncate && private_val.to_s.length > 88
|
|
private_val = "#{private_val[0,76]} (TRUNCATED)"
|
|
end
|
|
realm_val = core.realm ? core.realm.value : ''
|
|
human_val = core.private ? core.private.class.model_name.human : ''
|
|
if human_val == ''
|
|
jtr_val = '' #11433, private can be nil
|
|
else
|
|
jtr_val = core.private.jtr_format ? core.private.jtr_format : ''
|
|
end
|
|
|
|
if service.nil?
|
|
host = ''
|
|
service_info = ''
|
|
else
|
|
host = service.host.address
|
|
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,
|
|
service_info,
|
|
public_val,
|
|
private_val,
|
|
realm_val,
|
|
human_val, #private type
|
|
jtr_val,
|
|
cracked_password_val
|
|
]
|
|
end
|
|
end
|
|
|
|
if output_file.nil?
|
|
print_line(tbl.to_s)
|
|
else
|
|
output_file.write(tbl.to_csv) if output_formatter.nil?
|
|
output_file.close
|
|
print_status("Wrote creds to #{output_file.path}")
|
|
end
|
|
|
|
if mode == :delete
|
|
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
|
|
# of hosts to go into RHOSTS.
|
|
set_rhosts_from_addrs(rhosts.uniq) if set_rhosts
|
|
print_status("Deleted #{delete_count} creds") if delete_count > 0
|
|
end
|
|
|
|
def cmd_creds_tabs(str, words)
|
|
case words.length
|
|
when 1
|
|
# subcommands
|
|
tabs = [ 'add-ntlm', 'add-password', 'add-hash', 'add-ssh-key', ]
|
|
when 2
|
|
tabs = if words[1] == 'add-ssh-key'
|
|
tab_complete_filenames(str, words)
|
|
else
|
|
[]
|
|
end
|
|
#when 5
|
|
# tabs = Metasploit::Model::Realm::Key::SHORT_NAMES.keys
|
|
else
|
|
tabs = []
|
|
end
|
|
return tabs
|
|
end
|
|
|
|
protected
|
|
|
|
# @param [Array<Metasploit::Credential::Core>] cores The list of cores to filter
|
|
# @param [Hash] opts
|
|
# @param [Array<Rex::Socket::RangeWalker>] origin_ranges
|
|
# @param [Array<Rex::Socket::RangeWalker>] host_ranges
|
|
# @yieldparam [Metasploit::Credential::Core] core
|
|
# @yieldparam [Mdm::Service] service
|
|
# @yieldparam [Metasploit::Credential::Origin] origin
|
|
# @yieldparam [Metasploit::Credential::Origin::CrackedPassword] cracked_password_core
|
|
def filter_cred_cores(cores, opts, origin_ranges, host_ranges)
|
|
# Some creds may have been cracked that exist outside of the filtered cores list, let's resolve them all to show the cracked value
|
|
cores_by_id = cores.each_with_object({}) { |core, hash| hash[core.id] = core }
|
|
# Map of any originating core ids that have been cracked; The value is cracked core value
|
|
cracked_core_id_to_cracked_value = cores.each_with_object({}) do |core, hash|
|
|
next unless core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword)
|
|
hash[core.origin.metasploit_credential_core_id] = core
|
|
end
|
|
|
|
cores.each do |core|
|
|
# Skip the cracked password if it's planned to be shown on the originating core row in a separate column
|
|
is_duplicate_cracked_password_row = core.origin.kind_of?(Metasploit::Credential::Origin::CrackedPassword) &&
|
|
cracked_core_id_to_cracked_value.key?(core.origin.metasploit_credential_core_id) &&
|
|
# The core might exist outside of the currently available cores to render
|
|
cores_by_id.key?(core.origin.metasploit_credential_core_id)
|
|
next if is_duplicate_cracked_password_row
|
|
|
|
# Exclude non-blank username creds if that's what we're after
|
|
if opts[:user] == '' && core.public && !(core.public.username.blank?)
|
|
next
|
|
end
|
|
|
|
# Exclude non-blank password creds if that's what we're after
|
|
if opts[:pass] == '' && core.private && !(core.private.data.blank?)
|
|
next
|
|
end
|
|
|
|
origin = ''
|
|
if core.origin.kind_of?(Metasploit::Credential::Origin::Service)
|
|
service = framework.db.services(id: core.origin.service_id).first
|
|
origin = service.host.address
|
|
elsif core.origin.kind_of?(Metasploit::Credential::Origin::Session)
|
|
session = framework.db.sessions(id: core.origin.session_id).first
|
|
origin = session.host.address
|
|
end
|
|
|
|
if origin_ranges.present? && !origin_ranges.any? { |range| range.include?(origin) }
|
|
next
|
|
end
|
|
|
|
cracked_password_core = cracked_core_id_to_cracked_value.fetch(core.id, nil)
|
|
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, cracked_password_core
|
|
else
|
|
core.logins.each do |login|
|
|
service = framework.db.services(id: login.service_id).first
|
|
# If none of this Core's associated Logins is for a host within
|
|
# the user-supplied RangeWalker, then we don't have any reason to
|
|
# print it out. However, we treat the absence of ranges as meaning
|
|
# all hosts.
|
|
if host_ranges.present? && !host_ranges.any? { |range| range.include?(service.host.address) }
|
|
next
|
|
end
|
|
|
|
yield core, service, origin, cracked_password_core
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end end end end
|