lib/msf/core/db_manager/exploit_attempt.rb
module Msf::DBManager::ExploitAttempt
# report_exploit() used to be used to track sessions and which modules
# opened them. That information is now available with the session table
# directly. TODO: kill this completely some day -- for now just warn if
# some other UI is actually using it.
def report_exploit(opts={})
wlog("Deprecated method call: report_exploit()\n" +
"report_exploit() options: #{opts.inspect}\n" +
"report_exploit() call stack:\n\t#{caller.join("\n\t")}"
)
end
def report_exploit_attempt(host, opts)
::ApplicationRecord.connection_pool.with_connection {
return if not host
info = {}
# Opts can be keyed by strings or symbols
::Mdm::VulnAttempt.column_names.each do |kn|
k = kn.to_sym
next if ['id', 'host_id'].include?(kn)
info[k] = opts[kn] if opts[kn]
info[k] = opts[k] if opts[k]
end
host.exploit_attempts.create(info)
}
end
# Create an `Mdm::ExploitAttempt` (and possibly an `Mdm::VulnAttempt`, if
# the `vuln` option is passed).
#
# @option (see #do_report_failure_or_success)
# @return (see #do_report_failure_or_success)
def report_exploit_failure(opts)
return unless opts.has_key?(:refs) && !opts[:refs].blank?
host = opts[:host] || return
wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
port = opts[:port]
proto = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
svc = opts[:service]
# Look up the service as appropriate
if port and svc.nil?
# only one result can be returned, as the +port+ field restricts potential results to a single service
svc = services(:workspace => wspace,
:hosts => {address: host},
:proto => proto,
:port => port).first
end
# Look up the host as appropriate
if !host || !host.kind_of?(::Mdm::Host)
if svc.kind_of? ::Mdm::Service
host = svc.host
else
host = get_host(workspace: wspace, address: host)
end
end
# Bail if we dont have a host object
return if not host
opts = opts.clone()
opts[:service] = svc
opts[:host] = host
do_report_failure_or_success(opts)
end
# Create an `Mdm::ExploitAttempt` (and possibly an `Mdm::VulnAttempt`, if
# the `vuln` option is passed).
#
# @return (see #do_report_failure_or_success)
def report_exploit_success(opts)
return unless opts[:refs]
host = opts[:host] || return
wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
# it is rude to modify arguments in place
opts = opts.clone()
port = opts[:port]
prot = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
svc = opts[:service]
# Look up or generate the service as appropriate
if port and svc.nil?
opts[:proto] ||= Msf::DBManager::DEFAULT_SERVICE_PROTO
opts[:service] = report_service(
workspace: wspace, host: host, port: port, proto: prot
)
end
do_report_failure_or_success(opts)
end
private
# @option opts [Array<String>, Array<Msf::Module::Reference>] :refs
# @option opts [Mdm::Host] :host
# @option opts [Mdm::Service] :service
# @option opts [Integer] :port (nil)
# @option opts ["tcp","udp"] :proto (Msf::DBManager::DEFAULT_SERVICE_PROTO) See `Mdm::Service::PROTOS`
# @option opts [Mdm::Vuln] :vuln (nil)
# @option opts [Time] :timestamp (nil)
# @option opts [Mdm::Vuln] :timestamp (nil)
# @option opts [String] :module (nil)
# @return [void]
def do_report_failure_or_success(opts)
return unless opts[:refs]
::ApplicationRecord.connection_pool.with_connection {
mrefs = opts[:refs]
host = opts[:host]
port = opts[:port]
prot = opts[:proto]
svc = opts[:service]
vuln = opts[:vuln]
timestamp = opts[:timestamp]
freason = opts[:fail_reason]
fdetail = opts[:fail_detail]
username = opts[:username]
mname = opts[:module]
if vuln.nil?
ref_names = mrefs.map { |ref|
if ref.respond_to?(:ctx_id) and ref.respond_to?(:ctx_val)
"#{ref.ctx_id}-#{ref.ctx_val}"
else
ref.to_s
end
}
# Create a references map from the module list
ref_objs = ::Mdm::Ref.where(name: ref_names)
# Try find a matching vulnerability
vuln = find_vuln_by_refs(ref_objs, host, svc)
end
attempt_info = {
:attempted_at => timestamp || Time.now.utc,
:exploited => (freason.nil? ? true : false),
:fail_detail => fdetail,
:fail_reason => freason,
:module => mname,
:username => username || "unknown",
}
attempt_info[:session_id] = opts[:session_id] if opts[:session_id]
attempt_info[:loot_id] = opts[:loot_id] if opts[:loot_id]
# We have match, lets create a vuln_attempt record
if vuln
attempt_info[:vuln_id] = vuln.id
vuln.vuln_attempts.create(attempt_info)
create_match_result_for_vuln(vuln,opts)
# Correct the vuln's associated service if necessary
if svc and vuln.service_id.nil?
vuln.service = svc
vuln.save
end
end
# Report an exploit attempt all the same
if svc
attempt_info[:port] = svc.port
attempt_info[:proto] = svc.proto
end
if port and svc.nil?
attempt_info[:port] = port
attempt_info[:proto] = prot || Msf::DBManager::DEFAULT_SERVICE_PROTO
end
host.exploit_attempts.create(attempt_info)
}
end
# Create a MetasploitDataModels::AutomaticExploitation::Match result for the given vuln
# @option opts [Integer] :run_id
# @return [void]
def create_match_result_for_vuln(vuln, opts)
run = MetasploitDataModels::AutomaticExploitation::Run.where(id:opts[:run_id]).last
if run.present?
match = MetasploitDataModels::AutomaticExploitation::Match.by_run_and_vuln(run,vuln).last
# If no match found in the current run
unless match.present?
# Create match if the vuln has the data we need to create a match
match = create_match_for_vuln(vuln,opts.merge(run:run))
end
create_match_result(opts.merge(match:match,run:run)) if match.present?
end
end
# Create a MetasploitDataModels::AutomaticExploitation::Match result with a success or failure state
# @option opts [MetasploitDataModels::AutomaticExploitation::Match] :match
# @option opts [MetasploitDataModels::AutomaticExploitation::Run] :run
# @return [void]
def create_match_result(opts)
if opts[:session_id]
state = MetasploitDataModels::AutomaticExploitation::MatchResult::SUCCEEDED
else
state = MetasploitDataModels::AutomaticExploitation::MatchResult::FAILED
end
MetasploitDataModels::AutomaticExploitation::MatchResult.create!(
match: opts[:match],
run: opts[:run],
state: state
)
end
# Create a MetasploitDataModels::AutomaticExploitation::Match for the given vuln
# @option vuln [Mdm::Vuln] :vuln
# @option opts [Mdm::Workspace] :workspace
# @option opts [String] :username
# @return [ MetasploitDataModels::AutomaticExploitation::Match, MetasploitDataModels::AutomaticExploitation::Run]
def create_match_for_vuln(vuln,opts)
wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
run = opts[:run]
module_fullname = opts[:module]
run.match_set.create_match_for_vuln(
vuln,
workspace: wspace,
module_fullname: module_fullname
)
end
end