Path: blob/master/lib/msf/ui/console/command_dispatcher/modules.rb
19850 views
# -*- coding: binary -*-123module Msf4module Ui5module Console6module CommandDispatcher78#9# {CommandDispatcher} for commands related to background jobs in Metasploit Framework.10#11class Modules1213include Msf::Ui::Console::CommandDispatcher14include Msf::Ui::Console::CommandDispatcher::Common1516include Rex::Text::Color1718@@search_opts = Rex::Parser::Arguments.new(19['-h', '--help'] => [false, 'Help banner'],20['-I', '--ignore'] => [false, 'Ignore the command if the only match has the same name as the search'],21['-o', '--output'] => [true, 'Send output to a file in csv format', '<filename>'],22['-S', '--filter'] => [true, 'Regex pattern used to filter search results', '<filter>'],23['-u', '--use'] => [false, 'Use module if there is one result'],24['-s', '--sort-ascending'] => [true, 'Sort search results by the specified column in ascending order', '<column>'],25['-r', '--sort-descending'] => [true, 'Reverse the order of search results to descending order', '<column>']26)2728@@favorite_opts = Rex::Parser::Arguments.new(29'-h' => [false, 'Help banner'],30'-c' => [false, 'Clear the contents of the favorite modules file'],31'-d' => [false, 'Delete module(s) or the current active module from the favorite modules file'],32'-l' => [false, 'Print the list of favorite modules (alias for `show favorites`)']33)3435@@favorites_opts = Rex::Parser::Arguments.new(36'-h' => [false, 'Help banner']37)3839def commands40{41"back" => "Move back from the current context",42"advanced" => "Displays advanced options for one or more modules",43"info" => "Displays information about one or more modules",44"options" => "Displays global options or for one or more modules",45"loadpath" => "Searches for and loads modules from a path",46"popm" => "Pops the latest module off the stack and makes it active",47"pushm" => "Pushes the active or list of modules onto the module stack",48"listm" => "List the module stack",49"clearm" => "Clear the module stack",50"previous" => "Sets the previously loaded module as the current module",51"reload_all" => "Reloads all modules from all defined module paths",52"search" => "Searches module names and descriptions",53"show" => "Displays modules of a given type, or all modules",54"use" => "Interact with a module by name or search term/index",55"favorite" => "Add module(s) to the list of favorite modules",56"favorites" => "Print the list of favorite modules (alias for `show favorites`)"57}58end5960#61# Initializes the datastore cache62#63def initialize(driver)64super6566@dscache = {}67@previous_module = nil68@module_name_stack = []69# Array of individual modules that have been searched for70@module_search_results = []71# Module search results, with additional metadata on what to do if the module is interacted with72@module_search_results_with_usage_metadata = []73@@payload_show_results = []74@dangerzone_map = nil75end7677#78# Returns the name of the command dispatcher.79#80def name81"Module"82end8384def cmd_advanced_help85print_line 'Usage: advanced [mod1 mod2 ...]'86print_line87print_line 'Queries the supplied module or modules for advanced options. If no module is given,'88print_line 'show advanced options for the currently active module.'89print_line90end9192def cmd_advanced(*args)93if args.empty?94if (active_module)95show_advanced_options(active_module)96return true97else98print_error('No module active')99return false100end101end102103args.each { |name|104mod = framework.modules.create(name)105106if (mod == nil)107print_error("Invalid module: #{name}")108else109show_advanced_options(mod)110end111}112end113114def cmd_info_help115print_line "Usage: info <module name> [mod2 mod3 ...]"116print_line117print_line "Options:"118print_line "* The flag '-j' will print the data in json format"119print_line "* The flag '-d' will show the markdown version with a browser. More info, but could be slow."120print_line "Queries the supplied module or modules for information. If no module is given,"121print_line "show info for the currently active module."122print_line123end124125def print_module_info(mod, dump_json: false, show_doc: false)126if dump_json127print(Serializer::Json.dump_module(mod) + "\n")128elsif show_doc129f = Tempfile.new(["#{mod.shortname}_doc", '.html'])130begin131print_status("Generating documentation for #{mod.shortname}, then opening #{f.path} in a browser...")132Msf::Util::DocumentGenerator.spawn_module_document(mod, f)133ensure134f.close if f135end136else137print(Serializer::ReadableText.dump_module(mod))138print("\nView the full module info with the #{Msf::Ui::Tip.highlight('info -d')} command.\n\n")139end140end141142# Handles the index selection formatting143def print_module_search_results_usage144last_mod_with_usage_metadata = @module_search_results_with_usage_metadata.last145index_usage = "use #{@module_search_results_with_usage_metadata.length - 1}"146index_info = "info #{@module_search_results_with_usage_metadata.length - 1}"147name_usage = "use #{last_mod_with_usage_metadata[:mod].fullname}"148149additional_usage_message = ""150additional_usage_example = (last_mod_with_usage_metadata[:datastore] || {}).first151if framework.features.enabled?(Msf::FeatureManager::HIERARCHICAL_SEARCH_TABLE) && additional_usage_example152key, value = additional_usage_example153additional_usage_message = "\nAfter interacting with a module you can manually set a #{key} with %grnset #{key} '#{value}'%clr"154end155print("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")156end157158#159# Displays information about one or more module.160#161def cmd_info(*args)162dump_json = false163show_doc = false164165if args.include?('-j')166args.delete('-j')167dump_json = true168end169170if args.include?('-d')171args.delete('-d')172show_doc = true173end174175if (args.length == 0)176if active_module177print_module_info(active_module, dump_json: dump_json, show_doc: show_doc)178return true179else180cmd_info_help181return false182end183elsif args.include? '-h'184cmd_info_help185return false186end187188args.each do |arg|189mod_name = arg190191additional_datastore_values = nil192193# Use a module by search index194index_from_list(@module_search_results_with_usage_metadata, mod_name) do |result|195mod = result&.[](:mod)196next unless mod && mod.respond_to?(:fullname)197198# Module cache object199mod_name = mod.fullname200additional_datastore_values = result[:datastore]201end202203# Ensure we have a reference name and not a path204name = trim_path(mod_name, 'modules')205206# Creates an instance of the module207mod = framework.modules.create(name)208209# If any additional datastore values were provided, set these values210mod.datastore.update(additional_datastore_values) unless additional_datastore_values.nil?211212if mod.nil?213print_error("Invalid module: #{name}")214else215print_module_info(mod, dump_json: dump_json, show_doc: show_doc)216end217end218end219220def cmd_options_help221print_line 'Usage: options [mod1 mod2 ...]'222print_line223print_line 'Queries the supplied module or modules for options. If no module is given,'224print_line 'show options for the currently active module.'225print_line226end227228def cmd_options(*args)229if args.empty?230if (active_module)231show_options(active_module)232return true233else234show_global_options235return true236end237end238239args.each do |name|240mod = framework.modules.create(name)241242if (mod == nil)243print_error("Invalid module: #{name}")244else245show_options(mod)246end247end248end249250#251# Tab completion for the advanced command (same as use)252#253# @param str (see #cmd_use_tabs)254# @param words (see #cmd_use_tabs)255256def cmd_advanced_tabs(str, words)257cmd_use_tabs(str, words)258end259260#261# Tab completion for the advanced command (same as use)262#263# @param str (see #cmd_use_tabs)264# @param words (see #cmd_use_tabs)265266def cmd_info_tabs(str, words)267cmd_use_tabs(str, words)268end269270#271# Tab completion for the advanced command (same as use)272#273# @param str (see #cmd_use_tabs)274# @param words (see #cmd_use_tabs)275276def cmd_options_tabs(str, words)277cmd_use_tabs(str, words)278end279280def cmd_loadpath_help281print_line "Usage: loadpath </path/to/modules>"282print_line283print_line "Loads modules from the given directory which should contain subdirectories for"284print_line "module types, e.g. /path/to/modules/exploits"285print_line286end287288#289# Adds one or more search paths.290#291def cmd_loadpath(*args)292if (args.length == 0 or args.include? "-h")293cmd_loadpath_help294return true295end296297totals = {}298overall = 0299curr_path = nil300301begin302# Walk the list of supplied search paths attempting to add each one303# along the way304args.each { |path|305curr_path = path306307# Load modules, but do not consult the cache308if (counts = framework.modules.add_module_path(path))309counts.each_pair { |type, count|310totals[type] = (totals[type]) ? (totals[type] + count) : count311312overall += count313}314end315}316rescue NameError, RuntimeError317log_error("Failed to add search path #{curr_path}: #{$!}")318return true319end320321added = "Loaded #{overall} modules:\n"322323totals.sort_by { |type, _count| type }.each { |type, count|324added << " #{count} #{type} modules\n"325}326327print(added)328end329330#331# Tab completion for the loadpath command332#333# @param str [String] the string currently being typed before tab was hit334# @param words [Array<String>] the previously completed words on the command line. words is always335# at least 1 when tab completion has reached this stage since the command itself has been completed336337def cmd_loadpath_tabs(str, words)338return [] if words.length > 1339340# This custom completion might better than Readline's... We'll leave it for now.341#tab_complete_filenames(str,words)342343paths = []344if (File.directory?(str))345paths = Dir.entries(str)346paths = paths.map { |f|347if File.directory? File.join(str,f)348File.join(str,f)349end350}351paths.delete_if { |f| f.nil? or File.basename(f) == '.' or File.basename(f) == '..' }352else353d = Dir.glob(str + "*").map { |f| f if File.directory?(f) }354d.delete_if { |f| f.nil? or f == '.' or f == '..' }355# If there's only one possibility, descend to the next level356if (1 == d.length)357paths = Dir.entries(d[0])358paths = paths.map { |f|359if File.directory? File.join(d[0],f)360File.join(d[0],f)361end362}363paths.delete_if { |f| f.nil? or File.basename(f) == '.' or File.basename(f) == '..' }364else365paths = d366end367end368paths.sort!369return paths370end371372def cmd_search_help373print_line "Usage: search [<options>] [<keywords>:<value>]"374print_line375print_line "Prepending a value with '-' will exclude any matching results."376print_line "If no options or keywords are provided, cached results are displayed."377print_line378print @@search_opts.usage379print_line380print_line "Keywords:"381{382'action' => 'Modules with a matching action name or description',383'adapter' => 'Modules with a matching adapter reference name',384'aka' => 'Modules with a matching AKA (also-known-as) name',385'arch' => 'Modules affecting this architecture',386'att&ck' => 'Modules with a matching MITRE ATT&CK ID or reference',387'author' => 'Modules written by this author',388'bid' => 'Modules with a matching Bugtraq ID',389'check' => 'Modules that support the \'check\' method',390'cve' => 'Modules with a matching CVE ID',391'date' => 'Modules with a matching disclosure date',392'description' => 'Modules with a matching description',393'edb' => 'Modules with a matching Exploit-DB ID',394'fullname' => 'Modules with a matching full name',395'mod_time' => 'Modules with a matching modification date',396'name' => 'Modules with a matching descriptive name',397'osvdb' => 'Modules with a matching OSVDB ID',398'path' => 'Modules with a matching path',399'platform' => 'Modules affecting this platform',400'port' => 'Modules with a matching port',401'rank' => 'Modules with a matching rank (Can be descriptive (ex: \'good\') or numeric with comparison operators (ex: \'gte400\'))',402'ref' => 'Modules with a matching ref',403'reference' => 'Modules with a matching reference',404'session_type' => 'Modules with a matching session type (SMB, MySQL, Meterpreter, etc)',405'stage' => 'Modules with a matching stage reference name',406'stager' => 'Modules with a matching stager reference name',407'target' => 'Modules affecting this target',408'type' => 'Modules of a specific type (exploit, payload, auxiliary, encoder, evasion, post, or nop)',409}.each_pair do |keyword, description|410print_line " #{keyword.ljust 17}: #{description}"411end412print_line413print_line "Supported search columns:"414{415'rank' => 'Sort modules by their exploitability rank',416'date' => 'Sort modules by their disclosure date. Alias for disclosure_date',417'disclosure_date' => 'Sort modules by their disclosure date',418'name' => 'Sort modules by their name',419'type' => 'Sort modules by their type',420'check' => 'Sort modules by whether or not they have a check method',421'action' => 'Sort modules by whether or not they have actions',422}.each_pair do |keyword, description|423print_line " #{keyword.ljust 17}: #{description}"424end425print_line426print_line "Examples:"427print_line " search cve:2009 type:exploit"428print_line " search cve:2009 type:exploit platform:-linux"429print_line " search cve:2009 -s name"430print_line " search type:exploit -s type -r"431print_line " search att&ck:T1059"432print_line433end434435#436# Searches modules for specific keywords437#438def cmd_search(*args)439match = ''440row_filter = nil441output_file = nil442cached = false443use = false444count = -1445search_terms = []446sort_attribute = 'name'447valid_sort_attributes = ['action', 'rank','disclosure_date','name','date','type','check']448reverse_sort = false449ignore_use_exact_match = false450451@@search_opts.parse(args) do |opt, idx, val|452case opt453when '-S'454row_filter = val455when '-h'456cmd_search_help457return false458when '-o'459output_file = val460when '-u'461use = true462when '-I'463ignore_use_exact_match = true464when '-s'465sort_attribute = val466when '-r'467reverse_sort = true468else469match += val + ' '470end471end472473if args.empty?474if @module_search_results_with_usage_metadata.empty?475cmd_search_help476return false477end478479cached = true480end481482if sort_attribute && !valid_sort_attributes.include?(sort_attribute)483print_error("Supported options for the -s flag are: #{valid_sort_attributes}")484return false485end486487begin488if cached489print_status('Displaying cached results')490else491search_params = Msf::Modules::Metadata::Search.parse_search_string(match)492@module_search_results = Msf::Modules::Metadata::Cache.instance.find(search_params)493494@module_search_results.sort_by! do |module_metadata|495if sort_attribute == 'action'496module_metadata.actions&.any? ? 0 : 1497elsif sort_attribute == 'check'498module_metadata.check ? 0 : 1499elsif sort_attribute == 'disclosure_date' || sort_attribute == 'date'500# Not all modules have disclosure_date, i.e. multi/handler501module_metadata.disclosure_date || Time.utc(0)502else503module_metadata.send(sort_attribute)504end505end506507if reverse_sort508@module_search_results.reverse!509end510end511512if @module_search_results.empty?513print_error('No results from search')514return false515end516517if ignore_use_exact_match && @module_search_results.length == 1 &&518@module_search_results.first.fullname == match.strip519return false520end521522if !search_params.nil? && !search_params['text'].nil?523search_params['text'][0].each do |t|524search_terms << t525end526end527528# Generate the table used to display matches529tbl = generate_module_table('Matching Modules', search_terms, row_filter)530531@module_search_results_with_usage_metadata = []532@module_search_results.each do |m|533@module_search_results_with_usage_metadata << { mod: m }534count += 1535tbl << [536count,537"#{m.fullname}",538m.disclosure_date.nil? ? '' : m.disclosure_date.strftime("%Y-%m-%d"),539m.rank,540m.check ? 'Yes' : 'No',541m.name,542]543544if framework.features.enabled?(Msf::FeatureManager::HIERARCHICAL_SEARCH_TABLE)545total_children_rows = (m.actions&.length || 0) + (m.targets&.length || 0) + (m.notes&.[]('AKA')&.length || 0)546show_child_items = total_children_rows > 1547next unless show_child_items548549indent = " \\_ "550# Note: We still use visual indicators for blank values as it's easier to read551# We can't always use a generic formatter/styler, as it would be applied to the 'parent' rows too552blank_value = '.'553if (m.actions&.length || 0) > 1554m.actions.each do |action|555@module_search_results_with_usage_metadata << { mod: m, datastore: { 'ACTION' => action['name'] } }556count += 1557tbl << [558count,559"#{indent}action: #{action['name']}",560blank_value,561blank_value,562blank_value,563action['description'],564]565end566end567568if (m.targets&.length || 0) > 1569m.targets.each do |target|570@module_search_results_with_usage_metadata << { mod: m, datastore: { 'TARGET' => target } }571count += 1572tbl << [573count,574"#{indent}target: #{target}",575blank_value,576blank_value,577blank_value,578blank_value579]580end581end582583if (m.notes&.[]('AKA')&.length || 0) > 1584m.notes['AKA'].each do |aka|585@module_search_results_with_usage_metadata << { mod: m }586count += 1587tbl << [588count,589"#{indent}AKA: #{aka}",590blank_value,591blank_value,592blank_value,593blank_value594]595end596end597end598end599rescue ArgumentError600print_error("Invalid argument(s)\n")601cmd_search_help602return false603end604605if output_file606print_status("Wrote search results to #{output_file}")607::File.open(output_file, "wb") { |ofd|608ofd.write(tbl.to_csv)609}610return true611end612613print_line(tbl.to_s)614print_module_search_results_usage615616if @module_search_results.length == 1 && use617used_module = @module_search_results_with_usage_metadata.first[:mod].fullname618print_status("Using #{used_module}") if used_module619cmd_use(used_module, true)620end621622true623end624625#626# Tab completion for the search command627#628# @param str [String] the string currently being typed before tab was hit629# @param words [Array<String>] the previously completed words on the command line. words is always630# at least 1 when tab completion has reached this stage since the command itself has been completed631632def cmd_search_tabs(str, words)633if words.length == 1634return @@search_opts.option_keys635end636637[]638end639640def cmd_show_help641global_opts = %w{all encoders nops exploits payloads auxiliary post plugins info options favorites}642print_status("Valid parameters for the \"show\" command are: #{global_opts.join(", ")}")643644module_opts = %w{ missing advanced evasion targets actions }645print_status("Additional module-specific parameters are: #{module_opts.join(", ")}")646end647648#649# Displays the list of modules based on their type, or all modules if650# no type is provided.651#652def cmd_show(*args)653if args.empty?654print_error("Argument required\n")655cmd_show_help656return657end658659mod = self.active_module660661args.each { |type|662case type663when '-h'664cmd_show_help665when 'all'666show_encoders667show_nops668show_exploits669show_payloads670show_auxiliary671show_post672show_plugins673when 'encoders'674show_encoders675when 'nops'676show_nops677when 'exploits'678show_exploits679when 'payloads'680show_payloads681when 'auxiliary'682show_auxiliary683when 'post'684show_post685when 'favorites'686show_favorites687when 'info'688cmd_info(*args[1, args.length])689when 'options'690if (mod)691show_options(mod)692else693show_global_options694end695when 'missing'696if (mod)697show_missing(mod)698else699print_error("No module selected.")700end701when 'advanced'702if (mod)703show_advanced_options(mod)704else705print_error("No module selected.")706end707when 'evasion'708if (mod)709show_evasion_options(mod)710else711show_evasion712end713when 'sessions'714if (active_module and active_module.respond_to?(:compatible_sessions))715sessions = active_module.compatible_sessions716else717sessions = framework.sessions.keys.sort718end719print_line720print(Serializer::ReadableText.dump_sessions(framework, :session_ids => sessions))721print_line722when "plugins"723show_plugins724when "targets"725if (mod and (mod.exploit? or mod.evasion?))726show_targets(mod)727else728print_error("No exploit module selected.")729end730when "actions"731if mod && mod.kind_of?(Msf::Module::HasActions)732show_actions(mod)733else734print_error("No module with actions selected.")735end736737else738print_error("Invalid parameter \"#{type}\", use \"show -h\" for more information")739end740}741end742743#744# Tab completion for the show command745#746# @param str [String] the string currently being typed before tab was hit747# @param words [Array<String>] the previously completed words on the command line. words is always748# at least 1 when tab completion has reached this stage since the command itself has been completed749750def cmd_show_tabs(str, words)751return [] if words.length > 1752753res = %w{all encoders nops exploits payloads auxiliary post plugins options favorites}754if (active_module)755res.concat %w{missing advanced evasion targets actions info}756if (active_module.respond_to? :compatible_sessions)757res << "sessions"758end759end760return res761end762763def cmd_use_help764print_line 'Usage: use <name|term|index>'765print_line766print_line 'Interact with a module by name or search term/index.'767print_line 'If a module name is not found, it will be treated as a search term.'768print_line 'An index from the previous search results can be selected if desired.'769print_line770print_line 'Examples:'771print_line ' use exploit/windows/smb/ms17_010_eternalblue'772print_line773print_line ' use eternalblue'774print_line ' use <name|index>'775print_line776print_line ' search eternalblue'777print_line ' use <name|index>'778print_line779print_april_fools_module_use780end781782#783# Uses a module.784#785def cmd_use(*args)786if args.length == 0 || args.first == '-h'787cmd_use_help788return false789end790791# Divert logic for dangerzone mode792args = dangerzone_codename_to_module(args)793794# Try to create an instance of the supplied module name795mod_name = args[0]796797additional_datastore_values = nil798799# Use a module by search index800index_from_list(@module_search_results_with_usage_metadata, mod_name) do |result|801mod = result&.[](:mod)802unless mod && mod.respond_to?(:fullname)803print_error("Invalid module index: #{mod_name}")804return false805end806807# Module cache object from @module_search_results_with_usage_metadata808mod_name = mod.fullname809additional_datastore_values = result[:datastore]810end811812# See if the supplied module name has already been resolved813mod_resolved = args[1] == true ? true : false814815# Ensure we have a reference name and not a path816mod_name = trim_path(mod_name, "modules")817818begin819mod = framework.modules.create(mod_name)820821unless mod822# Checks to see if we have any load_errors for the current module.823# and if so, returns them to the user.824load_error = framework.modules.load_error_by_name(mod_name)825if load_error826print_error("Failed to load module: #{load_error}")827return false828end829unless mod_resolved830elog("Module #{mod_name} not found, and no loading errors found. If you're using a custom module" \831' refer to our wiki: https://docs.metasploit.com/docs/using-metasploit/intermediate/running-private-modules.html')832833# Avoid trying to use the search result if it exactly matches834# the module we were trying to load. The module cannot be835# loaded and searching isn't going to change that.836mods_found = cmd_search('-I', '-u', *args)837end838839unless mods_found840print_error("Failed to load module: #{mod_name}")841return false842end843end844rescue Rex::AmbiguousArgumentError => info845print_error(info.to_s)846rescue NameError => info847log_error("The supplied module name is ambiguous: #{$!}.")848end849850return false if (mod == nil)851852# Enstack the command dispatcher for this module type853dispatcher = nil854855case mod.type856when Msf::MODULE_ENCODER857dispatcher = Msf::Ui::Console::CommandDispatcher::Encoder858when Msf::MODULE_EXPLOIT859dispatcher = Msf::Ui::Console::CommandDispatcher::Exploit860when Msf::MODULE_NOP861dispatcher = Msf::Ui::Console::CommandDispatcher::Nop862when Msf::MODULE_PAYLOAD863dispatcher = Msf::Ui::Console::CommandDispatcher::Payload864when Msf::MODULE_AUX865dispatcher = Msf::Ui::Console::CommandDispatcher::Auxiliary866when Msf::MODULE_POST867dispatcher = Msf::Ui::Console::CommandDispatcher::Post868when Msf::MODULE_EVASION869dispatcher = Msf::Ui::Console::CommandDispatcher::Evasion870else871print_error("Unsupported module type: #{mod.type}")872return false873end874875# If there's currently an active module, enqueque it and go back876if (active_module)877@previous_module = active_module878cmd_back()879end880881if (dispatcher != nil)882driver.enstack_dispatcher(dispatcher)883end884885# Update the active module886self.active_module = mod887888# If a datastore cache exists for this module, then load it up889if @dscache[active_module.fullname]890active_module.datastore.update(@dscache[active_module.fullname])891end892893# If any additional datastore values were provided, set these values894unless additional_datastore_values.nil? || additional_datastore_values.empty?895mod.datastore.update(additional_datastore_values)896print_status("Additionally setting #{additional_datastore_values.map { |k,v| "#{k} => #{v}" }.join(", ")}")897if additional_datastore_values['TARGET'] && (mod.exploit? || mod.evasion?)898mod.import_target_defaults899end900end901902# Choose a default payload when the module is used, not run903if mod.datastore['PAYLOAD']904print_status("Using configured payload #{mod.datastore['PAYLOAD']}")905elsif dispatcher.respond_to?(:choose_payload)906chosen_payload = dispatcher.choose_payload(mod)907print_status("No payload configured, defaulting to #{chosen_payload}") if chosen_payload908end909910if framework.features.enabled?(Msf::FeatureManager::DISPLAY_MODULE_ACTION) && mod.respond_to?(:actions) && mod.actions.size > 1911print_status "Using action %grn#{mod.action.name}%clr - view all #{mod.actions.size} actions with the %grnshow actions%clr command"912end913914mod.init_ui(driver.input, driver.output)915end916917#918# Command to take to the previously active module919#920def cmd_previous(*args)921if @previous_module922self.cmd_use(@previous_module.fullname)923else924print_error("There isn't a previous module at the moment")925end926end927928#929# Help for the 'previous' command930#931def cmd_previous_help932print_line "Usage: previous"933print_line934print_line "Set the previously loaded module as the current module"935print_line936print_line "Previous module: #{@previous_module ? @previous_module.fullname : 'none'}"937print_line938end939940#941# Command to enqueque a module on the module stack942#943def cmd_pushm(*args)944# could check if each argument is a valid module, but for now let them hang themselves945if args.count > 0946args.each do |arg|947@module_name_stack.push(arg)948# Note new modules are appended to the array and are only module (full)names949end950else #then just push the active module951if active_module952#print_status "Pushing the active module"953@module_name_stack.push(active_module.fullname)954else955print_error("There isn't an active module and you didn't specify a module to push")956return self.cmd_pushm_help957end958end959end960961#962# Tab completion for the pushm command963#964# @param str [String] the string currently being typed before tab was hit965# @param words [Array<String>] the previously completed words on the command line. words is always966# at least 1 when tab completion has reached this stage since the command itself has been completed967968def cmd_pushm_tabs(str, words)969tab_complete_module(str, words)970end971972#973# Help for the 'pushm' command974#975def cmd_pushm_help976print_line "Usage: pushm [module1 [,module2, module3...]]"977print_line978print_line "push current active module or specified modules onto the module stack"979print_line980end981982#983# Command to dequeque a module from the module stack984#985def cmd_popm(*args)986if (args.count > 1 or not args[0].respond_to?("to_i"))987return self.cmd_popm_help988elsif args.count == 1989# then pop 'n' items off the stack, but don't change the active module990if args[0].to_i >= @module_name_stack.count991# in case they pass in a number >= the length of @module_name_stack992@module_name_stack = []993print_status("The module stack is empty")994else995@module_name_stack.pop(args[0].to_i)996end997else #then just pop the array and make that the active module998pop = @module_name_stack.pop999if pop1000return self.cmd_use(pop)1001else1002print_error("There isn't anything to pop, the module stack is empty")1003end1004end1005end10061007#1008# Help for the 'popm' command1009#1010def cmd_popm_help1011print_line "Usage: popm [n]"1012print_line1013print_line "pop the latest module off of the module stack and make it the active module"1014print_line "or pop n modules off the stack, but don't change the active module"1015print_line1016end10171018def cmd_listm_help1019print_line 'Usage: listm'1020print_line1021print_line 'List the module stack'1022print_line1023end10241025def cmd_listm(*_args)1026if @module_name_stack.empty?1027print_error('The module stack is empty')1028return1029end10301031print_status("Module stack:\n")10321033@module_name_stack.to_enum.with_index.reverse_each do |name, idx|1034print_line("[#{idx}]\t#{name}")1035end1036end10371038def cmd_clearm_help1039print_line 'Usage: clearm'1040print_line1041print_line 'Clear the module stack'1042print_line1043end10441045def cmd_clearm(*_args)1046print_status('Clearing the module stack')1047@module_name_stack.clear1048end10491050#1051# Tab completion for the use command1052#1053# @param str [String] the string currently being typed before tab was hit1054# @param words [Array<String>] the previously completed words on the command line. words is always1055# at least 1 when tab completion has reached this stage since the command itself has been completed10561057def cmd_use_tabs(str, words)1058return [] if words.length > 110591060tab_complete_module(str, words)1061end10621063def cmd_reload_all_help1064print_line "Usage: reload_all"1065print_line1066print_line "Reload all modules from all configured module paths. This may take awhile."1067print_line "See also: loadpath"1068print_line1069end10701071#1072# Reload all module paths that we are aware of1073#1074def cmd_reload_all(*args)1075if args.length > 01076cmd_reload_all_help1077return1078end10791080print_status("Reloading modules from all module paths...")1081framework.modules.reload_modules10821083log_msg = "Please see #{File.join(Msf::Config.log_directory, 'framework.log')} for details."10841085# Check for modules that failed to load1086if framework.modules.module_load_error_by_path.length > 01087wlog("WARNING! The following modules could not be loaded!")10881089framework.modules.module_load_error_by_path.each do |path, _error|1090wlog("\t#{path}")1091end10921093wlog(log_msg)1094end10951096if framework.modules.module_load_warnings.length > 01097wlog("The following modules were loaded with warnings:")10981099framework.modules.module_load_warnings.each do |path, _error|1100wlog("\t#{path}")1101end11021103wlog(log_msg)1104end11051106self.driver.run_single('reload')1107self.driver.run_single("banner")1108end11091110def cmd_back_help1111print_line "Usage: back"1112print_line1113print_line "Return to the global dispatcher context"1114print_line1115end11161117#1118# Pop the current dispatcher stack context, assuming it isn't pointed at1119# the core or database backend stack context.1120#1121def cmd_back(*args)1122if (driver.dispatcher_stack.size > 1 and1123driver.current_dispatcher.name != 'Core' and1124driver.current_dispatcher.name != 'Database Backend')1125# Reset the active module if we have one1126if (active_module)11271128# Do NOT reset the UI anymore1129# active_module.reset_ui11301131# Save the module's datastore so that we can load it later1132# if the module is used again1133@dscache[active_module.fullname] = active_module.datastore.dup11341135self.active_module = nil1136end11371138# Destack the current dispatcher1139driver.destack_dispatcher1140end1141end11421143def cmd_favorite_help1144print_line 'Usage: favorite [mod1 mod2 ...]'1145print_line1146print_line "Add one or multiple modules to the list of favorite modules stored in #{Msf::Config.fav_modules_file}"1147print_line 'If no module name is specified, the command will add the active module if there is one'1148print @@favorite_opts.usage1149end11501151#1152# Helper method for cmd_favorite that writes modules to the fav_modules_file1153#1154def favorite_add(modules, favs_file)1155fav_limit = 501156# obtain useful info about the fav_modules file1157exists, writable, readable, contents = favorite_check_fav_modules(favs_file)11581159# if the fav_modules file exists, check the file permissions1160if exists1161case1162when !writable1163print_error("Unable to save module(s) to the favorite modules file because it is not writable")1164return1165when !readable1166print_error("Unable to save module(s) to the favorite modules file because it is not readable")1167return1168end1169end11701171fav_count = 01172if contents1173fav_count = contents.split.size1174end11751176modules = modules.uniq # prevent modules from being added more than once1177modules.each do |name|1178mod = framework.modules.create(name)1179if (mod == nil)1180print_error("Invalid module: #{name}")1181next1182end11831184if contents && contents.include?(mod.fullname)1185print_warning("Module #{mod.fullname} has already been favorited and will not be added to the favorite modules file")1186next1187end11881189if fav_count >= fav_limit1190print_error("Favorite module limit (#{fav_limit}) exceeded. No more modules will be added.")1191return1192end11931194File.open(favs_file, 'a+') { |file| file.puts(mod.fullname) }1195print_good("Added #{mod.fullname} to the favorite modules file")1196fav_count += 11197end1198return1199end12001201#1202# Helper method for cmd_favorite that deletes modules from the fav_modules_file1203#1204def favorite_del(modules, delete_all, favs_file)1205# obtain useful info about the fav_modules file1206exists, writable, readable, contents = favorite_check_fav_modules(favs_file)12071208if delete_all1209custom_message = 'clear the contents of'1210else1211custom_message = 'delete module(s) from'1212end12131214case # error handling based on the existence / permissions of the fav_modules file1215when !exists1216print_warning("Unable to #{custom_message} the favorite modules file because it does not exist")1217return1218when !writable1219print_error("Unable to #{custom_message} the favorite modules file because it is not writable")1220return1221when !readable1222unless delete_all1223print_error("Unable to #{custom_message} the favorite modules file because it is not readable")1224return1225end1226when contents.empty?1227print_warning("Unable to #{custom_message} the favorite modules file because it is already empty")1228return1229end12301231if delete_all1232File.write(favs_file, '')1233print_good("Favorite modules file cleared")1234return1235end12361237modules = modules.uniq # prevent modules from being deleted more than once1238contents = contents.split1239modules.each do |name|1240mod = framework.modules.create(name)1241if (mod == nil)1242print_error("Invalid module: #{name}")1243next1244end12451246unless contents.include?(mod.fullname)1247print_warning("Module #{mod.fullname} cannot be deleted because it is not in the favorite modules file")1248next1249end12501251contents.delete(mod.fullname)1252print_status("Removing #{mod.fullname} from the favorite modules file")1253end12541255# clear the contents of the fav_modules file if removing the module(s) makes it empty1256if contents.length == 01257File.write(favs_file, '')1258return1259end12601261File.open(favs_file, 'w') { |file| file.puts(contents.join("\n")) }1262end12631264#1265# Helper method for cmd_favorite that checks if the fav_modules file exists and is readable / writable1266#1267def favorite_check_fav_modules(favs_file)1268exists = false1269writable = false1270readable = false1271contents = ''12721273if File.exist?(favs_file)1274exists = true1275end12761277if File.writable?(favs_file)1278writable = true1279end12801281if File.readable?(favs_file)1282readable = true1283contents = File.read(favs_file)1284end12851286return exists, writable, readable, contents1287end12881289#1290# Add modules to or delete modules from the fav_modules file1291#1292def cmd_favorite(*args)1293valid_custom_args = ['-c', '-d', '-l']1294favs_file = Msf::Config.fav_modules_file12951296# always display the help banner if -h is provided or if multiple options are provided1297if args.include?('-h') || args.select{ |arg| arg if valid_custom_args.include?(arg) }.length > 11298cmd_favorite_help1299return1300end13011302# if no arguments were provided, check if there is an active module to add1303if args.empty?1304unless active_module1305print_error('No module has been provided to favorite.')1306cmd_favorite_help1307return1308end13091310args = [active_module.fullname]1311favorite_add(args, favs_file)1312return1313end13141315case args[0]1316when '-c'1317args.delete('-c')1318unless args.empty?1319print_error('Option `-c` does not support arguments.')1320cmd_favorite_help1321return1322end13231324favorite_del(args, true, favs_file)1325when '-d'1326args.delete('-d')1327if args.empty?1328unless active_module1329print_error('No module has been provided to delete.')1330cmd_favorite_help1331return1332end13331334args = [active_module.fullname]1335end13361337favorite_del(args, false, favs_file)1338when '-l'1339args.delete('-l')1340unless args.empty?1341print_error('Option `-l` does not support arguments.')1342cmd_favorite_help1343return1344end1345cmd_show('favorites')1346else # no valid options, but there are arguments1347if args[0].start_with?('-')1348print_error('Invalid option provided')1349cmd_favorite_help1350return1351end13521353favorite_add(args, favs_file)1354end1355end13561357def cmd_favorites_help1358print_line 'Usage: favorites'1359print_line1360print_line 'Print the list of favorite modules (alias for `show favorites`)'1361print_line 'You can use the %grnfavorite%clr command to add the current module to your favorites list'1362print @@favorites_opts.usage1363end13641365#1366# Print the list of favorite modules from the fav_modules file (alias for `show favorites`)1367#1368def cmd_favorites(*args)1369if args.empty?1370cmd_show('favorites')1371return1372end13731374# always display the help banner if the command is called with arguments1375unless args.include?('-h')1376print_error('Invalid option(s) provided')1377end13781379cmd_favorites_help1380end13811382#1383# Tab complete module names1384#1385def tab_complete_module(str, words)1386res = []1387module_metadata = Msf::Modules::Metadata::Cache.instance.get_metadata1388module_metadata.each do |m|1389res << "#{m.type}/#{m.ref_name}"1390end1391framework.modules.module_types.each do |mtyp|1392mset = framework.modules.module_names(mtyp)1393mset.each do |mref|1394res << mtyp + '/' + mref1395end1396end13971398return dangerzone_modules_to_codenames(res.sort) if dangerzone_active?1399return res.uniq.sort1400end14011402def print_april_fools_module_use1403return unless ENV['APRILFOOLSMODULEUSE'] || Time.now.strftime("%m%d") == "0401"14041405banner = Msf::Ui::Banner.readfile('help-using-a-module.txt')1406print_line("%grn#{banner}%clr")1407end14081409#1410# Convert squirrel names back to regular module names1411#1412def dangerzone_codename_to_module(args)1413return args unless dangerzone_active? && args.length > 0 && args[0].length > 01414return args unless args[0] =~ /^[A-Z]/1415args[0] = dangerzone_codename_to_module_name(args[0])1416args1417end14181419#1420# Determine if dangerzone mode is active via date or environment variable1421#1422def dangerzone_active?1423active = Time.now.strftime("%m%d") == "0401" || Rex::Compat.getenv('DANGERZONE').to_i > 01424if active && @dangerzone_map.nil?1425dangerzone_build_map1426end1427active1428end14291430#1431# Convert module names to squirrel names1432#1433def dangerzone_modules_to_codenames(names)1434(names + @dangerzone_map.keys.grep(/^[A-Z]+/)).sort1435end14361437def dangerzone_codename_to_module_name(cname)1438@dangerzone_map[cname] || cname1439end14401441def dangerzone_module_name_to_codename(mname)1442@dangerzone_map[mname] || mname1443end14441445def dangerzone_build_map1446return unless @dangerzone_map.nil?14471448@dangerzone_map = {}14491450res = []1451%W{exploit auxiliary}.each do |mtyp|1452mset = framework.modules.module_names(mtyp)1453mset.each do |mref|1454res << mtyp + '/' + mref1455end1456end14571458words_a = ::File.readlines(::File.join(1459::Msf::Config.data_directory, "wordlists", "dangerzone_a.txt"1460)).map{|line| line.strip.upcase}14611462words_b = ::File.readlines(::File.join(1463::Msf::Config.data_directory, "wordlists", "dangerzone_b.txt"1464)).map{|line| line.strip.upcase}14651466aidx = -11467bidx = -114681469res.sort.each do |mname|1470word_a = words_a[ (aidx += 1) % words_a.length ]1471word_b = words_b[ (bidx += 1) % words_b.length ]1472cname = word_a + word_b14731474while @dangerzone_map[cname]1475aidx += 11476word_a = words_a[ (aidx += 1) % words_a.length ]1477cname = word_a + word_b1478end14791480@dangerzone_map[mname] = cname1481@dangerzone_map[cname] = mname1482end1483end14841485#1486# Module list enumeration1487#14881489def show_encoders # :nodoc:1490# If an active module has been selected and it's an exploit, get the1491# list of compatible encoders and display them1492if (active_module and active_module.exploit? == true)1493show_module_metadata('Compatible Encoders', active_module.compatible_encoders)1494else1495show_module_metadata('Encoders', 'encoder')1496end1497end14981499def show_nops # :nodoc:1500show_module_metadata('NOP Generators', 'nop')1501end15021503def show_exploits # :nodoc:1504show_module_metadata('Exploits', 'exploit')1505end15061507def show_payloads # :nodoc:1508# If an active module has been selected and it's an exploit, get the1509# list of compatible payloads and display them1510if active_module && (active_module.exploit? || active_module.evasion?)1511@@payload_show_results = active_module.compatible_payloads15121513show_module_metadata('Compatible Payloads', @@payload_show_results)1514else1515# show_module_set(‘Payloads’, framework.payloads, regex, minrank, opts)1516show_module_metadata('Payloads', 'payload')1517end1518end15191520def show_auxiliary # :nodoc:1521show_module_metadata('Auxiliary','auxiliary')1522end15231524def show_post # :nodoc:1525show_module_metadata('Post','post')1526end15271528def show_evasion # :nodoc:1529show_module_metadata('Evasion','evasion')1530end15311532def show_favorites # :nodoc:1533favs_file = Msf::Config.fav_modules_file15341535unless File.exist?(favs_file)1536print_error("The favorite modules file does not exist")1537return1538end15391540if File.zero?(favs_file)1541print_warning("The favorite modules file is empty")1542return1543end15441545unless File.readable?(favs_file)1546print_error("Unable to read from #{favs_file}")1547return1548end15491550# create module set using the saved modules1551fav_modules = {}15521553# get the full module names from the favorites file and use then to search the MetaData Cache for matching modules1554saved_favs = File.readlines(favs_file).map(&:strip)1555saved_favs.each do |mod|1556# populate hash with module fullname and module object1557fav_modules[mod] = framework.modules[mod]1558end15591560fav_modules.each do |fullname, mod_obj|1561if mod_obj.nil?1562print_warning("#{favs_file} contains a module that can not be found - #{fullname}.")1563end1564end15651566# find cache module instance and add it to @module_search_results1567@module_search_results = Msf::Modules::Metadata::Cache.instance.find('fullname' => [saved_favs, []])15681569# This scenario is for when a module fullname is a substring of other module fullnames1570# Example, searching for the payload/windows/meterpreter/reverse_tcp module can result in matches for:1571# - windows/meterpreter/reverse_tcp_allports1572# - windows/meterpreter/reverse_tcp_dns1573# So if @module_search_results is greater than the amount of fav_modules, we need to filter the results to be more accurate1574if fav_modules.length < @module_search_results.length1575filtered_results = []1576fav_modules.each do |fullname, _mod_obj|1577filtered_results << @module_search_results.select do |search_result|1578search_result.fullname == fullname1579end1580end1581@module_search_results = filtered_results.flatten.sort_by(&:fullname)1582end1583@module_search_results_with_usage_metadata = @module_search_results.map { |mod| { mod: mod, datastore: {} } }15841585show_module_metadata('Favorites', fav_modules)1586print_module_search_results_usage1587end15881589def show_missing(mod) # :nodoc:1590mod_opt = Serializer::ReadableText.dump_options(mod, ' ', true)1591print("\nModule options (#{mod.fullname}):\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)15921593# If it's an exploit and a payload is defined, create it and1594# display the payload's options1595if (mod.exploit? and mod.datastore['PAYLOAD'])1596p = framework.payloads.create(mod.datastore['PAYLOAD'])15971598if (!p)1599print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")1600return1601end16021603p.share_datastore(mod.datastore)16041605if (p)1606p_opt = Serializer::ReadableText.dump_options(p, ' ', true)1607print("\nPayload options (#{mod.datastore['PAYLOAD']}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0)1608end1609end1610end16111612def show_global_options1613columns = [ 'Option', 'Current Setting', 'Description' ]1614tbl = Table.new(1615Table::Style::Default,1616'Header' => 'Global Options:',1617'Prefix' => "\n",1618'Postfix' => "\n",1619'Columns' => columns1620)1621[1622[ 'ConsoleLogging', framework.datastore['ConsoleLogging'] || "false", 'Log all console input and output' ],1623[ 'LogLevel', framework.datastore['LogLevel'] || "0", 'Verbosity of logs (default 0, max 3)' ],1624[ 'MinimumRank', framework.datastore['MinimumRank'] || "0", 'The minimum rank of exploits that will run without explicit confirmation' ],1625[ 'SessionLogging', framework.datastore['SessionLogging'] || "false", 'Log all input and output for sessions' ],1626[ 'SessionTlvLogging', framework.datastore['SessionTlvLogging'] || "false", 'Log all incoming and outgoing TLV packets' ],1627[ 'TimestampOutput', framework.datastore['TimestampOutput'] || "false", 'Prefix all console output with a timestamp' ],1628[ 'Prompt', framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt.to_s.gsub(/%.../,"") , "The prompt string" ],1629[ 'PromptChar', framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar.to_s.gsub(/%.../,""), "The prompt character" ],1630[ 'PromptTimeFormat', framework.datastore['PromptTimeFormat'] || Time::DATE_FORMATS[:db].to_s, 'Format for timestamp escapes in prompts' ],1631[ 'MeterpreterPrompt', framework.datastore['MeterpreterPrompt'] || '%undmeterpreter%clr', 'The meterpreter prompt string' ],1632].each { |r| tbl << r }16331634print(tbl.to_s)1635end16361637def show_targets(mod) # :nodoc:1638case mod1639when Msf::Exploit1640mod_targs = Serializer::ReadableText.dump_exploit_targets(mod, '', "\nExploit targets:")1641print("#{mod_targs}\n") if (mod_targs and mod_targs.length > 0)1642when Msf::Evasion1643mod_targs = Serializer::ReadableText.dump_evasion_targets(mod, '', "\nEvasion targets:")1644print("#{mod_targs}\n") if (mod_targs and mod_targs.length > 0)1645end1646end16471648def show_actions(mod) # :nodoc:1649mod_actions = Serializer::ReadableText.dump_module_actions(mod, ' ')1650print("\n#{mod.type.capitalize} actions:\n\n#{mod_actions}\n") if (mod_actions and mod_actions.length > 0)1651end16521653def show_advanced_options(mod) # :nodoc:1654mod_opt = Serializer::ReadableText.dump_advanced_options(mod, ' ')1655print("\nModule advanced options (#{mod.fullname}):\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)16561657# If it's an exploit and a payload is defined, create it and1658# display the payload's options1659if (mod.exploit? and mod.datastore['PAYLOAD'])1660p = framework.payloads.create(mod.datastore['PAYLOAD'])16611662if (!p)1663print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")1664return1665end16661667p.share_datastore(mod.datastore)16681669if (p)1670p_opt = Serializer::ReadableText.dump_advanced_options(p, ' ')1671print("\nPayload advanced options (#{mod.datastore['PAYLOAD']}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0)1672end1673end1674print("\nView the full module info with the #{Msf::Ui::Tip.highlight('info')}, or #{Msf::Ui::Tip.highlight('info -d')} command.\n\n")1675end16761677def show_evasion_options(mod) # :nodoc:1678mod_opt = Serializer::ReadableText.dump_evasion_options(mod, ' ')1679print("\nModule evasion options:\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)16801681# If it's an exploit and a payload is defined, create it and1682# display the payload's options1683if (mod.evasion? and mod.datastore['PAYLOAD'])1684p = framework.payloads.create(mod.datastore['PAYLOAD'])16851686if (!p)1687print_error("Invalid payload defined: #{mod.datastore['PAYLOAD']}\n")1688return1689end16901691p.share_datastore(mod.datastore)16921693if (p)1694p_opt = Serializer::ReadableText.dump_evasion_options(p, ' ')1695print("\nPayload evasion options (#{mod.datastore['PAYLOAD']}):\n\n#{p_opt}\n") if (p_opt and p_opt.length > 0)1696end1697end1698end16991700def show_plugins # :nodoc:1701tbl = Table.new(1702Table::Style::Default,1703'Header' => 'Loaded Plugins',1704'Prefix' => "\n",1705'Postfix' => "\n",1706'Columns' => [ 'Name', 'Description' ]1707)17081709framework.plugins.each { |plugin|1710tbl << [ plugin.name, plugin.desc ]1711}17121713# create an instance of core to call the list_plugins1714core = Msf::Ui::Console::CommandDispatcher::Core.new(driver)1715core.list_plugins1716print(tbl.to_s)1717end17181719# @param [table_name] used to name table1720# @param [module_filter] this will either be a modules fullname, or it will be an Array(show payloads/encoders)1721# or a Hash(show favorites) containing fullname1722# @param [compatible_mod] handles logic for if there is an active module when the1723# `show` command is run1724#1725# Handles the filtering of modules that will be generated into a table1726def show_module_metadata(table_name, module_filter)1727count = -11728tbl = generate_module_table(table_name)17291730if module_filter.is_a?(Array) || module_filter.is_a?(Hash)1731module_filter.sort.each do |_mod_fullname, mod_obj|1732mod = nil17331734begin1735mod = mod_obj.new1736rescue ::Exception1737end1738next unless mod17391740count += 11741tbl << add_record(mod, count, true)1742end1743else1744results = Msf::Modules::Metadata::Cache.instance.find(1745'type' => [[module_filter], []]1746)1747# Loop over each module and gather data1748results.each do |mod, _value|1749count += 11750tbl << add_record(mod, count, false)1751end1752end1753print(tbl.to_s)1754end17551756# @param [mod] current module being passed in1757# @param [count] passes the count for each record1758# @param [compatible_mod] handles logic for if there is an active module when the1759# `show` command is run1760#1761# Adds a record for a table, also handle logic for whether the module is currently1762# handling compatible payloads/encoders1763def add_record(mod, count, compatible_mod)1764if compatible_mod1765check = mod.has_check? ? 'Yes' : 'No'1766else1767check = mod.check ? 'Yes' : 'No'1768end1769[1770count,1771mod.fullname,1772mod.disclosure_date.nil? ? '' : mod.disclosure_date.strftime('%Y-%m-%d'),1773mod.rank,1774check,1775mod.name1776]1777end17781779def generate_module_table(type, search_terms = [], row_filter = nil) # :nodoc:1780table_hierarchy_formatters = framework.features.enabled?(Msf::FeatureManager::HIERARCHICAL_SEARCH_TABLE) ? [Msf::Ui::Console::TablePrint::BlankFormatter.new] : []17811782Table.new(1783Table::Style::Default,1784'Header' => type,1785'Prefix' => "\n",1786'Postfix' => "\n",1787'SearchTerm' => row_filter,1788'SortIndex' => -1,1789# For now, don't perform any word wrapping on the search table as it breaks the workflow of1790# copying module names in conjunction with the `use <paste-buffer>` command1791'WordWrap' => false,1792'Columns' => [1793'#',1794'Name',1795'Disclosure Date',1796'Rank',1797'Check',1798'Description'1799],1800'ColProps' => {1801'Rank' => {1802'Formatters' => [1803*table_hierarchy_formatters,1804Msf::Ui::Console::TablePrint::RankFormatter.new1805],1806'Stylers' => [1807Msf::Ui::Console::TablePrint::RankStyler.new1808]1809},1810'Name' => {1811'Strip' => false,1812'Stylers' => [Msf::Ui::Console::TablePrint::HighlightSubstringStyler.new(search_terms)]1813},1814'Check' => {1815'Formatters' => [1816*table_hierarchy_formatters,1817]1818},1819'Disclosure Date' => {1820'Formatters' => [1821*table_hierarchy_formatters,1822]1823},1824'Description' => {1825'Stylers' => [1826Msf::Ui::Console::TablePrint::HighlightSubstringStyler.new(search_terms)1827]1828}1829}1830)1831end1832end1833end1834end1835end1836end183718381839