Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/msf/ui/console/command_dispatcher/modules.rb
19850 views
1
# -*- coding: binary -*-
2
3
4
module Msf
5
module Ui
6
module Console
7
module CommandDispatcher
8
9
#
10
# {CommandDispatcher} for commands related to background jobs in Metasploit Framework.
11
#
12
class Modules
13
14
include Msf::Ui::Console::CommandDispatcher
15
include Msf::Ui::Console::CommandDispatcher::Common
16
17
include Rex::Text::Color
18
19
@@search_opts = Rex::Parser::Arguments.new(
20
['-h', '--help'] => [false, 'Help banner'],
21
['-I', '--ignore'] => [false, 'Ignore the command if the only match has the same name as the search'],
22
['-o', '--output'] => [true, 'Send output to a file in csv format', '<filename>'],
23
['-S', '--filter'] => [true, 'Regex pattern used to filter search results', '<filter>'],
24
['-u', '--use'] => [false, 'Use module if there is one result'],
25
['-s', '--sort-ascending'] => [true, 'Sort search results by the specified column in ascending order', '<column>'],
26
['-r', '--sort-descending'] => [true, 'Reverse the order of search results to descending order', '<column>']
27
)
28
29
@@favorite_opts = Rex::Parser::Arguments.new(
30
'-h' => [false, 'Help banner'],
31
'-c' => [false, 'Clear the contents of the favorite modules file'],
32
'-d' => [false, 'Delete module(s) or the current active module from the favorite modules file'],
33
'-l' => [false, 'Print the list of favorite modules (alias for `show favorites`)']
34
)
35
36
@@favorites_opts = Rex::Parser::Arguments.new(
37
'-h' => [false, 'Help banner']
38
)
39
40
def commands
41
{
42
"back" => "Move back from the current context",
43
"advanced" => "Displays advanced options for one or more modules",
44
"info" => "Displays information about one or more modules",
45
"options" => "Displays global options or for one or more modules",
46
"loadpath" => "Searches for and loads modules from a path",
47
"popm" => "Pops the latest module off the stack and makes it active",
48
"pushm" => "Pushes the active or list of modules onto the module stack",
49
"listm" => "List the module stack",
50
"clearm" => "Clear the module stack",
51
"previous" => "Sets the previously loaded module as the current module",
52
"reload_all" => "Reloads all modules from all defined module paths",
53
"search" => "Searches module names and descriptions",
54
"show" => "Displays modules of a given type, or all modules",
55
"use" => "Interact with a module by name or search term/index",
56
"favorite" => "Add module(s) to the list of favorite modules",
57
"favorites" => "Print the list of favorite modules (alias for `show favorites`)"
58
}
59
end
60
61
#
62
# Initializes the datastore cache
63
#
64
def initialize(driver)
65
super
66
67
@dscache = {}
68
@previous_module = nil
69
@module_name_stack = []
70
# Array of individual modules that have been searched for
71
@module_search_results = []
72
# Module search results, with additional metadata on what to do if the module is interacted with
73
@module_search_results_with_usage_metadata = []
74
@@payload_show_results = []
75
@dangerzone_map = nil
76
end
77
78
#
79
# Returns the name of the command dispatcher.
80
#
81
def name
82
"Module"
83
end
84
85
def cmd_advanced_help
86
print_line 'Usage: advanced [mod1 mod2 ...]'
87
print_line
88
print_line 'Queries the supplied module or modules for advanced options. If no module is given,'
89
print_line 'show advanced options for the currently active module.'
90
print_line
91
end
92
93
def cmd_advanced(*args)
94
if args.empty?
95
if (active_module)
96
show_advanced_options(active_module)
97
return true
98
else
99
print_error('No module active')
100
return false
101
end
102
end
103
104
args.each { |name|
105
mod = framework.modules.create(name)
106
107
if (mod == nil)
108
print_error("Invalid module: #{name}")
109
else
110
show_advanced_options(mod)
111
end
112
}
113
end
114
115
def cmd_info_help
116
print_line "Usage: info <module name> [mod2 mod3 ...]"
117
print_line
118
print_line "Options:"
119
print_line "* The flag '-j' will print the data in json format"
120
print_line "* The flag '-d' will show the markdown version with a browser. More info, but could be slow."
121
print_line "Queries the supplied module or modules for information. If no module is given,"
122
print_line "show info for the currently active module."
123
print_line
124
end
125
126
def print_module_info(mod, dump_json: false, show_doc: false)
127
if dump_json
128
print(Serializer::Json.dump_module(mod) + "\n")
129
elsif show_doc
130
f = Tempfile.new(["#{mod.shortname}_doc", '.html'])
131
begin
132
print_status("Generating documentation for #{mod.shortname}, then opening #{f.path} in a browser...")
133
Msf::Util::DocumentGenerator.spawn_module_document(mod, f)
134
ensure
135
f.close if f
136
end
137
else
138
print(Serializer::ReadableText.dump_module(mod))
139
print("\nView the full module info with the #{Msf::Ui::Tip.highlight('info -d')} command.\n\n")
140
end
141
end
142
143
# Handles the index selection formatting
144
def print_module_search_results_usage
145
last_mod_with_usage_metadata = @module_search_results_with_usage_metadata.last
146
index_usage = "use #{@module_search_results_with_usage_metadata.length - 1}"
147
index_info = "info #{@module_search_results_with_usage_metadata.length - 1}"
148
name_usage = "use #{last_mod_with_usage_metadata[:mod].fullname}"
149
150
additional_usage_message = ""
151
additional_usage_example = (last_mod_with_usage_metadata[:datastore] || {}).first
152
if framework.features.enabled?(Msf::FeatureManager::HIERARCHICAL_SEARCH_TABLE) && additional_usage_example
153
key, value = additional_usage_example
154
additional_usage_message = "\nAfter interacting with a module you can manually set a #{key} with %grnset #{key} '#{value}'%clr"
155
end
156
print("Interact with a module by name or index. For example %grn#{index_info}%clr, %grn#{index_usage}%clr or %grn#{name_usage}%clr#{additional_usage_message}\n\n")
157
end
158
159
#
160
# Displays information about one or more module.
161
#
162
def cmd_info(*args)
163
dump_json = false
164
show_doc = false
165
166
if args.include?('-j')
167
args.delete('-j')
168
dump_json = true
169
end
170
171
if args.include?('-d')
172
args.delete('-d')
173
show_doc = true
174
end
175
176
if (args.length == 0)
177
if active_module
178
print_module_info(active_module, dump_json: dump_json, show_doc: show_doc)
179
return true
180
else
181
cmd_info_help
182
return false
183
end
184
elsif args.include? '-h'
185
cmd_info_help
186
return false
187
end
188
189
args.each do |arg|
190
mod_name = arg
191
192
additional_datastore_values = nil
193
194
# Use a module by search index
195
index_from_list(@module_search_results_with_usage_metadata, mod_name) do |result|
196
mod = result&.[](:mod)
197
next unless mod && mod.respond_to?(:fullname)
198
199
# Module cache object
200
mod_name = mod.fullname
201
additional_datastore_values = result[:datastore]
202
end
203
204
# Ensure we have a reference name and not a path
205
name = trim_path(mod_name, 'modules')
206
207
# Creates an instance of the module
208
mod = framework.modules.create(name)
209
210
# If any additional datastore values were provided, set these values
211
mod.datastore.update(additional_datastore_values) unless additional_datastore_values.nil?
212
213
if mod.nil?
214
print_error("Invalid module: #{name}")
215
else
216
print_module_info(mod, dump_json: dump_json, show_doc: show_doc)
217
end
218
end
219
end
220
221
def cmd_options_help
222
print_line 'Usage: options [mod1 mod2 ...]'
223
print_line
224
print_line 'Queries the supplied module or modules for options. If no module is given,'
225
print_line 'show options for the currently active module.'
226
print_line
227
end
228
229
def cmd_options(*args)
230
if args.empty?
231
if (active_module)
232
show_options(active_module)
233
return true
234
else
235
show_global_options
236
return true
237
end
238
end
239
240
args.each do |name|
241
mod = framework.modules.create(name)
242
243
if (mod == nil)
244
print_error("Invalid module: #{name}")
245
else
246
show_options(mod)
247
end
248
end
249
end
250
251
#
252
# Tab completion for the advanced command (same as use)
253
#
254
# @param str (see #cmd_use_tabs)
255
# @param words (see #cmd_use_tabs)
256
257
def cmd_advanced_tabs(str, words)
258
cmd_use_tabs(str, words)
259
end
260
261
#
262
# Tab completion for the advanced command (same as use)
263
#
264
# @param str (see #cmd_use_tabs)
265
# @param words (see #cmd_use_tabs)
266
267
def cmd_info_tabs(str, words)
268
cmd_use_tabs(str, words)
269
end
270
271
#
272
# Tab completion for the advanced command (same as use)
273
#
274
# @param str (see #cmd_use_tabs)
275
# @param words (see #cmd_use_tabs)
276
277
def cmd_options_tabs(str, words)
278
cmd_use_tabs(str, words)
279
end
280
281
def cmd_loadpath_help
282
print_line "Usage: loadpath </path/to/modules>"
283
print_line
284
print_line "Loads modules from the given directory which should contain subdirectories for"
285
print_line "module types, e.g. /path/to/modules/exploits"
286
print_line
287
end
288
289
#
290
# Adds one or more search paths.
291
#
292
def cmd_loadpath(*args)
293
if (args.length == 0 or args.include? "-h")
294
cmd_loadpath_help
295
return true
296
end
297
298
totals = {}
299
overall = 0
300
curr_path = nil
301
302
begin
303
# Walk the list of supplied search paths attempting to add each one
304
# along the way
305
args.each { |path|
306
curr_path = path
307
308
# Load modules, but do not consult the cache
309
if (counts = framework.modules.add_module_path(path))
310
counts.each_pair { |type, count|
311
totals[type] = (totals[type]) ? (totals[type] + count) : count
312
313
overall += count
314
}
315
end
316
}
317
rescue NameError, RuntimeError
318
log_error("Failed to add search path #{curr_path}: #{$!}")
319
return true
320
end
321
322
added = "Loaded #{overall} modules:\n"
323
324
totals.sort_by { |type, _count| type }.each { |type, count|
325
added << " #{count} #{type} modules\n"
326
}
327
328
print(added)
329
end
330
331
#
332
# Tab completion for the loadpath command
333
#
334
# @param str [String] the string currently being typed before tab was hit
335
# @param words [Array<String>] the previously completed words on the command line. words is always
336
# at least 1 when tab completion has reached this stage since the command itself has been completed
337
338
def cmd_loadpath_tabs(str, words)
339
return [] if words.length > 1
340
341
# This custom completion might better than Readline's... We'll leave it for now.
342
#tab_complete_filenames(str,words)
343
344
paths = []
345
if (File.directory?(str))
346
paths = Dir.entries(str)
347
paths = paths.map { |f|
348
if File.directory? File.join(str,f)
349
File.join(str,f)
350
end
351
}
352
paths.delete_if { |f| f.nil? or File.basename(f) == '.' or File.basename(f) == '..' }
353
else
354
d = Dir.glob(str + "*").map { |f| f if File.directory?(f) }
355
d.delete_if { |f| f.nil? or f == '.' or f == '..' }
356
# If there's only one possibility, descend to the next level
357
if (1 == d.length)
358
paths = Dir.entries(d[0])
359
paths = paths.map { |f|
360
if File.directory? File.join(d[0],f)
361
File.join(d[0],f)
362
end
363
}
364
paths.delete_if { |f| f.nil? or File.basename(f) == '.' or File.basename(f) == '..' }
365
else
366
paths = d
367
end
368
end
369
paths.sort!
370
return paths
371
end
372
373
def cmd_search_help
374
print_line "Usage: search [<options>] [<keywords>:<value>]"
375
print_line
376
print_line "Prepending a value with '-' will exclude any matching results."
377
print_line "If no options or keywords are provided, cached results are displayed."
378
print_line
379
print @@search_opts.usage
380
print_line
381
print_line "Keywords:"
382
{
383
'action' => 'Modules with a matching action name or description',
384
'adapter' => 'Modules with a matching adapter reference name',
385
'aka' => 'Modules with a matching AKA (also-known-as) name',
386
'arch' => 'Modules affecting this architecture',
387
'att&ck' => 'Modules with a matching MITRE ATT&CK ID or reference',
388
'author' => 'Modules written by this author',
389
'bid' => 'Modules with a matching Bugtraq ID',
390
'check' => 'Modules that support the \'check\' method',
391
'cve' => 'Modules with a matching CVE ID',
392
'date' => 'Modules with a matching disclosure date',
393
'description' => 'Modules with a matching description',
394
'edb' => 'Modules with a matching Exploit-DB ID',
395
'fullname' => 'Modules with a matching full name',
396
'mod_time' => 'Modules with a matching modification date',
397
'name' => 'Modules with a matching descriptive name',
398
'osvdb' => 'Modules with a matching OSVDB ID',
399
'path' => 'Modules with a matching path',
400
'platform' => 'Modules affecting this platform',
401
'port' => 'Modules with a matching port',
402
'rank' => 'Modules with a matching rank (Can be descriptive (ex: \'good\') or numeric with comparison operators (ex: \'gte400\'))',
403
'ref' => 'Modules with a matching ref',
404
'reference' => 'Modules with a matching reference',
405
'session_type' => 'Modules with a matching session type (SMB, MySQL, Meterpreter, etc)',
406
'stage' => 'Modules with a matching stage reference name',
407
'stager' => 'Modules with a matching stager reference name',
408
'target' => 'Modules affecting this target',
409
'type' => 'Modules of a specific type (exploit, payload, auxiliary, encoder, evasion, post, or nop)',
410
}.each_pair do |keyword, description|
411
print_line " #{keyword.ljust 17}: #{description}"
412
end
413
print_line
414
print_line "Supported search columns:"
415
{
416
'rank' => 'Sort modules by their exploitability rank',
417
'date' => 'Sort modules by their disclosure date. Alias for disclosure_date',
418
'disclosure_date' => 'Sort modules by their disclosure date',
419
'name' => 'Sort modules by their name',
420
'type' => 'Sort modules by their type',
421
'check' => 'Sort modules by whether or not they have a check method',
422
'action' => 'Sort modules by whether or not they have actions',
423
}.each_pair do |keyword, description|
424
print_line " #{keyword.ljust 17}: #{description}"
425
end
426
print_line
427
print_line "Examples:"
428
print_line " search cve:2009 type:exploit"
429
print_line " search cve:2009 type:exploit platform:-linux"
430
print_line " search cve:2009 -s name"
431
print_line " search type:exploit -s type -r"
432
print_line " search att&ck:T1059"
433
print_line
434
end
435
436
#
437
# Searches modules for specific keywords
438
#
439
def cmd_search(*args)
440
match = ''
441
row_filter = nil
442
output_file = nil
443
cached = false
444
use = false
445
count = -1
446
search_terms = []
447
sort_attribute = 'name'
448
valid_sort_attributes = ['action', 'rank','disclosure_date','name','date','type','check']
449
reverse_sort = false
450
ignore_use_exact_match = false
451
452
@@search_opts.parse(args) do |opt, idx, val|
453
case opt
454
when '-S'
455
row_filter = val
456
when '-h'
457
cmd_search_help
458
return false
459
when '-o'
460
output_file = val
461
when '-u'
462
use = true
463
when '-I'
464
ignore_use_exact_match = true
465
when '-s'
466
sort_attribute = val
467
when '-r'
468
reverse_sort = true
469
else
470
match += val + ' '
471
end
472
end
473
474
if args.empty?
475
if @module_search_results_with_usage_metadata.empty?
476
cmd_search_help
477
return false
478
end
479
480
cached = true
481
end
482
483
if sort_attribute && !valid_sort_attributes.include?(sort_attribute)
484
print_error("Supported options for the -s flag are: #{valid_sort_attributes}")
485
return false
486
end
487
488
begin
489
if cached
490
print_status('Displaying cached results')
491
else
492
search_params = Msf::Modules::Metadata::Search.parse_search_string(match)
493
@module_search_results = Msf::Modules::Metadata::Cache.instance.find(search_params)
494
495
@module_search_results.sort_by! do |module_metadata|
496
if sort_attribute == 'action'
497
module_metadata.actions&.any? ? 0 : 1
498
elsif sort_attribute == 'check'
499
module_metadata.check ? 0 : 1
500
elsif sort_attribute == 'disclosure_date' || sort_attribute == 'date'
501
# Not all modules have disclosure_date, i.e. multi/handler
502
module_metadata.disclosure_date || Time.utc(0)
503
else
504
module_metadata.send(sort_attribute)
505
end
506
end
507
508
if reverse_sort
509
@module_search_results.reverse!
510
end
511
end
512
513
if @module_search_results.empty?
514
print_error('No results from search')
515
return false
516
end
517
518
if ignore_use_exact_match && @module_search_results.length == 1 &&
519
@module_search_results.first.fullname == match.strip
520
return false
521
end
522
523
if !search_params.nil? && !search_params['text'].nil?
524
search_params['text'][0].each do |t|
525
search_terms << t
526
end
527
end
528
529
# Generate the table used to display matches
530
tbl = generate_module_table('Matching Modules', search_terms, row_filter)
531
532
@module_search_results_with_usage_metadata = []
533
@module_search_results.each do |m|
534
@module_search_results_with_usage_metadata << { mod: m }
535
count += 1
536
tbl << [
537
count,
538
"#{m.fullname}",
539
m.disclosure_date.nil? ? '' : m.disclosure_date.strftime("%Y-%m-%d"),
540
m.rank,
541
m.check ? 'Yes' : 'No',
542
m.name,
543
]
544
545
if framework.features.enabled?(Msf::FeatureManager::HIERARCHICAL_SEARCH_TABLE)
546
total_children_rows = (m.actions&.length || 0) + (m.targets&.length || 0) + (m.notes&.[]('AKA')&.length || 0)
547
show_child_items = total_children_rows > 1
548
next unless show_child_items
549
550
indent = " \\_ "
551
# Note: We still use visual indicators for blank values as it's easier to read
552
# We can't always use a generic formatter/styler, as it would be applied to the 'parent' rows too
553
blank_value = '.'
554
if (m.actions&.length || 0) > 1
555
m.actions.each do |action|
556
@module_search_results_with_usage_metadata << { mod: m, datastore: { 'ACTION' => action['name'] } }
557
count += 1
558
tbl << [
559
count,
560
"#{indent}action: #{action['name']}",
561
blank_value,
562
blank_value,
563
blank_value,
564
action['description'],
565
]
566
end
567
end
568
569
if (m.targets&.length || 0) > 1
570
m.targets.each do |target|
571
@module_search_results_with_usage_metadata << { mod: m, datastore: { 'TARGET' => target } }
572
count += 1
573
tbl << [
574
count,
575
"#{indent}target: #{target}",
576
blank_value,
577
blank_value,
578
blank_value,
579
blank_value
580
]
581
end
582
end
583
584
if (m.notes&.[]('AKA')&.length || 0) > 1
585
m.notes['AKA'].each do |aka|
586
@module_search_results_with_usage_metadata << { mod: m }
587
count += 1
588
tbl << [
589
count,
590
"#{indent}AKA: #{aka}",
591
blank_value,
592
blank_value,
593
blank_value,
594
blank_value
595
]
596
end
597
end
598
end
599
end
600
rescue ArgumentError
601
print_error("Invalid argument(s)\n")
602
cmd_search_help
603
return false
604
end
605
606
if output_file
607
print_status("Wrote search results to #{output_file}")
608
::File.open(output_file, "wb") { |ofd|
609
ofd.write(tbl.to_csv)
610
}
611
return true
612
end
613
614
print_line(tbl.to_s)
615
print_module_search_results_usage
616
617
if @module_search_results.length == 1 && use
618
used_module = @module_search_results_with_usage_metadata.first[:mod].fullname
619
print_status("Using #{used_module}") if used_module
620
cmd_use(used_module, true)
621
end
622
623
true
624
end
625
626
#
627
# Tab completion for the search command
628
#
629
# @param str [String] the string currently being typed before tab was hit
630
# @param words [Array<String>] the previously completed words on the command line. words is always
631
# at least 1 when tab completion has reached this stage since the command itself has been completed
632
633
def cmd_search_tabs(str, words)
634
if words.length == 1
635
return @@search_opts.option_keys
636
end
637
638
[]
639
end
640
641
def cmd_show_help
642
global_opts = %w{all encoders nops exploits payloads auxiliary post plugins info options favorites}
643
print_status("Valid parameters for the \"show\" command are: #{global_opts.join(", ")}")
644
645
module_opts = %w{ missing advanced evasion targets actions }
646
print_status("Additional module-specific parameters are: #{module_opts.join(", ")}")
647
end
648
649
#
650
# Displays the list of modules based on their type, or all modules if
651
# no type is provided.
652
#
653
def cmd_show(*args)
654
if args.empty?
655
print_error("Argument required\n")
656
cmd_show_help
657
return
658
end
659
660
mod = self.active_module
661
662
args.each { |type|
663
case type
664
when '-h'
665
cmd_show_help
666
when 'all'
667
show_encoders
668
show_nops
669
show_exploits
670
show_payloads
671
show_auxiliary
672
show_post
673
show_plugins
674
when 'encoders'
675
show_encoders
676
when 'nops'
677
show_nops
678
when 'exploits'
679
show_exploits
680
when 'payloads'
681
show_payloads
682
when 'auxiliary'
683
show_auxiliary
684
when 'post'
685
show_post
686
when 'favorites'
687
show_favorites
688
when 'info'
689
cmd_info(*args[1, args.length])
690
when 'options'
691
if (mod)
692
show_options(mod)
693
else
694
show_global_options
695
end
696
when 'missing'
697
if (mod)
698
show_missing(mod)
699
else
700
print_error("No module selected.")
701
end
702
when 'advanced'
703
if (mod)
704
show_advanced_options(mod)
705
else
706
print_error("No module selected.")
707
end
708
when 'evasion'
709
if (mod)
710
show_evasion_options(mod)
711
else
712
show_evasion
713
end
714
when 'sessions'
715
if (active_module and active_module.respond_to?(:compatible_sessions))
716
sessions = active_module.compatible_sessions
717
else
718
sessions = framework.sessions.keys.sort
719
end
720
print_line
721
print(Serializer::ReadableText.dump_sessions(framework, :session_ids => sessions))
722
print_line
723
when "plugins"
724
show_plugins
725
when "targets"
726
if (mod and (mod.exploit? or mod.evasion?))
727
show_targets(mod)
728
else
729
print_error("No exploit module selected.")
730
end
731
when "actions"
732
if mod && mod.kind_of?(Msf::Module::HasActions)
733
show_actions(mod)
734
else
735
print_error("No module with actions selected.")
736
end
737
738
else
739
print_error("Invalid parameter \"#{type}\", use \"show -h\" for more information")
740
end
741
}
742
end
743
744
#
745
# Tab completion for the show command
746
#
747
# @param str [String] the string currently being typed before tab was hit
748
# @param words [Array<String>] the previously completed words on the command line. words is always
749
# at least 1 when tab completion has reached this stage since the command itself has been completed
750
751
def cmd_show_tabs(str, words)
752
return [] if words.length > 1
753
754
res = %w{all encoders nops exploits payloads auxiliary post plugins options favorites}
755
if (active_module)
756
res.concat %w{missing advanced evasion targets actions info}
757
if (active_module.respond_to? :compatible_sessions)
758
res << "sessions"
759
end
760
end
761
return res
762
end
763
764
def cmd_use_help
765
print_line 'Usage: use <name|term|index>'
766
print_line
767
print_line 'Interact with a module by name or search term/index.'
768
print_line 'If a module name is not found, it will be treated as a search term.'
769
print_line 'An index from the previous search results can be selected if desired.'
770
print_line
771
print_line 'Examples:'
772
print_line ' use exploit/windows/smb/ms17_010_eternalblue'
773
print_line
774
print_line ' use eternalblue'
775
print_line ' use <name|index>'
776
print_line
777
print_line ' search eternalblue'
778
print_line ' use <name|index>'
779
print_line
780
print_april_fools_module_use
781
end
782
783
#
784
# Uses a module.
785
#
786
def cmd_use(*args)
787
if args.length == 0 || args.first == '-h'
788
cmd_use_help
789
return false
790
end
791
792
# Divert logic for dangerzone mode
793
args = dangerzone_codename_to_module(args)
794
795
# Try to create an instance of the supplied module name
796
mod_name = args[0]
797
798
additional_datastore_values = nil
799
800
# Use a module by search index
801
index_from_list(@module_search_results_with_usage_metadata, mod_name) do |result|
802
mod = result&.[](:mod)
803
unless mod && mod.respond_to?(:fullname)
804
print_error("Invalid module index: #{mod_name}")
805
return false
806
end
807
808
# Module cache object from @module_search_results_with_usage_metadata
809
mod_name = mod.fullname
810
additional_datastore_values = result[:datastore]
811
end
812
813
# See if the supplied module name has already been resolved
814
mod_resolved = args[1] == true ? true : false
815
816
# Ensure we have a reference name and not a path
817
mod_name = trim_path(mod_name, "modules")
818
819
begin
820
mod = framework.modules.create(mod_name)
821
822
unless mod
823
# Checks to see if we have any load_errors for the current module.
824
# and if so, returns them to the user.
825
load_error = framework.modules.load_error_by_name(mod_name)
826
if load_error
827
print_error("Failed to load module: #{load_error}")
828
return false
829
end
830
unless mod_resolved
831
elog("Module #{mod_name} not found, and no loading errors found. If you're using a custom module" \
832
' refer to our wiki: https://docs.metasploit.com/docs/using-metasploit/intermediate/running-private-modules.html')
833
834
# Avoid trying to use the search result if it exactly matches
835
# the module we were trying to load. The module cannot be
836
# loaded and searching isn't going to change that.
837
mods_found = cmd_search('-I', '-u', *args)
838
end
839
840
unless mods_found
841
print_error("Failed to load module: #{mod_name}")
842
return false
843
end
844
end
845
rescue Rex::AmbiguousArgumentError => info
846
print_error(info.to_s)
847
rescue NameError => info
848
log_error("The supplied module name is ambiguous: #{$!}.")
849
end
850
851
return false if (mod == nil)
852
853
# Enstack the command dispatcher for this module type
854
dispatcher = nil
855
856
case mod.type
857
when Msf::MODULE_ENCODER
858
dispatcher = Msf::Ui::Console::CommandDispatcher::Encoder
859
when Msf::MODULE_EXPLOIT
860
dispatcher = Msf::Ui::Console::CommandDispatcher::Exploit
861
when Msf::MODULE_NOP
862
dispatcher = Msf::Ui::Console::CommandDispatcher::Nop
863
when Msf::MODULE_PAYLOAD
864
dispatcher = Msf::Ui::Console::CommandDispatcher::Payload
865
when Msf::MODULE_AUX
866
dispatcher = Msf::Ui::Console::CommandDispatcher::Auxiliary
867
when Msf::MODULE_POST
868
dispatcher = Msf::Ui::Console::CommandDispatcher::Post
869
when Msf::MODULE_EVASION
870
dispatcher = Msf::Ui::Console::CommandDispatcher::Evasion
871
else
872
print_error("Unsupported module type: #{mod.type}")
873
return false
874
end
875
876
# If there's currently an active module, enqueque it and go back
877
if (active_module)
878
@previous_module = active_module
879
cmd_back()
880
end
881
882
if (dispatcher != nil)
883
driver.enstack_dispatcher(dispatcher)
884
end
885
886
# Update the active module
887
self.active_module = mod
888
889
# If a datastore cache exists for this module, then load it up
890
if @dscache[active_module.fullname]
891
active_module.datastore.update(@dscache[active_module.fullname])
892
end
893
894
# If any additional datastore values were provided, set these values
895
unless additional_datastore_values.nil? || additional_datastore_values.empty?
896
mod.datastore.update(additional_datastore_values)
897
print_status("Additionally setting #{additional_datastore_values.map { |k,v| "#{k} => #{v}" }.join(", ")}")
898
if additional_datastore_values['TARGET'] && (mod.exploit? || mod.evasion?)
899
mod.import_target_defaults
900
end
901
end
902
903
# Choose a default payload when the module is used, not run
904
if mod.datastore['PAYLOAD']
905
print_status("Using configured payload #{mod.datastore['PAYLOAD']}")
906
elsif dispatcher.respond_to?(:choose_payload)
907
chosen_payload = dispatcher.choose_payload(mod)
908
print_status("No payload configured, defaulting to #{chosen_payload}") if chosen_payload
909
end
910
911
if framework.features.enabled?(Msf::FeatureManager::DISPLAY_MODULE_ACTION) && mod.respond_to?(:actions) && mod.actions.size > 1
912
print_status "Using action %grn#{mod.action.name}%clr - view all #{mod.actions.size} actions with the %grnshow actions%clr command"
913
end
914
915
mod.init_ui(driver.input, driver.output)
916
end
917
918
#
919
# Command to take to the previously active module
920
#
921
def cmd_previous(*args)
922
if @previous_module
923
self.cmd_use(@previous_module.fullname)
924
else
925
print_error("There isn't a previous module at the moment")
926
end
927
end
928
929
#
930
# Help for the 'previous' command
931
#
932
def cmd_previous_help
933
print_line "Usage: previous"
934
print_line
935
print_line "Set the previously loaded module as the current module"
936
print_line
937
print_line "Previous module: #{@previous_module ? @previous_module.fullname : 'none'}"
938
print_line
939
end
940
941
#
942
# Command to enqueque a module on the module stack
943
#
944
def cmd_pushm(*args)
945
# could check if each argument is a valid module, but for now let them hang themselves
946
if args.count > 0
947
args.each do |arg|
948
@module_name_stack.push(arg)
949
# Note new modules are appended to the array and are only module (full)names
950
end
951
else #then just push the active module
952
if active_module
953
#print_status "Pushing the active module"
954
@module_name_stack.push(active_module.fullname)
955
else
956
print_error("There isn't an active module and you didn't specify a module to push")
957
return self.cmd_pushm_help
958
end
959
end
960
end
961
962
#
963
# Tab completion for the pushm command
964
#
965
# @param str [String] the string currently being typed before tab was hit
966
# @param words [Array<String>] the previously completed words on the command line. words is always
967
# at least 1 when tab completion has reached this stage since the command itself has been completed
968
969
def cmd_pushm_tabs(str, words)
970
tab_complete_module(str, words)
971
end
972
973
#
974
# Help for the 'pushm' command
975
#
976
def cmd_pushm_help
977
print_line "Usage: pushm [module1 [,module2, module3...]]"
978
print_line
979
print_line "push current active module or specified modules onto the module stack"
980
print_line
981
end
982
983
#
984
# Command to dequeque a module from the module stack
985
#
986
def cmd_popm(*args)
987
if (args.count > 1 or not args[0].respond_to?("to_i"))
988
return self.cmd_popm_help
989
elsif args.count == 1
990
# then pop 'n' items off the stack, but don't change the active module
991
if args[0].to_i >= @module_name_stack.count
992
# in case they pass in a number >= the length of @module_name_stack
993
@module_name_stack = []
994
print_status("The module stack is empty")
995
else
996
@module_name_stack.pop(args[0].to_i)
997
end
998
else #then just pop the array and make that the active module
999
pop = @module_name_stack.pop
1000
if pop
1001
return self.cmd_use(pop)
1002
else
1003
print_error("There isn't anything to pop, the module stack is empty")
1004
end
1005
end
1006
end
1007
1008
#
1009
# Help for the 'popm' command
1010
#
1011
def cmd_popm_help
1012
print_line "Usage: popm [n]"
1013
print_line
1014
print_line "pop the latest module off of the module stack and make it the active module"
1015
print_line "or pop n modules off the stack, but don't change the active module"
1016
print_line
1017
end
1018
1019
def cmd_listm_help
1020
print_line 'Usage: listm'
1021
print_line
1022
print_line 'List the module stack'
1023
print_line
1024
end
1025
1026
def cmd_listm(*_args)
1027
if @module_name_stack.empty?
1028
print_error('The module stack is empty')
1029
return
1030
end
1031
1032
print_status("Module stack:\n")
1033
1034
@module_name_stack.to_enum.with_index.reverse_each do |name, idx|
1035
print_line("[#{idx}]\t#{name}")
1036
end
1037
end
1038
1039
def cmd_clearm_help
1040
print_line 'Usage: clearm'
1041
print_line
1042
print_line 'Clear the module stack'
1043
print_line
1044
end
1045
1046
def cmd_clearm(*_args)
1047
print_status('Clearing the module stack')
1048
@module_name_stack.clear
1049
end
1050
1051
#
1052
# Tab completion for the use command
1053
#
1054
# @param str [String] the string currently being typed before tab was hit
1055
# @param words [Array<String>] the previously completed words on the command line. words is always
1056
# at least 1 when tab completion has reached this stage since the command itself has been completed
1057
1058
def cmd_use_tabs(str, words)
1059
return [] if words.length > 1
1060
1061
tab_complete_module(str, words)
1062
end
1063
1064
def cmd_reload_all_help
1065
print_line "Usage: reload_all"
1066
print_line
1067
print_line "Reload all modules from all configured module paths. This may take awhile."
1068
print_line "See also: loadpath"
1069
print_line
1070
end
1071
1072
#
1073
# Reload all module paths that we are aware of
1074
#
1075
def cmd_reload_all(*args)
1076
if args.length > 0
1077
cmd_reload_all_help
1078
return
1079
end
1080
1081
print_status("Reloading modules from all module paths...")
1082
framework.modules.reload_modules
1083
1084
log_msg = "Please see #{File.join(Msf::Config.log_directory, 'framework.log')} for details."
1085
1086
# Check for modules that failed to load
1087
if framework.modules.module_load_error_by_path.length > 0
1088
wlog("WARNING! The following modules could not be loaded!")
1089
1090
framework.modules.module_load_error_by_path.each do |path, _error|
1091
wlog("\t#{path}")
1092
end
1093
1094
wlog(log_msg)
1095
end
1096
1097
if framework.modules.module_load_warnings.length > 0
1098
wlog("The following modules were loaded with warnings:")
1099
1100
framework.modules.module_load_warnings.each do |path, _error|
1101
wlog("\t#{path}")
1102
end
1103
1104
wlog(log_msg)
1105
end
1106
1107
self.driver.run_single('reload')
1108
self.driver.run_single("banner")
1109
end
1110
1111
def cmd_back_help
1112
print_line "Usage: back"
1113
print_line
1114
print_line "Return to the global dispatcher context"
1115
print_line
1116
end
1117
1118
#
1119
# Pop the current dispatcher stack context, assuming it isn't pointed at
1120
# the core or database backend stack context.
1121
#
1122
def cmd_back(*args)
1123
if (driver.dispatcher_stack.size > 1 and
1124
driver.current_dispatcher.name != 'Core' and
1125
driver.current_dispatcher.name != 'Database Backend')
1126
# Reset the active module if we have one
1127
if (active_module)
1128
1129
# Do NOT reset the UI anymore
1130
# active_module.reset_ui
1131
1132
# Save the module's datastore so that we can load it later
1133
# if the module is used again
1134
@dscache[active_module.fullname] = active_module.datastore.dup
1135
1136
self.active_module = nil
1137
end
1138
1139
# Destack the current dispatcher
1140
driver.destack_dispatcher
1141
end
1142
end
1143
1144
def cmd_favorite_help
1145
print_line 'Usage: favorite [mod1 mod2 ...]'
1146
print_line
1147
print_line "Add one or multiple modules to the list of favorite modules stored in #{Msf::Config.fav_modules_file}"
1148
print_line 'If no module name is specified, the command will add the active module if there is one'
1149
print @@favorite_opts.usage
1150
end
1151
1152
#
1153
# Helper method for cmd_favorite that writes modules to the fav_modules_file
1154
#
1155
def favorite_add(modules, favs_file)
1156
fav_limit = 50
1157
# obtain useful info about the fav_modules file
1158
exists, writable, readable, contents = favorite_check_fav_modules(favs_file)
1159
1160
# if the fav_modules file exists, check the file permissions
1161
if exists
1162
case
1163
when !writable
1164
print_error("Unable to save module(s) to the favorite modules file because it is not writable")
1165
return
1166
when !readable
1167
print_error("Unable to save module(s) to the favorite modules file because it is not readable")
1168
return
1169
end
1170
end
1171
1172
fav_count = 0
1173
if contents
1174
fav_count = contents.split.size
1175
end
1176
1177
modules = modules.uniq # prevent modules from being added more than once
1178
modules.each do |name|
1179
mod = framework.modules.create(name)
1180
if (mod == nil)
1181
print_error("Invalid module: #{name}")
1182
next
1183
end
1184
1185
if contents && contents.include?(mod.fullname)
1186
print_warning("Module #{mod.fullname} has already been favorited and will not be added to the favorite modules file")
1187
next
1188
end
1189
1190
if fav_count >= fav_limit
1191
print_error("Favorite module limit (#{fav_limit}) exceeded. No more modules will be added.")
1192
return
1193
end
1194
1195
File.open(favs_file, 'a+') { |file| file.puts(mod.fullname) }
1196
print_good("Added #{mod.fullname} to the favorite modules file")
1197
fav_count += 1
1198
end
1199
return
1200
end
1201
1202
#
1203
# Helper method for cmd_favorite that deletes modules from the fav_modules_file
1204
#
1205
def favorite_del(modules, delete_all, favs_file)
1206
# obtain useful info about the fav_modules file
1207
exists, writable, readable, contents = favorite_check_fav_modules(favs_file)
1208
1209
if delete_all
1210
custom_message = 'clear the contents of'
1211
else
1212
custom_message = 'delete module(s) from'
1213
end
1214
1215
case # error handling based on the existence / permissions of the fav_modules file
1216
when !exists
1217
print_warning("Unable to #{custom_message} the favorite modules file because it does not exist")
1218
return
1219
when !writable
1220
print_error("Unable to #{custom_message} the favorite modules file because it is not writable")
1221
return
1222
when !readable
1223
unless delete_all
1224
print_error("Unable to #{custom_message} the favorite modules file because it is not readable")
1225
return
1226
end
1227
when contents.empty?
1228
print_warning("Unable to #{custom_message} the favorite modules file because it is already empty")
1229
return
1230
end
1231
1232
if delete_all
1233
File.write(favs_file, '')
1234
print_good("Favorite modules file cleared")
1235
return
1236
end
1237
1238
modules = modules.uniq # prevent modules from being deleted more than once
1239
contents = contents.split
1240
modules.each do |name|
1241
mod = framework.modules.create(name)
1242
if (mod == nil)
1243
print_error("Invalid module: #{name}")
1244
next
1245
end
1246
1247
unless contents.include?(mod.fullname)
1248
print_warning("Module #{mod.fullname} cannot be deleted because it is not in the favorite modules file")
1249
next
1250
end
1251
1252
contents.delete(mod.fullname)
1253
print_status("Removing #{mod.fullname} from the favorite modules file")
1254
end
1255
1256
# clear the contents of the fav_modules file if removing the module(s) makes it empty
1257
if contents.length == 0
1258
File.write(favs_file, '')
1259
return
1260
end
1261
1262
File.open(favs_file, 'w') { |file| file.puts(contents.join("\n")) }
1263
end
1264
1265
#
1266
# Helper method for cmd_favorite that checks if the fav_modules file exists and is readable / writable
1267
#
1268
def favorite_check_fav_modules(favs_file)
1269
exists = false
1270
writable = false
1271
readable = false
1272
contents = ''
1273
1274
if File.exist?(favs_file)
1275
exists = true
1276
end
1277
1278
if File.writable?(favs_file)
1279
writable = true
1280
end
1281
1282
if File.readable?(favs_file)
1283
readable = true
1284
contents = File.read(favs_file)
1285
end
1286
1287
return exists, writable, readable, contents
1288
end
1289
1290
#
1291
# Add modules to or delete modules from the fav_modules file
1292
#
1293
def cmd_favorite(*args)
1294
valid_custom_args = ['-c', '-d', '-l']
1295
favs_file = Msf::Config.fav_modules_file
1296
1297
# always display the help banner if -h is provided or if multiple options are provided
1298
if args.include?('-h') || args.select{ |arg| arg if valid_custom_args.include?(arg) }.length > 1
1299
cmd_favorite_help
1300
return
1301
end
1302
1303
# if no arguments were provided, check if there is an active module to add
1304
if args.empty?
1305
unless active_module
1306
print_error('No module has been provided to favorite.')
1307
cmd_favorite_help
1308
return
1309
end
1310
1311
args = [active_module.fullname]
1312
favorite_add(args, favs_file)
1313
return
1314
end
1315
1316
case args[0]
1317
when '-c'
1318
args.delete('-c')
1319
unless args.empty?
1320
print_error('Option `-c` does not support arguments.')
1321
cmd_favorite_help
1322
return
1323
end
1324
1325
favorite_del(args, true, favs_file)
1326
when '-d'
1327
args.delete('-d')
1328
if args.empty?
1329
unless active_module
1330
print_error('No module has been provided to delete.')
1331
cmd_favorite_help
1332
return
1333
end
1334
1335
args = [active_module.fullname]
1336
end
1337
1338
favorite_del(args, false, favs_file)
1339
when '-l'
1340
args.delete('-l')
1341
unless args.empty?
1342
print_error('Option `-l` does not support arguments.')
1343
cmd_favorite_help
1344
return
1345
end
1346
cmd_show('favorites')
1347
else # no valid options, but there are arguments
1348
if args[0].start_with?('-')
1349
print_error('Invalid option provided')
1350
cmd_favorite_help
1351
return
1352
end
1353
1354
favorite_add(args, favs_file)
1355
end
1356
end
1357
1358
def cmd_favorites_help
1359
print_line 'Usage: favorites'
1360
print_line
1361
print_line 'Print the list of favorite modules (alias for `show favorites`)'
1362
print_line 'You can use the %grnfavorite%clr command to add the current module to your favorites list'
1363
print @@favorites_opts.usage
1364
end
1365
1366
#
1367
# Print the list of favorite modules from the fav_modules file (alias for `show favorites`)
1368
#
1369
def cmd_favorites(*args)
1370
if args.empty?
1371
cmd_show('favorites')
1372
return
1373
end
1374
1375
# always display the help banner if the command is called with arguments
1376
unless args.include?('-h')
1377
print_error('Invalid option(s) provided')
1378
end
1379
1380
cmd_favorites_help
1381
end
1382
1383
#
1384
# Tab complete module names
1385
#
1386
def tab_complete_module(str, words)
1387
res = []
1388
module_metadata = Msf::Modules::Metadata::Cache.instance.get_metadata
1389
module_metadata.each do |m|
1390
res << "#{m.type}/#{m.ref_name}"
1391
end
1392
framework.modules.module_types.each do |mtyp|
1393
mset = framework.modules.module_names(mtyp)
1394
mset.each do |mref|
1395
res << mtyp + '/' + mref
1396
end
1397
end
1398
1399
return dangerzone_modules_to_codenames(res.sort) if dangerzone_active?
1400
return res.uniq.sort
1401
end
1402
1403
def print_april_fools_module_use
1404
return unless ENV['APRILFOOLSMODULEUSE'] || Time.now.strftime("%m%d") == "0401"
1405
1406
banner = Msf::Ui::Banner.readfile('help-using-a-module.txt')
1407
print_line("%grn#{banner}%clr")
1408
end
1409
1410
#
1411
# Convert squirrel names back to regular module names
1412
#
1413
def dangerzone_codename_to_module(args)
1414
return args unless dangerzone_active? && args.length > 0 && args[0].length > 0
1415
return args unless args[0] =~ /^[A-Z]/
1416
args[0] = dangerzone_codename_to_module_name(args[0])
1417
args
1418
end
1419
1420
#
1421
# Determine if dangerzone mode is active via date or environment variable
1422
#
1423
def dangerzone_active?
1424
active = Time.now.strftime("%m%d") == "0401" || Rex::Compat.getenv('DANGERZONE').to_i > 0
1425
if active && @dangerzone_map.nil?
1426
dangerzone_build_map
1427
end
1428
active
1429
end
1430
1431
#
1432
# Convert module names to squirrel names
1433
#
1434
def dangerzone_modules_to_codenames(names)
1435
(names + @dangerzone_map.keys.grep(/^[A-Z]+/)).sort
1436
end
1437
1438
def dangerzone_codename_to_module_name(cname)
1439
@dangerzone_map[cname] || cname
1440
end
1441
1442
def dangerzone_module_name_to_codename(mname)
1443
@dangerzone_map[mname] || mname
1444
end
1445
1446
def dangerzone_build_map
1447
return unless @dangerzone_map.nil?
1448
1449
@dangerzone_map = {}
1450
1451
res = []
1452
%W{exploit auxiliary}.each do |mtyp|
1453
mset = framework.modules.module_names(mtyp)
1454
mset.each do |mref|
1455
res << mtyp + '/' + mref
1456
end
1457
end
1458
1459
words_a = ::File.readlines(::File.join(
1460
::Msf::Config.data_directory, "wordlists", "dangerzone_a.txt"
1461
)).map{|line| line.strip.upcase}
1462
1463
words_b = ::File.readlines(::File.join(
1464
::Msf::Config.data_directory, "wordlists", "dangerzone_b.txt"
1465
)).map{|line| line.strip.upcase}
1466
1467
aidx = -1
1468
bidx = -1
1469
1470
res.sort.each do |mname|
1471
word_a = words_a[ (aidx += 1) % words_a.length ]
1472
word_b = words_b[ (bidx += 1) % words_b.length ]
1473
cname = word_a + word_b
1474
1475
while @dangerzone_map[cname]
1476
aidx += 1
1477
word_a = words_a[ (aidx += 1) % words_a.length ]
1478
cname = word_a + word_b
1479
end
1480
1481
@dangerzone_map[mname] = cname
1482
@dangerzone_map[cname] = mname
1483
end
1484
end
1485
1486
#
1487
# Module list enumeration
1488
#
1489
1490
def show_encoders # :nodoc:
1491
# If an active module has been selected and it's an exploit, get the
1492
# list of compatible encoders and display them
1493
if (active_module and active_module.exploit? == true)
1494
show_module_metadata('Compatible Encoders', active_module.compatible_encoders)
1495
else
1496
show_module_metadata('Encoders', 'encoder')
1497
end
1498
end
1499
1500
def show_nops # :nodoc:
1501
show_module_metadata('NOP Generators', 'nop')
1502
end
1503
1504
def show_exploits # :nodoc:
1505
show_module_metadata('Exploits', 'exploit')
1506
end
1507
1508
def show_payloads # :nodoc:
1509
# If an active module has been selected and it's an exploit, get the
1510
# list of compatible payloads and display them
1511
if active_module && (active_module.exploit? || active_module.evasion?)
1512
@@payload_show_results = active_module.compatible_payloads
1513
1514
show_module_metadata('Compatible Payloads', @@payload_show_results)
1515
else
1516
# show_module_set(‘Payloads’, framework.payloads, regex, minrank, opts)
1517
show_module_metadata('Payloads', 'payload')
1518
end
1519
end
1520
1521
def show_auxiliary # :nodoc:
1522
show_module_metadata('Auxiliary','auxiliary')
1523
end
1524
1525
def show_post # :nodoc:
1526
show_module_metadata('Post','post')
1527
end
1528
1529
def show_evasion # :nodoc:
1530
show_module_metadata('Evasion','evasion')
1531
end
1532
1533
def show_favorites # :nodoc:
1534
favs_file = Msf::Config.fav_modules_file
1535
1536
unless File.exist?(favs_file)
1537
print_error("The favorite modules file does not exist")
1538
return
1539
end
1540
1541
if File.zero?(favs_file)
1542
print_warning("The favorite modules file is empty")
1543
return
1544
end
1545
1546
unless File.readable?(favs_file)
1547
print_error("Unable to read from #{favs_file}")
1548
return
1549
end
1550
1551
# create module set using the saved modules
1552
fav_modules = {}
1553
1554
# get the full module names from the favorites file and use then to search the MetaData Cache for matching modules
1555
saved_favs = File.readlines(favs_file).map(&:strip)
1556
saved_favs.each do |mod|
1557
# populate hash with module fullname and module object
1558
fav_modules[mod] = framework.modules[mod]
1559
end
1560
1561
fav_modules.each do |fullname, mod_obj|
1562
if mod_obj.nil?
1563
print_warning("#{favs_file} contains a module that can not be found - #{fullname}.")
1564
end
1565
end
1566
1567
# find cache module instance and add it to @module_search_results
1568
@module_search_results = Msf::Modules::Metadata::Cache.instance.find('fullname' => [saved_favs, []])
1569
1570
# This scenario is for when a module fullname is a substring of other module fullnames
1571
# Example, searching for the payload/windows/meterpreter/reverse_tcp module can result in matches for:
1572
# - windows/meterpreter/reverse_tcp_allports
1573
# - windows/meterpreter/reverse_tcp_dns
1574
# So if @module_search_results is greater than the amount of fav_modules, we need to filter the results to be more accurate
1575
if fav_modules.length < @module_search_results.length
1576
filtered_results = []
1577
fav_modules.each do |fullname, _mod_obj|
1578
filtered_results << @module_search_results.select do |search_result|
1579
search_result.fullname == fullname
1580
end
1581
end
1582
@module_search_results = filtered_results.flatten.sort_by(&:fullname)
1583
end
1584
@module_search_results_with_usage_metadata = @module_search_results.map { |mod| { mod: mod, datastore: {} } }
1585
1586
show_module_metadata('Favorites', fav_modules)
1587
print_module_search_results_usage
1588
end
1589
1590
def show_missing(mod) # :nodoc:
1591
mod_opt = Serializer::ReadableText.dump_options(mod, ' ', true)
1592
print("\nModule options (#{mod.fullname}):\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)
1593
1594
# If it's an exploit and a payload is defined, create it and
1595
# display the payload's options
1596
if (mod.exploit? and mod.datastore['PAYLOAD'])
1597
p = framework.payloads.create(mod.datastore['PAYLOAD'])
1598
1599
if (!p)
1600
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
1601
return
1602
end
1603
1604
p.share_datastore(mod.datastore)
1605
1606
if (p)
1607
p_opt = Serializer::ReadableText.dump_options(p, ' ', true)
1608
print("\nPayload options (#{mod.datastore['PAYLOAD']}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0)
1609
end
1610
end
1611
end
1612
1613
def show_global_options
1614
columns = [ 'Option', 'Current Setting', 'Description' ]
1615
tbl = Table.new(
1616
Table::Style::Default,
1617
'Header' => 'Global Options:',
1618
'Prefix' => "\n",
1619
'Postfix' => "\n",
1620
'Columns' => columns
1621
)
1622
[
1623
[ 'ConsoleLogging', framework.datastore['ConsoleLogging'] || "false", 'Log all console input and output' ],
1624
[ 'LogLevel', framework.datastore['LogLevel'] || "0", 'Verbosity of logs (default 0, max 3)' ],
1625
[ 'MinimumRank', framework.datastore['MinimumRank'] || "0", 'The minimum rank of exploits that will run without explicit confirmation' ],
1626
[ 'SessionLogging', framework.datastore['SessionLogging'] || "false", 'Log all input and output for sessions' ],
1627
[ 'SessionTlvLogging', framework.datastore['SessionTlvLogging'] || "false", 'Log all incoming and outgoing TLV packets' ],
1628
[ 'TimestampOutput', framework.datastore['TimestampOutput'] || "false", 'Prefix all console output with a timestamp' ],
1629
[ 'Prompt', framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt.to_s.gsub(/%.../,"") , "The prompt string" ],
1630
[ 'PromptChar', framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar.to_s.gsub(/%.../,""), "The prompt character" ],
1631
[ 'PromptTimeFormat', framework.datastore['PromptTimeFormat'] || Time::DATE_FORMATS[:db].to_s, 'Format for timestamp escapes in prompts' ],
1632
[ 'MeterpreterPrompt', framework.datastore['MeterpreterPrompt'] || '%undmeterpreter%clr', 'The meterpreter prompt string' ],
1633
].each { |r| tbl << r }
1634
1635
print(tbl.to_s)
1636
end
1637
1638
def show_targets(mod) # :nodoc:
1639
case mod
1640
when Msf::Exploit
1641
mod_targs = Serializer::ReadableText.dump_exploit_targets(mod, '', "\nExploit targets:")
1642
print("#{mod_targs}\n") if (mod_targs and mod_targs.length > 0)
1643
when Msf::Evasion
1644
mod_targs = Serializer::ReadableText.dump_evasion_targets(mod, '', "\nEvasion targets:")
1645
print("#{mod_targs}\n") if (mod_targs and mod_targs.length > 0)
1646
end
1647
end
1648
1649
def show_actions(mod) # :nodoc:
1650
mod_actions = Serializer::ReadableText.dump_module_actions(mod, ' ')
1651
print("\n#{mod.type.capitalize} actions:\n\n#{mod_actions}\n") if (mod_actions and mod_actions.length > 0)
1652
end
1653
1654
def show_advanced_options(mod) # :nodoc:
1655
mod_opt = Serializer::ReadableText.dump_advanced_options(mod, ' ')
1656
print("\nModule advanced options (#{mod.fullname}):\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)
1657
1658
# If it's an exploit and a payload is defined, create it and
1659
# display the payload's options
1660
if (mod.exploit? and mod.datastore['PAYLOAD'])
1661
p = framework.payloads.create(mod.datastore['PAYLOAD'])
1662
1663
if (!p)
1664
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
1665
return
1666
end
1667
1668
p.share_datastore(mod.datastore)
1669
1670
if (p)
1671
p_opt = Serializer::ReadableText.dump_advanced_options(p, ' ')
1672
print("\nPayload advanced options (#{mod.datastore['PAYLOAD']}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0)
1673
end
1674
end
1675
print("\nView the full module info with the #{Msf::Ui::Tip.highlight('info')}, or #{Msf::Ui::Tip.highlight('info -d')} command.\n\n")
1676
end
1677
1678
def show_evasion_options(mod) # :nodoc:
1679
mod_opt = Serializer::ReadableText.dump_evasion_options(mod, ' ')
1680
print("\nModule evasion options:\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)
1681
1682
# If it's an exploit and a payload is defined, create it and
1683
# display the payload's options
1684
if (mod.evasion? and mod.datastore['PAYLOAD'])
1685
p = framework.payloads.create(mod.datastore['PAYLOAD'])
1686
1687
if (!p)
1688
print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")
1689
return
1690
end
1691
1692
p.share_datastore(mod.datastore)
1693
1694
if (p)
1695
p_opt = Serializer::ReadableText.dump_evasion_options(p, ' ')
1696
print("\nPayload evasion options (#{mod.datastore['PAYLOAD']}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0)
1697
end
1698
end
1699
end
1700
1701
def show_plugins # :nodoc:
1702
tbl = Table.new(
1703
Table::Style::Default,
1704
'Header' => 'Loaded Plugins',
1705
'Prefix' => "\n",
1706
'Postfix' => "\n",
1707
'Columns' => [ 'Name', 'Description' ]
1708
)
1709
1710
framework.plugins.each { |plugin|
1711
tbl << [ plugin.name, plugin.desc ]
1712
}
1713
1714
# create an instance of core to call the list_plugins
1715
core = Msf::Ui::Console::CommandDispatcher::Core.new(driver)
1716
core.list_plugins
1717
print(tbl.to_s)
1718
end
1719
1720
# @param [table_name] used to name table
1721
# @param [module_filter] this will either be a modules fullname, or it will be an Array(show payloads/encoders)
1722
# or a Hash(show favorites) containing fullname
1723
# @param [compatible_mod] handles logic for if there is an active module when the
1724
# `show` command is run
1725
#
1726
# Handles the filtering of modules that will be generated into a table
1727
def show_module_metadata(table_name, module_filter)
1728
count = -1
1729
tbl = generate_module_table(table_name)
1730
1731
if module_filter.is_a?(Array) || module_filter.is_a?(Hash)
1732
module_filter.sort.each do |_mod_fullname, mod_obj|
1733
mod = nil
1734
1735
begin
1736
mod = mod_obj.new
1737
rescue ::Exception
1738
end
1739
next unless mod
1740
1741
count += 1
1742
tbl << add_record(mod, count, true)
1743
end
1744
else
1745
results = Msf::Modules::Metadata::Cache.instance.find(
1746
'type' => [[module_filter], []]
1747
)
1748
# Loop over each module and gather data
1749
results.each do |mod, _value|
1750
count += 1
1751
tbl << add_record(mod, count, false)
1752
end
1753
end
1754
print(tbl.to_s)
1755
end
1756
1757
# @param [mod] current module being passed in
1758
# @param [count] passes the count for each record
1759
# @param [compatible_mod] handles logic for if there is an active module when the
1760
# `show` command is run
1761
#
1762
# Adds a record for a table, also handle logic for whether the module is currently
1763
# handling compatible payloads/encoders
1764
def add_record(mod, count, compatible_mod)
1765
if compatible_mod
1766
check = mod.has_check? ? 'Yes' : 'No'
1767
else
1768
check = mod.check ? 'Yes' : 'No'
1769
end
1770
[
1771
count,
1772
mod.fullname,
1773
mod.disclosure_date.nil? ? '' : mod.disclosure_date.strftime('%Y-%m-%d'),
1774
mod.rank,
1775
check,
1776
mod.name
1777
]
1778
end
1779
1780
def generate_module_table(type, search_terms = [], row_filter = nil) # :nodoc:
1781
table_hierarchy_formatters = framework.features.enabled?(Msf::FeatureManager::HIERARCHICAL_SEARCH_TABLE) ? [Msf::Ui::Console::TablePrint::BlankFormatter.new] : []
1782
1783
Table.new(
1784
Table::Style::Default,
1785
'Header' => type,
1786
'Prefix' => "\n",
1787
'Postfix' => "\n",
1788
'SearchTerm' => row_filter,
1789
'SortIndex' => -1,
1790
# For now, don't perform any word wrapping on the search table as it breaks the workflow of
1791
# copying module names in conjunction with the `use <paste-buffer>` command
1792
'WordWrap' => false,
1793
'Columns' => [
1794
'#',
1795
'Name',
1796
'Disclosure Date',
1797
'Rank',
1798
'Check',
1799
'Description'
1800
],
1801
'ColProps' => {
1802
'Rank' => {
1803
'Formatters' => [
1804
*table_hierarchy_formatters,
1805
Msf::Ui::Console::TablePrint::RankFormatter.new
1806
],
1807
'Stylers' => [
1808
Msf::Ui::Console::TablePrint::RankStyler.new
1809
]
1810
},
1811
'Name' => {
1812
'Strip' => false,
1813
'Stylers' => [Msf::Ui::Console::TablePrint::HighlightSubstringStyler.new(search_terms)]
1814
},
1815
'Check' => {
1816
'Formatters' => [
1817
*table_hierarchy_formatters,
1818
]
1819
},
1820
'Disclosure Date' => {
1821
'Formatters' => [
1822
*table_hierarchy_formatters,
1823
]
1824
},
1825
'Description' => {
1826
'Stylers' => [
1827
Msf::Ui::Console::TablePrint::HighlightSubstringStyler.new(search_terms)
1828
]
1829
}
1830
}
1831
)
1832
end
1833
end
1834
end
1835
end
1836
end
1837
end
1838
1839