639 lines
23 KiB
Ruby
639 lines
23 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.",
|
|
'adcs-ca' => 'CA, Certificate Authority that issued the pkcs12 certificate',
|
|
'adcs-template' => 'ADCS Template, template used to issue the pkcs12 certificate',
|
|
'pkcs12-password' => 'The password to decrypt the Pkcs12, defaults to an empty password'
|
|
}.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', 'adcs-ca', 'adcs-template', 'pkcs12-password')
|
|
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
|
|
data[:private_metadata] = {}
|
|
data[:private_metadata][:adcs_ca] = params['adcs-ca'] if params['adcs-ca']
|
|
data[:private_metadata][:adcs_template] = params['adcs-template'] if params['adcs-template']
|
|
data[:private_metadata][:pkcs12_password] = params['pkcs12-password'] if params['pkcs12-password']
|
|
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 = [ 'id', '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::NonreplayableHash
|
|
when 'ntlm'
|
|
Metasploit::Credential::NTLMHash
|
|
when 'KrbEncKey'.downcase
|
|
Metasploit::Credential::KrbEncKey
|
|
when 'pkcs12'
|
|
Metasploit::Credential::Pkcs12
|
|
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
|
|
row = [
|
|
core.id,
|
|
host,
|
|
origin,
|
|
service_info,
|
|
public_val,
|
|
private_val,
|
|
realm_val,
|
|
human_val, #private type
|
|
jtr_val,
|
|
cracked_password_val
|
|
]
|
|
tbl << row
|
|
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
|