Skip to content

Commit 257f6db

Browse files
authored
Land #20263, adds registration of VulnAttempts for Exploit/Auxiliary modules
Register VulnAttempts for both Exploit and Auxiliary modules
2 parents 3b67e55 + f5161ea commit 257f6db

File tree

8 files changed

+155
-55
lines changed

8 files changed

+155
-55
lines changed

lib/msf/base/simple/auxiliary.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,15 +189,26 @@ def self.job_run_proc(ctx, &block)
189189
rescue Msf::Auxiliary::Failed => e
190190
mod.error = e
191191
mod.print_error("Auxiliary aborted due to failure: #{e.message}")
192+
193+
# The caller should have already set mod.fail_reason
194+
if mod.fail_reason == Msf::Module::Failure::None
195+
mod.fail_reason = Msf::Module::Failure::Unknown
196+
end
197+
mod.fail_detail ||= e.to_s
198+
192199
mod.cleanup
193200
return
194201
rescue ::Timeout::Error => e
195202
mod.error = e
203+
mod.fail_reason = Msf::Module::Failure::TimeoutExpired
204+
mod.fail_detail ||= e.to_s
196205
mod.print_error("Auxiliary triggered a timeout exception")
197206
mod.cleanup
198207
return
199208
rescue ::Interrupt => e
200209
mod.error = e
210+
mod.fail_reason = Msf::Module::Failure::UserInterrupt
211+
mod.fail_detail ||= e.to_s
201212
mod.print_error("Stopping running against current target...")
202213
mod.cleanup
203214
mod.print_status("Control-C again to force quit all targets.")
@@ -209,9 +220,13 @@ def self.job_run_proc(ctx, &block)
209220
return
210221
rescue ::Msf::OptionValidateError => e
211222
mod.error = e
223+
mod.fail_reason = Msf::Module::Failure::BadConfig
224+
mod.fail_detail ||= e.to_s
212225
::Msf::Ui::Formatter::OptionValidateError.print_error(mod, e)
213226
rescue ::Exception => e
214227
mod.error = e
228+
mod.fail_reason = Msf::Module::Failure::Unknown
229+
mod.fail_detail ||= e.to_s
215230
mod.print_error("Auxiliary failed: #{e.class} #{e}")
216231
if(e.class.to_s != 'Msf::OptionValidateError')
217232
mod.print_error("Call stack:")
@@ -226,6 +241,16 @@ def self.job_run_proc(ctx, &block)
226241

227242
end
228243
return result
244+
ensure
245+
# Register an attempt in the database (an `Mdm::ExploitAttempt` (and
246+
# possibly an `Mdm::VulnAttempt`).
247+
#
248+
# Since auxiliary modules don't report clearly when it is a success or a
249+
# failure, we are calling #report_failure keeping the `mod.fail_reason`
250+
# value unchanged. This value is set to `Msf::Module::Failure::None` when
251+
# no error was reported. It should be set to another
252+
# `Msf::Module::Failure::*` value otherwise.
253+
mod.report_failure
229254
end
230255

231256
#

lib/msf/core/auxiliary.rb

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def initialize(info = {})
4646

4747
self.sockets = Array.new
4848
self.queue = Array.new
49+
self.fail_reason = Msf::Module::Failure::None
4950
end
5051

5152
#
@@ -159,9 +160,27 @@ def abort_sockets
159160

160161
# Override Msf::Module#fail_with for Msf::Simple::Auxiliary::job_run_proc
161162
def fail_with(reason, msg = nil)
162-
raise Msf::Auxiliary::Failed, "#{reason.to_s}: #{msg}"
163+
allowed_values = Msf::Module::Failure.constants.collect {|e| Msf::Module::Failure.const_get(e)}
164+
if allowed_values.include?(reason)
165+
self.fail_reason = reason
166+
else
167+
self.fail_reason = Msf::Module::Failure::Unknown
168+
end
169+
170+
self.fail_detail = msg
171+
raise Msf::Auxiliary::Failed, "#{reason.to_s}: #{(msg || "No failure message given")}"
163172
end
164173

174+
#
175+
# The reason why the module was not successful (one of the constant defined above)
176+
#
177+
attr_accessor :fail_reason
178+
179+
#
180+
# Detailed exception string indicating why the module was not successful
181+
#
182+
attr_accessor :fail_detail
183+
165184
attr_accessor :queue
166185

167186
protected

lib/msf/core/db_manager/exploit_attempt.rb

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,16 +33,31 @@ def report_exploit_attempt(host, opts)
3333
# @option (see #do_report_failure_or_success)
3434
# @return (see #do_report_failure_or_success)
3535
def report_exploit_failure(opts)
36-
return unless opts.has_key?(:refs) && !opts[:refs].blank?
37-
host = opts[:host] || return
36+
return unless opts[:refs].present? && opts[:host].present?
3837

38+
host = opts[:host]
3939
wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
40-
port = opts[:port]
41-
proto = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
42-
svc = opts[:service]
40+
opts = opts.clone()
41+
port = opts[:port]
42+
proto = opts[:proto] || Msf::DBManager::DEFAULT_SERVICE_PROTO
43+
svc = opts[:service]
44+
rids = opts.delete(:ref_ids) || []
45+
46+
opts[:refs].each do |ref|
47+
if ref.instance_of?(Mdm::Module::Ref)
48+
str = ref.name
49+
elsif (ref.respond_to?(:ctx_id)) && (ref.respond_to?(:ctx_val))
50+
str = "#{ref.ctx_id}-#{ref.ctx_val}"
51+
elsif (ref.is_a?(Hash) && ref[:ctx_id] && ref[:ctx_val])
52+
str = "#{ref[:ctx_id]}-#{ref[:ctx_val]}"
53+
elsif ref.is_a?(String)
54+
str = ref
55+
end
56+
rids << find_or_create_ref(name: str) unless str.nil?
57+
end
4358

