Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/module_option_tab_completion.rb
19535 views
1
module Msf
2
module Ui
3
module Console
4
###
5
#
6
# Module-specific tab completion helper.
7
#
8
###
9
module ModuleOptionTabCompletion
10
#
11
# Tab completion for datastore names
12
#
13
# @param datastore [Msf::DataStore]
14
# @param _str [String] the string currently being typed before tab was hit
15
# @param _words [Array<String>] the previously completed words on the command
16
# line. `_words` is always at least 1 when tab completion has reached this
17
# stage since the command itself has been completed.
18
def tab_complete_datastore_names(datastore, _str, _words)
19
keys = (
20
Msf::DataStore::GLOBAL_KEYS +
21
datastore.keys
22
)
23
keys.concat(datastore.options.values.flat_map(&:fallbacks)) if datastore.is_a?(Msf::DataStore)
24
keys.uniq! { |key| key.downcase }
25
keys
26
end
27
28
#
29
# Tab completion for a module's datastore names
30
#
31
# @param mod [Msf::Module]
32
# @param str [String] the string currently being typed before tab was hit
33
# @param words [Array<String>] the previously completed words on the command
34
# line. `words` is always at least 1 when tab completion has reached this
35
# stage since the command itself has been completed.
36
def tab_complete_module_datastore_names(mod, str, words)
37
datastore = mod ? mod.datastore : framework.datastore
38
keys = tab_complete_datastore_names(datastore, str, words)
39
40
if mod
41
keys = keys.delete_if do |name|
42
!(mod_opt = mod.options[name]).nil? && !Msf::OptCondition.show_option(mod, mod_opt)
43
end
44
end
45
keys
46
end
47
48
#
49
# Tab completion options values
50
#
51
def tab_complete_option(mod, str, words)
52
if str.end_with?('=')
53
option_name = str.chop
54
option_value = ''
55
56
::Readline.completion_append_character = ' '
57
return tab_complete_option_values(mod, option_value, words, opt: option_name).map { |value| "#{str}#{value}" }
58
elsif str.include?('=')
59
str_split = str.split('=')
60
option_name = str_split[0].strip
61
option_value = str_split[1].strip
62
63
::Readline.completion_append_character = ' '
64
return tab_complete_option_values(mod, option_value, words, opt: option_name).map { |value| "#{option_name}=#{value}" }
65
end
66
67
::Readline.completion_append_character = ''
68
tab_complete_option_names(mod, str, words).map { |name| "#{name}=" }
69
end
70
71
#
72
# Provide tab completion for name values
73
#
74
def tab_complete_option_names(mod, str, words)
75
res = tab_complete_module_datastore_names(mod, str, words) || [ ]
76
77
if !mod
78
return res
79
end
80
81
mod.options.each do |e|
82
name, _opt = e
83
res << name
84
end
85
# Exploits provide these three default options
86
if mod.exploit?
87
res << 'PAYLOAD'
88
res << 'NOP'
89
res << 'TARGET'
90
res << 'ENCODER'
91
elsif mod.evasion?
92
res << 'PAYLOAD'
93
res << 'TARGET'
94
res << 'ENCODER'
95
elsif mod.payload?
96
res << 'ENCODER'
97
end
98
if mod.is_a?(Msf::Module::HasActions)
99
res << 'ACTION'
100
end
101
if ((mod.exploit? || mod.evasion?) && mod.datastore['PAYLOAD'])
102
p = framework.payloads.create(mod.datastore['PAYLOAD'])
103
if p
104
p.options.each do |e|
105
name, _opt = e
106
res << name
107
end
108
end
109
end
110
unless str.blank?
111
res = res.select { |term| term.upcase.start_with?(str.upcase) }
112
res = res.map do |term|
113
if str == str.upcase
114
str + term[str.length..-1].upcase
115
elsif str == str.downcase
116
str + term[str.length..-1].downcase
117
else
118
str + term[str.length..-1]
119
end
120
end
121
end
122
123
return res.sort
124
end
125
126
#
127
# Provide tab completion for option values
128
#
129
def tab_complete_option_values(mod, str, words, opt:)
130
if words.last.casecmp?('SessionTlvLogging')
131
return %w[console true false file:<file>]
132
end
133
134
res = []
135
# With no module, we have nothing to complete
136
if !mod
137
return res
138
end
139
140
# Well-known option names specific to exploits
141
if mod.exploit?
142
return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'
143
return option_values_targets(mod) if opt.upcase == 'TARGET'
144
return option_values_nops if opt.upcase == 'NOPS'
145
return option_values_encoders if opt.upcase == 'STAGEENCODER'
146
elsif mod.evasion?
147
return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'
148
return option_values_targets(mod) if opt.upcase == 'TARGET'
149
end
150
# Well-known option names specific to modules with actions
151
if mod.is_a?(Msf::Module::HasActions)
152
return option_values_actions(mod) if opt.upcase == 'ACTION'
153
end
154
# The ENCODER option works for evasions, payloads and exploits
155
if ((mod.evasion? || mod.exploit? || mod.payload?) && (opt.upcase == 'ENCODER'))
156
return option_values_encoders
157
end
158
159
# Well-known option names specific to post-exploitation
160
if (mod.post? || mod.exploit?)
161
return option_values_sessions(mod) if opt.upcase == 'SESSION'
162
end
163
# Is this option used by the active module?
164
mod.options.each_key do |key|
165
if key.downcase == opt.downcase
166
res.concat(option_values_dispatch(mod, mod.options[key], str, words))
167
end
168
end
169
# How about the selected payload?
170
if ((mod.evasion? || mod.exploit?) && mod.datastore['PAYLOAD'])
171
if p = framework.payloads.create(mod.datastore['PAYLOAD'])
172
p.options.each_key do |key|
173
res.concat(option_values_dispatch(mod, p.options[key], str, words)) if key.downcase == opt.downcase
174
end
175
end
176
end
177
return res
178
end
179
180
#
181
# Provide possible option values based on type
182
#
183
def option_values_dispatch(mod, o, str, words)
184
res = []
185
res << o.default.to_s if o.default
186
case o
187
when Msf::OptAddress
188
case o.name.upcase
189
when 'RHOST'
190
option_values_target_addrs(mod).each do |addr|
191
res << addr
192
end
193
when 'LHOST', 'SRVHOST', 'REVERSELISTENERBINDADDRESS'
194
rh = mod.datastore['RHOST'] || framework.datastore['RHOST']
195
if rh && !rh.empty?
196
res << Rex::Socket.source_address(rh)
197
else
198
res += tab_complete_source_address
199
res += tab_complete_source_interface(o)
200
end
201
end
202
when Msf::OptAddressRange, Msf::OptRhosts
203
case str
204
when /^file:(.*)/
205
files = tab_complete_filenames(Regexp.last_match(1), words)
206
res += files.map { |f| 'file:' + f } if files
207
when %r{^(.*)/\d{0,2}$}
208
left = Regexp.last_match(1)
209
if Rex::Socket.is_ipv4?(left)
210
res << left + '/32'
211
res << left + '/24'
212
res << left + '/16'
213
end
214
when /^(.*)\-$/
215
left = Regexp.last_match(1)
216
if Rex::Socket.is_ipv4?(left)
217
res << str + str[0, str.length - 1]
218
end
219
else
220
option_values_target_addrs(mod).each do |addr|
221
res << addr
222
end
223
end
224
when Msf::OptPort
225
case o.name.upcase
226
when 'RPORT'
227
option_values_target_ports(mod).each do |port|
228
res << port
229
end
230
end
231
when Msf::OptEnum
232
o.enums.each do |val|
233
res << val
234
end
235
when Msf::OptPath
236
files = tab_complete_filenames(str, words)
237
res += files if files
238
when Msf::OptBool
239
res << 'true'
240
res << 'false'
241
when Msf::OptString
242
if (str =~ /^file:(.*)/)
243
files = tab_complete_filenames(Regexp.last_match(1), words)
244
res += files.map { |f| 'file:' + f } if files
245
end
246
end
247
return res
248
end
249
250
# XXX: We repurpose OptAddressLocal#interfaces, so we can't put this in Rex
251
def tab_complete_source_interface(o)
252
return [] unless o.is_a?(Msf::OptAddressLocal)
253
254
o.interfaces
255
end
256
257
#
258
# Provide valid payload options for the current exploit
259
#
260
def option_values_payloads(mod)
261
if @cache_payloads && mod == @previous_module && mod.target == @previous_target
262
return @cache_payloads
263
end
264
265
@previous_module = mod
266
@previous_target = mod.target
267
@cache_payloads = mod.compatible_payloads.map do |refname, _payload|
268
refname
269
end
270
@cache_payloads
271
end
272
273
#
274
# Provide valid session options for the current post-exploit module
275
#
276
def option_values_sessions(mod)
277
if mod.respond_to?(:compatible_sessions)
278
mod.compatible_sessions.map { |sid| sid.to_s }
279
end
280
end
281
282
#
283
# Provide valid target options for the current exploit
284
#
285
def option_values_targets(mod)
286
res = []
287
if mod.targets
288
1.upto(mod.targets.length) { |i| res << (i - 1).to_s }
289
res += mod.targets.map(&:name)
290
end
291
return res
292
end
293
294
#
295
# Provide valid action options for the current module
296
#
297
def option_values_actions(mod)
298
res = []
299
if mod.actions
300
mod.actions.each { |i| res << i.name }
301
end
302
return res
303
end
304
305
#
306
# Provide valid nops options for the current exploit
307
#
308
def option_values_nops
309
framework.nops.module_refnames
310
end
311
312
#
313
# Provide valid encoders options for the current exploit or payload
314
#
315
def option_values_encoders
316
framework.encoders.module_refnames
317
end
318
319
#
320
# Provide the target addresses
321
#
322
def option_values_target_addrs(mod)
323
res = [ ]
324
res << Rex::Socket.source_address
325
return res if !framework.db.active
326
327
# List only those hosts with matching open ports?
328
mport = mod.datastore['RPORT']
329
if mport
330
mport = mport.to_i
331
hosts = {}
332
framework.db.services.each do |service|
333
if service.port == mport
334
hosts[service.host.address] = true
335
end
336
end
337
hosts.keys.each do |host|
338
res << host
339
end
340
# List all hosts in the database
341
else
342
framework.db.hosts.each do |host|
343
res << host.address
344
end
345
end
346
return res
347
end
348
349
#
350
# Provide the target ports
351
#
352
def option_values_target_ports(mod)
353
return [] unless framework.db.active
354
return [] if mod.datastore['RHOST'].nil?
355
356
host_addresses = mod.datastore['RHOST'].split.map do |addr|
357
address, _scope = addr.split('%', 2)
358
address
359
end
360
361
hosts = framework.db.hosts({:address => host_addresses, :workspace => framework.db.workspace})
362
return [] if hosts.empty?
363
364
res = []
365
hosts.each do |host|
366
host.services.each do |service|
367
res << service.port.to_s
368
end
369
end
370
371
res.uniq
372
end
373
end
374
end
375
end
376
end
377
378