CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/analyze.rb
Views: 11779
1
class Msf::Analyze
2
3
def initialize(framework)
4
@framework = framework
5
end
6
7
def host(eval_host, payloads: nil)
8
unless eval_host.vulns
9
return {}
10
end
11
12
# Group related vulns
13
vuln_families = group_vulns(eval_host.vulns)
14
15
# finds all modules that have references matching those found on host vulns with service data
16
suggested_modules = suggest_modules_for_vulns(eval_host, vuln_families, payloads: payloads)
17
18
{results: suggested_modules}
19
end
20
21
private
22
23
def group_vulns(vulns)
24
return [] if vulns.empty?
25
26
vulns = vulns.map do |vuln|
27
[vuln, Set.new(vuln.refs.map {|r| r.name.upcase})]
28
end
29
grouped_vulns = Hash.new
30
31
vulns.each_index do |ii|
32
vuln, refs = vulns[ii]
33
grouped_vulns[vuln] ||= [Set.new, Set.new]
34
grouped_vulns[vuln][0] << vuln
35
grouped_vulns[vuln][1].merge(refs)
36
37
vulns[(ii+1)..-1].each do |candidate_match, candidate_refs|
38
# TODO: measure if sorting the refs ahead of time and doing a O(n + m)
39
# walk here is faster
40
if candidate_refs.intersect? refs
41
if grouped_vulns[candidate_match]
42
# For merging two different transitive sets that overlap, we need
43
# to merge the individual grouping elements and then use those
44
# cells inside both the grouping arrays so that all the
45
# already-visited vulns are rolled into the big new set
46
grouped_vulns[candidate_match][0] = grouped_vulns[candidate_match][0].merge(grouped_vulns[vuln][0])
47
grouped_vulns[candidate_match][1] = grouped_vulns[candidate_match][1].merge(grouped_vulns[vuln][1])
48
grouped_vulns[vuln][0] = grouped_vulns[candidate_match][0]
49
grouped_vulns[vuln][1] = grouped_vulns[candidate_match][1]
50
else
51
# Whoever was initialized first has the canonical set
52
grouped_vulns[candidate_match] = grouped_vulns[vuln]
53
end
54
end
55
end
56
end
57
58
vuln_families = grouped_vulns.values
59
vuln_families.uniq!
60
vuln_families
61
end
62
63
def suggest_modules_for_vulns(eval_host, vuln_families, payloads: nil)
64
mrefs, _mports, _mservs = Msf::Modules::Metadata::Cache.instance.all_exploit_maps
65
suggested_modules = []
66
67
evaluated_module_targets = Set.new
68
to_evaluate_with_defaults = Array.new
69
vuln_families.each do |vulns, refs|
70
found_modules = mrefs.values_at(*refs).compact.reduce(:+)
71
next unless found_modules
72
73
services = vulns.map(&:service).compact
74
found_modules.each do |fnd_mod|
75
if services.any?
76
services.each do |svc|
77
port = svc.port
78
next if evaluated_module_targets.include?([fnd_mod, port])
79
80
creds = @framework.db.creds(svcs: [svc.name], workspace: eval_host.workspace)
81
r = Result.new(mod: fnd_mod, host: eval_host, datastore: {'rport': port},
82
available_creds: creds, payloads: payloads, framework: @framework)
83
if r.match?
84
suggested_modules << r.evaluate
85
end
86
evaluated_module_targets << [fnd_mod, port]
87
end
88
else
89
# Only have the default port to go off of, at least for this vuln,
90
# prefer using the service data if available on a different vuln
91
# entry
92
port = fnd_mod.rport
93
to_evaluate_with_defaults << [fnd_mod, port]
94
end
95
end
96
end
97
98
to_evaluate_with_defaults.each do |fnd_mod, port|
99
next if evaluated_module_targets.include?([fnd_mod, port])
100
101
creds = @framework.db.creds(port: port, workspace: eval_host.workspace) if port
102
r = Result.new(mod: fnd_mod, host: eval_host, datastore: {'rport': port},
103
available_creds: creds, payloads: payloads, framework: @framework)
104
105
if r.match?
106
suggested_modules << r.evaluate
107
end
108
evaluated_module_targets << [fnd_mod, port]
109
end
110
111
suggested_modules
112
end
113
end
114
115