Path: blob/master/lib/msf/ui/console/module_option_tab_completion.rb
19535 views
module Msf1module Ui2module Console3###4#5# Module-specific tab completion helper.6#7###8module ModuleOptionTabCompletion9#10# Tab completion for datastore names11#12# @param datastore [Msf::DataStore]13# @param _str [String] the string currently being typed before tab was hit14# @param _words [Array<String>] the previously completed words on the command15# line. `_words` is always at least 1 when tab completion has reached this16# stage since the command itself has been completed.17def tab_complete_datastore_names(datastore, _str, _words)18keys = (19Msf::DataStore::GLOBAL_KEYS +20datastore.keys21)22keys.concat(datastore.options.values.flat_map(&:fallbacks)) if datastore.is_a?(Msf::DataStore)23keys.uniq! { |key| key.downcase }24keys25end2627#28# Tab completion for a module's datastore names29#30# @param mod [Msf::Module]31# @param str [String] the string currently being typed before tab was hit32# @param words [Array<String>] the previously completed words on the command33# line. `words` is always at least 1 when tab completion has reached this34# stage since the command itself has been completed.35def tab_complete_module_datastore_names(mod, str, words)36datastore = mod ? mod.datastore : framework.datastore37keys = tab_complete_datastore_names(datastore, str, words)3839if mod40keys = keys.delete_if do |name|41!(mod_opt = mod.options[name]).nil? && !Msf::OptCondition.show_option(mod, mod_opt)42end43end44keys45end4647#48# Tab completion options values49#50def tab_complete_option(mod, str, words)51if str.end_with?('=')52option_name = str.chop53option_value = ''5455::Readline.completion_append_character = ' '56return tab_complete_option_values(mod, option_value, words, opt: option_name).map { |value| "#{str}#{value}" }57elsif str.include?('=')58str_split = str.split('=')59option_name = str_split[0].strip60option_value = str_split[1].strip6162::Readline.completion_append_character = ' '63return tab_complete_option_values(mod, option_value, words, opt: option_name).map { |value| "#{option_name}=#{value}" }64end6566::Readline.completion_append_character = ''67tab_complete_option_names(mod, str, words).map { |name| "#{name}=" }68end6970#71# Provide tab completion for name values72#73def tab_complete_option_names(mod, str, words)74res = tab_complete_module_datastore_names(mod, str, words) || [ ]7576if !mod77return res78end7980mod.options.each do |e|81name, _opt = e82res << name83end84# Exploits provide these three default options85if mod.exploit?86res << 'PAYLOAD'87res << 'NOP'88res << 'TARGET'89res << 'ENCODER'90elsif mod.evasion?91res << 'PAYLOAD'92res << 'TARGET'93res << 'ENCODER'94elsif mod.payload?95res << 'ENCODER'96end97if mod.is_a?(Msf::Module::HasActions)98res << 'ACTION'99end100if ((mod.exploit? || mod.evasion?) && mod.datastore['PAYLOAD'])101p = framework.payloads.create(mod.datastore['PAYLOAD'])102if p103p.options.each do |e|104name, _opt = e105res << name106end107end108end109unless str.blank?110res = res.select { |term| term.upcase.start_with?(str.upcase) }111res = res.map do |term|112if str == str.upcase113str + term[str.length..-1].upcase114elsif str == str.downcase115str + term[str.length..-1].downcase116else117str + term[str.length..-1]118end119end120end121122return res.sort123end124125#126# Provide tab completion for option values127#128def tab_complete_option_values(mod, str, words, opt:)129if words.last.casecmp?('SessionTlvLogging')130return %w[console true false file:<file>]131end132133res = []134# With no module, we have nothing to complete135if !mod136return res137end138139# Well-known option names specific to exploits140if mod.exploit?141return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'142return option_values_targets(mod) if opt.upcase == 'TARGET'143return option_values_nops if opt.upcase == 'NOPS'144return option_values_encoders if opt.upcase == 'STAGEENCODER'145elsif mod.evasion?146return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'147return option_values_targets(mod) if opt.upcase == 'TARGET'148end149# Well-known option names specific to modules with actions150if mod.is_a?(Msf::Module::HasActions)151return option_values_actions(mod) if opt.upcase == 'ACTION'152end153# The ENCODER option works for evasions, payloads and exploits154if ((mod.evasion? || mod.exploit? || mod.payload?) && (opt.upcase == 'ENCODER'))155return option_values_encoders156end157158# Well-known option names specific to post-exploitation159if (mod.post? || mod.exploit?)160return option_values_sessions(mod) if opt.upcase == 'SESSION'161end162# Is this option used by the active module?163mod.options.each_key do |key|164if key.downcase == opt.downcase165res.concat(option_values_dispatch(mod, mod.options[key], str, words))166end167end168# How about the selected payload?169if ((mod.evasion? || mod.exploit?) && mod.datastore['PAYLOAD'])170if p = framework.payloads.create(mod.datastore['PAYLOAD'])171p.options.each_key do |key|172res.concat(option_values_dispatch(mod, p.options[key], str, words)) if key.downcase == opt.downcase173end174end175end176return res177end178179#180# Provide possible option values based on type181#182def option_values_dispatch(mod, o, str, words)183res = []184res << o.default.to_s if o.default185case o186when Msf::OptAddress187case o.name.upcase188when 'RHOST'189option_values_target_addrs(mod).each do |addr|190res << addr191end192when 'LHOST', 'SRVHOST', 'REVERSELISTENERBINDADDRESS'193rh = mod.datastore['RHOST'] || framework.datastore['RHOST']194if rh && !rh.empty?195res << Rex::Socket.source_address(rh)196else197res += tab_complete_source_address198res += tab_complete_source_interface(o)199end200end201when Msf::OptAddressRange, Msf::OptRhosts202case str203when /^file:(.*)/204files = tab_complete_filenames(Regexp.last_match(1), words)205res += files.map { |f| 'file:' + f } if files206when %r{^(.*)/\d{0,2}$}207left = Regexp.last_match(1)208if Rex::Socket.is_ipv4?(left)209res << left + '/32'210res << left + '/24'211res << left + '/16'212end213when /^(.*)\-$/214left = Regexp.last_match(1)215if Rex::Socket.is_ipv4?(left)216res << str + str[0, str.length - 1]217end218else219option_values_target_addrs(mod).each do |addr|220res << addr221end222end223when Msf::OptPort224case o.name.upcase225when 'RPORT'226option_values_target_ports(mod).each do |port|227res << port228end229end230when Msf::OptEnum231o.enums.each do |val|232res << val233end234when Msf::OptPath235files = tab_complete_filenames(str, words)236res += files if files237when Msf::OptBool238res << 'true'239res << 'false'240when Msf::OptString241if (str =~ /^file:(.*)/)242files = tab_complete_filenames(Regexp.last_match(1), words)243res += files.map { |f| 'file:' + f } if files244end245end246return res247end248249# XXX: We repurpose OptAddressLocal#interfaces, so we can't put this in Rex250def tab_complete_source_interface(o)251return [] unless o.is_a?(Msf::OptAddressLocal)252253o.interfaces254end255256#257# Provide valid payload options for the current exploit258#259def option_values_payloads(mod)260if @cache_payloads && mod == @previous_module && mod.target == @previous_target261return @cache_payloads262end263264@previous_module = mod265@previous_target = mod.target266@cache_payloads = mod.compatible_payloads.map do |refname, _payload|267refname268end269@cache_payloads270end271272#273# Provide valid session options for the current post-exploit module274#275def option_values_sessions(mod)276if mod.respond_to?(:compatible_sessions)277mod.compatible_sessions.map { |sid| sid.to_s }278end279end280281#282# Provide valid target options for the current exploit283#284def option_values_targets(mod)285res = []286if mod.targets2871.upto(mod.targets.length) { |i| res << (i - 1).to_s }288res += mod.targets.map(&:name)289end290return res291end292293#294# Provide valid action options for the current module295#296def option_values_actions(mod)297res = []298if mod.actions299mod.actions.each { |i| res << i.name }300end301return res302end303304#305# Provide valid nops options for the current exploit306#307def option_values_nops308framework.nops.module_refnames309end310311#312# Provide valid encoders options for the current exploit or payload313#314def option_values_encoders315framework.encoders.module_refnames316end317318#319# Provide the target addresses320#321def option_values_target_addrs(mod)322res = [ ]323res << Rex::Socket.source_address324return res if !framework.db.active325326# List only those hosts with matching open ports?327mport = mod.datastore['RPORT']328if mport329mport = mport.to_i330hosts = {}331framework.db.services.each do |service|332if service.port == mport333hosts[service.host.address] = true334end335end336hosts.keys.each do |host|337res << host338end339# List all hosts in the database340else341framework.db.hosts.each do |host|342res << host.address343end344end345return res346end347348#349# Provide the target ports350#351def option_values_target_ports(mod)352return [] unless framework.db.active353return [] if mod.datastore['RHOST'].nil?354355host_addresses = mod.datastore['RHOST'].split.map do |addr|356address, _scope = addr.split('%', 2)357address358end359360hosts = framework.db.hosts({:address => host_addresses, :workspace => framework.db.workspace})361return [] if hosts.empty?362363res = []364hosts.each do |host|365host.services.each do |service|366res << service.port.to_s367end368end369370res.uniq371end372end373end374end375end376377378