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/ui/console/module_option_tab_completion.rb
Views: 11783
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::DataStoreWithFallbacks::GLOBAL_KEYS +
21
datastore.keys
22
)
23
keys.concat(datastore.options.values.flat_map(&:fallbacks)) if datastore.is_a?(Msf::DataStoreWithFallbacks)
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.sorted.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.sorted.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
return res
123
end
124
125
#
126
# Provide tab completion for option values
127
#
128
def tab_complete_option_values(mod, str, words, opt:)
129
if words.last.casecmp?('SessionTlvLogging')
130
return %w[console true false file:<file>]
131
end
132
133
res = []
134
# With no module, we have nothing to complete
135
if !mod
136
return res
137
end
138
139
# Well-known option names specific to exploits
140
if mod.exploit?
141
return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'
142
return option_values_targets(mod) if opt.upcase == 'TARGET'
143
return option_values_nops if opt.upcase == 'NOPS'
144
return option_values_encoders if opt.upcase == 'STAGEENCODER'
145
elsif mod.evasion?
146
return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'
147
return option_values_targets(mod) if opt.upcase == 'TARGET'
148
end
149
# Well-known option names specific to modules with actions
150
if mod.is_a?(Msf::Module::HasActions)
151
return option_values_actions(mod) if opt.upcase == 'ACTION'
152
end
153
# The ENCODER option works for evasions, payloads and exploits
154
if ((mod.evasion? || mod.exploit? || mod.payload?) && (opt.upcase == 'ENCODER'))
155
return option_values_encoders
156
end
157
158
# Well-known option names specific to post-exploitation
159
if (mod.post? || mod.exploit?)
160
return option_values_sessions(mod) if opt.upcase == 'SESSION'
161
end
162
# Is this option used by the active module?
163
mod.options.each_key do |key|
164
if key.downcase == opt.downcase
165
res.concat(option_values_dispatch(mod, mod.options[key], str, words))
166
end
167
end
168
# How about the selected payload?
169
if ((mod.evasion? || mod.exploit?) && mod.datastore['PAYLOAD'])
170
if p = framework.payloads.create(mod.datastore['PAYLOAD'])
171
p.options.each_key do |key|
172
res.concat(option_values_dispatch(mod, p.options[key], str, words)) if key.downcase == opt.downcase
173
end
174
end
175
end
176
return res
177
end
178
179
#
180
# Provide possible option values based on type
181
#
182
def option_values_dispatch(mod, o, str, words)
183
res = []
184
res << o.default.to_s if o.default
185
case o
186
when Msf::OptAddress
187
case o.name.upcase
188
when 'RHOST'
189
option_values_target_addrs(mod).each do |addr|
190
res << addr
191
end
192
when 'LHOST', 'SRVHOST', 'REVERSELISTENERBINDADDRESS'
193
rh = mod.datastore['RHOST'] || framework.datastore['RHOST']
194
if rh && !rh.empty?
195
res << Rex::Socket.source_address(rh)
196
else
197
res += tab_complete_source_address
198
res += tab_complete_source_interface(o)
199
end
200
end
201
when Msf::OptAddressRange, Msf::OptRhosts
202
case str
203
when /^file:(.*)/
204
files = tab_complete_filenames(Regexp.last_match(1), words)
205
res += files.map { |f| 'file:' + f } if files
206
when %r{^(.*)/\d{0,2}$}
207
left = Regexp.last_match(1)
208
if Rex::Socket.is_ipv4?(left)
209
res << left + '/32'
210
res << left + '/24'
211
res << left + '/16'
212
end
213
when /^(.*)\-$/
214
left = Regexp.last_match(1)
215
if Rex::Socket.is_ipv4?(left)
216
res << str + str[0, str.length - 1]
217
end
218
else
219
option_values_target_addrs(mod).each do |addr|
220
res << addr
221
end
222
end
223
when Msf::OptPort
224
case o.name.upcase
225
when 'RPORT'
226
option_values_target_ports(mod).each do |port|
227
res << port
228
end
229
end
230
when Msf::OptEnum
231
o.enums.each do |val|
232
res << val
233
end
234
when Msf::OptPath
235
files = tab_complete_filenames(str, words)
236
res += files if files
237
when Msf::OptBool
238
res << 'true'
239
res << 'false'
240
when Msf::OptString
241
if (str =~ /^file:(.*)/)
242
files = tab_complete_filenames(Regexp.last_match(1), words)
243
res += files.map { |f| 'file:' + f } if files
244
end
245
end
246
return res
247
end
248
249
# XXX: We repurpose OptAddressLocal#interfaces, so we can't put this in Rex
250
def tab_complete_source_interface(o)
251
return [] unless o.is_a?(Msf::OptAddressLocal)
252
253
o.interfaces
254
end
255
256
#
257
# Provide valid payload options for the current exploit
258
#
259
def option_values_payloads(mod)
260
if @cache_payloads && mod == @previous_module && mod.target == @previous_target
261
return @cache_payloads
262
end
263
264
@previous_module = mod
265
@previous_target = mod.target
266
@cache_payloads = mod.compatible_payloads.map do |refname, _payload|
267
refname
268
end
269
@cache_payloads
270
end
271
272
#
273
# Provide valid session options for the current post-exploit module
274
#
275
def option_values_sessions(mod)
276
if mod.respond_to?(:compatible_sessions)
277
mod.compatible_sessions.map { |sid| sid.to_s }
278
end
279
end
280
281
#
282
# Provide valid target options for the current exploit
283
#
284
def option_values_targets(mod)
285
res = []
286
if mod.targets
287
1.upto(mod.targets.length) { |i| res << (i - 1).to_s }
288
res += mod.targets.map(&:name)
289
end
290
return res
291
end
292
293
#
294
# Provide valid action options for the current module
295
#
296
def option_values_actions(mod)
297
res = []
298
if mod.actions
299
mod.actions.each { |i| res << i.name }
300
end
301
return res
302
end
303
304
#
305
# Provide valid nops options for the current exploit
306
#
307
def option_values_nops
308
framework.nops.module_refnames
309
end
310
311
#
312
# Provide valid encoders options for the current exploit or payload
313
#
314
def option_values_encoders
315
framework.encoders.module_refnames
316
end
317
318
#
319
# Provide the target addresses
320
#
321
def option_values_target_addrs(mod)
322
res = [ ]
323
res << Rex::Socket.source_address
324
return res if !framework.db.active
325
326
# List only those hosts with matching open ports?
327
mport = mod.datastore['RPORT']
328
if mport
329
mport = mport.to_i
330
hosts = {}
331
framework.db.services.each do |service|
332
if service.port == mport
333
hosts[service.host.address] = true
334
end
335
end
336
hosts.keys.each do |host|
337
res << host
338
end
339
# List all hosts in the database
340
else
341
framework.db.hosts.each do |host|
342
res << host.address
343
end
344
end
345
return res
346
end
347
348
#
349
# Provide the target ports
350
#
351
def option_values_target_ports(mod)
352
return [] unless framework.db.active
353
return [] if mod.datastore['RHOST'].nil?
354
355
host_addresses = mod.datastore['RHOST'].split.map do |addr|
356
address, _scope = addr.split('%', 2)
357
address
358
end
359
360
hosts = framework.db.hosts({:address => host_addresses, :workspace => framework.db.workspace})
361
return [] if hosts.empty?
362
363
res = []
364
hosts.each do |host|
365
host.services.each do |service|
366
res << service.port.to_s
367
end
368
end
369
370
res.uniq
371
end
372
end
373
end
374
end
375
end
376
377