CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/core/analyze/result.rb
Views: 1904
1
class Msf::Analyze::Result
2
3
attr_reader :datastore
4
attr_reader :host
5
attr_reader :invalid
6
attr_reader :missing
7
attr_reader :mod
8
attr_reader :required
9
10
def initialize(host:, mod:, framework:, available_creds: nil, payloads: nil, datastore: nil)
11
@host = host
12
@mod = mod
13
@required = []
14
@missing = []
15
@invalid = []
16
@datastore = datastore&.transform_keys(&:downcase) || Hash.new
17
@available_creds = available_creds
18
@wanted_payloads = payloads
19
@framework = framework
20
21
determine_likely_compatibility
22
end
23
24
def evaluate(with: @datastore, payloads: @wanted_payloads)
25
@datastore = with
26
@wanted_payloads = payloads
27
28
determine_prerequisites
29
self
30
end
31
32
# Returns state for module readiness.
33
#
34
# @return :sym the stateful result one of:
35
# * :READY_FOR_TEST, :REQUIRES_CRED, :REUSE_PREVIOUS_OPTIONS, :MISSING_REQUIRED_OPTION, :MISSING_PAYLOAD, :REQUIRES_SESSION, :NEEDS_TARGET_ACTION, :INVALID_OPTION, :NOT_APPLICABLE
36
#
37
# | State | Detailed Reason |
38
# |-------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
39
# | READY_FOR_TEST | Ready for Test - All required options have defaults |
40
# | REQUIRES_CRED | Requires DB Credentials - Required options have defaults except credential values - if db contains known credentials for required fields validation is possible |
41
# | REUSE_PREVIOUS_OPTIONS | Reuse Previous Options- Taken as an analysis option, process existing module runs to gather options set for same module on other hosts |
42
# | MISSING_REQUIRED_OPTION | Missing Required Options - Some options are not available requiring manual configuration |
43
# | MISSING_PAYLOAD | Missing Compatible Payload - Known host details and payload restrictions exclude all payloads |
44
# | REQUIRES_SESSION | Requires Session - Modules that require an existing session can cannot be executed as first entry point on targets |
45
# | NEEDS_TARGET_ACTION | Needs target action - Module that either start a service and need the target to respond in a way that may require user interaction. (Browser exploit, needs target reboot....) |
46
# | INVALID_OPTION | Options used in Result evaluation are invalid |
47
# | NOT_APPLICABLE | Module is not applicable to the host |
48
def state
49
if ready_for_test? || (@missing.empty? && @invalid.empty?)
50
:READY_FOR_TEST
51
# TODO: result eval can look for previous attempts to determine :REUSE_PREVIOUS_OPTIONS state
52
else
53
unless @missing.empty?
54
if @missing.include?(:credential)
55
:REQUIRES_CRED
56
elsif @missing.include?(:payload_match)
57
:MISSING_PAYLOAD
58
elsif @missing.include?(:session)
59
:REQUIRES_SESSION
60
elsif @missing.include?(:os_match)
61
:NOT_APPLICABLE
62
# TODO: result eval check for module stance to determine :NEEDS_TARGET_ACTION state?
63
else
64
:MISSING_REQUIRED_OPTION
65
end
66
else
67
:INVALID_OPTION
68
end
69
end
70
end
71
72
# Returns state for module readiness.
73
# @return :String detailed sentence form description of result evaluation.
74
def description
75
if ready_for_test?
76
"ready for testing"
77
elsif @missing.empty? && @invalid.empty?
78
# TODO? confirm vuln match in this class
79
"has matching reference"
80
else
81
if missing_message.empty? || invalid_message.empty?
82
missing_message + invalid_message
83
else
84
[missing_message, invalid_message].join(', ')
85
end
86
end
87
end
88
89
def match?
90
!@missing.include? :os_match
91
end
92
93
def ready_for_test?
94
@prerequisites_evaluated && @missing.empty? && @invalid.empty?
95
end
96
97
private
98
99
def determine_likely_compatibility
100
if matches_host_os?
101
@datastore['rhost'] = @host.address
102
else
103
@missing << :os_match
104
end
105
106
if @mod.post_auth?
107
unless @mod.default_cred? || has_service_cred? || has_datastore_cred?
108
@missing << :credential
109
end
110
end
111
end
112
113
def determine_prerequisites
114
mod_detail = @framework.modules.create(@mod.fullname)
115
if mod_detail.nil?
116
@required << :module_not_loadable
117
return
118
end
119
@mod = mod_detail
120
121
if @mod.respond_to?(:session_types) && @mod.session_types
122
@required << :session
123
124
if s = @host.sessions.alive.detect { |sess| matches_session?(sess) }
125
@datastore['session'] = s.local_id.to_s
126
else
127
@missing << :session
128
end
129
end
130
131
@mod.options.each_pair do |name, opt|
132
@required << name if opt.required? && !opt.default.nil?
133
end
134
135
@datastore.each_pair do |k, v|
136
@mod.datastore[k] = v
137
end
138
139
target_idx = @mod.respond_to?(:auto_targeted_index) ? @mod.auto_targeted_index(@host) : nil
140
if target_idx
141
@datastore['target'] = target_idx
142
@mod.datastore['target'] = target_idx
143
end
144
145
# Must come after the target so we know we match the target we want.
146
# TODO: feed available payloads into target selection
147
if @wanted_payloads
148
if p = @wanted_payloads.find { |p| @mod.is_payload_compatible?(p) }
149
@datastore['payload'] = p
150
else
151
@missing << :payload_match
152
end
153
end
154
155
@mod.validate
156
rescue Msf::OptionValidateError => e
157
unset_options = []
158
bad_options = []
159
160
e.options.each do |opt|
161
if @mod.datastore[opt].nil?
162
unset_options << opt
163
else
164
bad_options << opt
165
end
166
end
167
168
@missing.concat unset_options
169
@invalid.concat bad_options
170
ensure
171
@prerequisites_evaluated = true
172
end
173
174
def matches_session?(session)
175
session.stype == 'meterpreter' || !!@mod.session_types&.include?(session.stype)
176
end
177
178
def required_sessions_list
179
return "meterpreter" unless @mod.session_types&.any?
180
181
@mod.session_types.join(' or ')
182
end
183
184
def has_service_cred?
185
@available_creds&.any?
186
end
187
188
def has_datastore_cred?
189
!!(@datastore['username'] && @datastore['password'])
190
end
191
192
# Determines if an exploit (mod, an instantiated module) is suitable for the host (host)
193
# defined operating system. Returns true if the host.os isn't defined, if the module's target
194
# OS isn't defined, if the module's OS is "unix" and the host's OS is not "windows," or if
195
# the module's target is "php", "python", or "java." Or, of course, in the event the host.os
196
# actually matches. This is a fail-open gate; if there's a doubt, assume the module will work
197
# on this target.
198
def matches_host_os?
199
hos = @host.os_name&.downcase
200
return true if hos.nil? || hos.empty?
201
202
set = @mod.platform.split(',').map{ |x| x.downcase }
203
return true if set.empty?
204
205
# Special cases
206
if set.include?('unix')
207
# Skip archaic old HPUX bugs if we have a solid match against another OS
208
return false if set.include?("hpux") && mod.refname.include?("hpux") && !hos.include?("hpux")
209
# Skip AIX bugs if we have a solid match against another OS
210
return false if set.include?("aix") && mod.refname.include?("aix") && !hos.include?("aix")
211
# Skip IRIX bugs if we have a solid match against another OS
212
return false if set.include?("irix") && mod.refname.include?("irix") && !hos.include?("irix")
213
214
return true if !hos.include?('windows')
215
end
216
217
return true if set.include?("php")
218
return true if set.include?("python")
219
return true if set.include?("java")
220
221
set.each do |mos|
222
return true if hos.include?(mos)
223
end
224
225
false
226
end
227
228
def missing_message
229
@missing.map do |m|
230
case m
231
when :module_not_loadable
232
"module not loadable"
233
when :os_match
234
"operating system does not match"
235
when :session, "SESSION"
236
"open #{required_sessions_list} session required"
237
when :credential
238
"credentials are required"
239
when :payload_match
240
"none of the requested payloads match"
241
when String
242
"option #{m.inspect} needs to be set"
243
end
244
end.uniq.join(', ')
245
end
246
247
def invalid_message
248
@invalid.map do |o|
249
case o
250
when String
251
"option #{o.inspect} is currently invalid"
252
end
253
end.join(', ')
254
end
255
256
=begin
257
# Tests for various service conditions by comparing the module's fullname (which
258
# is basically a pathname) to the intended target service record. The service.info
259
# column is tested against a regex in most/all cases and "false" is returned in the
260
# event of a match between an incompatible module and service fingerprint.
261
# TODO: fix and integrate
262
def exploit_filter_by_service(mod, serv)
263
264
# Filter out Unix vs Windows exploits for SMB services
265
return true if (mod.fullname =~ /\/samba/ and serv.info.to_s =~ /windows/i)
266
return true if (mod.fullname =~ /\/windows/ and serv.info.to_s =~ /samba|unix|vxworks|qnx|netware/i)
267
return true if (mod.fullname =~ /\/netware/ and serv.info.to_s =~ /samba|unix|vxworks|qnx/i)
268
269
# Filter out IIS exploits for non-Microsoft services
270
return true if (mod.fullname =~ /\/iis\/|\/isapi\// and (serv.info.to_s !~ /microsoft|asp/i))
271
272
# Filter out Apache exploits for non-Apache services
273
return true if (mod.fullname =~ /\/apache/ and serv.info.to_s !~ /apache|ibm/i)
274
275
false
276
end
277
=end
278
end
279
280