4459
# Look up the service as appropriate
45-
if port and svc.nil?
60+
if port && svc.nil?
4661
# only one result can be returned, as the +port+ field restricts potential results to a single service
4762
svc = services(:workspace => wspace,
4863
:hosts => {address: host},
@@ -52,19 +67,26 @@ def report_exploit_failure(opts)
5267

5368
# Look up the host as appropriate
5469
if !host || !host.kind_of?(::Mdm::Host)
55-
if svc.kind_of? ::Mdm::Service
70+
if svc&.kind_of? ::Mdm::Service
5671
host = svc.host
5772
else
5873
host = get_host(workspace: wspace, address: host)
5974
end
6075
end
6176

6277
# Bail if we dont have a host object
63-
return if not host
78+
return unless host
79+
80+
vuln = nil
81+
if rids.present?
82+
# Try to find an existing vulnerability with the same service & references
83+
# or, if svc is nil, with the same host & references
84+
vuln = find_vuln_by_refs(rids, host, svc, false)
85+
end
6486

65-
opts = opts.clone()
6687
opts[:service] = svc
6788
opts[:host] = host
89+
opts[:vuln] = vuln if vuln
6890

6991
do_report_failure_or_success(opts)
7092
end
@@ -137,7 +159,7 @@ def do_report_failure_or_success(opts)
137159
ref_objs = ::Mdm::Ref.where(name: ref_names)
138160

139161
# Try find a matching vulnerability
140-
vuln = find_vuln_by_refs(ref_objs, host, svc)
162+
vuln = find_vuln_by_refs(ref_objs, host, svc, false)
141163
end
142164

143165
attempt_info = {

lib/msf/core/db_manager/vuln.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def find_vuln_by_details(details_map, host, service=nil)
4444
other_vulns.empty? ? nil : other_vulns.first
4545
end
4646

47-
def find_vuln_by_refs(refs, host, service=nil)
48-
ref_ids = refs.find_all { |ref| ref.name.starts_with? 'CVE-'}
47+
def find_vuln_by_refs(refs, host, service = nil, cve_only = true)
48+
ref_ids = cve_only ? refs.find_all { |ref| ref.name.starts_with? 'CVE-'} : refs
4949
relation = host.vulns.joins(:refs)
5050
if !service.try(:id).nil?
5151
return relation.where(service_id: service.try(:id), refs: { id: ref_ids}).first

lib/msf/core/exploit.rb

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,34 +1460,6 @@ def handle_exception e
14601460
return self.fail_reason
14611461
end
14621462

1463-
def report_failure
1464-
return unless framework.db and framework.db.active
1465-
1466-
info = {
1467-
:timestamp => Time.now.utc,
1468-
:workspace => framework.db.find_workspace(self.workspace),
1469-
:module => self.fullname,
1470-
:fail_reason => self.fail_reason,
1471-
:fail_detail => self.fail_detail,
1472-
:target_name => self.target.name,
1473-
:username => self.owner,
1474-
:refs => self.references,
1475-
:run_id => self.datastore['RUN_ID']
1476-
}
1477-
1478-
if self.datastore['RHOST'] && (self.options['RHOST'] || self.options['RHOSTS'])
1479-
info[:host] = self.datastore['RHOST']
1480-
end
1481-
1482-
if self.datastore['RPORT'] and self.options['RPORT']
1483-
info[:port] = self.datastore['RPORT']
1484-
if self.class.ancestors.include?(Msf::Exploit::Remote::Tcp)
1485-
info[:proto] = 'tcp'
1486-
end
1487-
end
1488-
1489-
framework.db.report_exploit_failure(info)
1490-
end
14911463

14921464
##
14931465
#

lib/msf/core/module.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class Module
4848
include Msf::Module::Author
4949
include Msf::Module::Compatibility
5050
include Msf::Module::DataStore
51+
include Msf::Module::Failure
5152
include Msf::Module::FullName
5253
include Msf::Module::ModuleInfo
5354
include Msf::Module::ModuleStore

lib/msf/core/module/failure.rb

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,35 @@ module Msf::Module::Failure
3838

3939
# The exploit was interrupted by the user
4040
UserInterrupt = 'user-interrupt'
41-
end
41+
42+
43+
def report_failure
44+
return unless framework.db and framework.db.active
45+
46+
info = {
47+
timestamp: Time.now.utc,
48+
workspace: framework.db.find_workspace(self.workspace),
49+
module: self.fullname,
50+
fail_reason: self.fail_reason,
51+
fail_detail: self.fail_detail,
52+
username: self.owner,
53+
refs: self.references,
54+
run_id: self.datastore['RUN_ID']
55+
}
56+
info[:target_name] = self.target.name if self.respond_to?(:target)
57+
58+
if self.datastore['RHOST'] && (self.options['RHOST'] || self.options['RHOSTS'])
59+
info[:host] = self.datastore['RHOST']
60+
end
61+
62+
if self.datastore['RPORT'] and self.options['RPORT']
63+
info[:port] = self.datastore['RPORT']
64+
if self.class.ancestors.include?(Msf::Exploit::Remote::Tcp)
65+
info[:proto] = 'tcp'
66+
end
67+
end
68+
69+
framework.db.report_exploit_failure(info)
70+
end
71+
72+
end

0 commit comments

Comments
 (0)