Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/lib/msf/ui/console/module_option_tab_completion.rb
Views: 11783
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::DataStoreWithFallbacks::GLOBAL_KEYS +20datastore.keys21)22keys.concat(datastore.options.values.flat_map(&:fallbacks)) if datastore.is_a?(Msf::DataStoreWithFallbacks)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.sorted.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.sorted.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]118end119end120end121return res122end123124#125# Provide tab completion for option values126#127def tab_complete_option_values(mod, str, words, opt:)128if words.last.casecmp?('SessionTlvLogging')129return %w[console true false file:<file>]130end131132res = []133# With no module, we have nothing to complete134if !mod135return res136end137138# Well-known option names specific to exploits139if mod.exploit?140return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'141return option_values_targets(mod) if opt.upcase == 'TARGET'142return option_values_nops if opt.upcase == 'NOPS'143return option_values_encoders if opt.upcase == 'STAGEENCODER'144elsif mod.evasion?145return option_values_payloads(mod) if opt.upcase == 'PAYLOAD'146return option_values_targets(mod) if opt.upcase == 'TARGET'147end148# Well-known option names specific to modules with actions149if mod.is_a?(Msf::Module::HasActions)150return option_values_actions(mod) if opt.upcase == 'ACTION'151end152# The ENCODER option works for evasions, payloads and exploits153if ((mod.evasion? || mod.exploit? || mod.payload?) && (opt.upcase == 'ENCODER'))154return option_values_encoders155end156157# Well-known option names specific to post-exploitation158if (mod.post? || mod.exploit?)159return option_values_sessions(mod) if opt.upcase == 'SESSION'160end161# Is this option used by the active module?162mod.options.each_key do |key|163if key.downcase == opt.downcase164res.concat(option_values_dispatch(mod, mod.options[key], str, words))165end166end167# How about the selected payload?168if ((mod.evasion? || mod.exploit?) && mod.datastore['PAYLOAD'])169if p = framework.payloads.create(mod.datastore['PAYLOAD'])170p.options.each_key do |key|171res.concat(option_values_dispatch(mod, p.options[key], str, words)) if key.downcase == opt.downcase172end173end174end175return res176end177178#179# Provide possible option values based on type180#181def option_values_dispatch(mod, o, str, words)182res = []183res << o.default.to_s if o.default184case o185when Msf::OptAddress186case o.name.upcase187when 'RHOST'188option_values_target_addrs(mod).each do |addr|189res << addr190end191when 'LHOST', 'SRVHOST', 'REVERSELISTENERBINDADDRESS'192rh = mod.datastore['RHOST'] || framework.datastore['RHOST']193if rh && !rh.empty?194res << Rex::Socket.source_address(rh)195else196res += tab_complete_source_address197res += tab_complete_source_interface(o)198end199end200when Msf::OptAddressRange, Msf::OptRhosts201case str202when /^file:(.*)/203files = tab_complete_filenames(Regexp.last_match(1), words)204res += files.map { |f| 'file:' + f } if files205when %r{^(.*)/\d{0,2}$}206left = Regexp.last_match(1)207if Rex::Socket.is_ipv4?(left)208res << left + '/32'209res << left + '/24'210res << left + '/16'211end212when /^(.*)\-$/213left = Regexp.last_match(1)214if Rex::Socket.is_ipv4?(left)215res << str + str[0, str.length - 1]216end217else218option_values_target_addrs(mod).each do |addr|219res << addr220end221end222when Msf::OptPort223case o.name.upcase224when 'RPORT'225option_values_target_ports(mod).each do |port|226res << port227end228end229when Msf::OptEnum230o.enums.each do |val|231res << val232end233when Msf::OptPath234files = tab_complete_filenames(str, words)235res += files if files236when Msf::OptBool237res << 'true'238res << 'false'239when Msf::OptString240if (str =~ /^file:(.*)/)241files = tab_complete_filenames(Regexp.last_match(1), words)242res += files.map { |f| 'file:' + f } if files243end244end245return res246end247248# XXX: We repurpose OptAddressLocal#interfaces, so we can't put this in Rex249def tab_complete_source_interface(o)250return [] unless o.is_a?(Msf::OptAddressLocal)251252o.interfaces253end254255#256# Provide valid payload options for the current exploit257#258def option_values_payloads(mod)259if @cache_payloads && mod == @previous_module && mod.target == @previous_target260return @cache_payloads261end262263@previous_module = mod264@previous_target = mod.target265@cache_payloads = mod.compatible_payloads.map do |refname, _payload|266refname267end268@cache_payloads269end270271#272# Provide valid session options for the current post-exploit module273#274def option_values_sessions(mod)275if mod.respond_to?(:compatible_sessions)276mod.compatible_sessions.map { |sid| sid.to_s }277end278end279280#281# Provide valid target options for the current exploit282#283def option_values_targets(mod)284res = []285if mod.targets2861.upto(mod.targets.length) { |i| res << (i - 1).to_s }287res += mod.targets.map(&:name)288end289return res290end291292#293# Provide valid action options for the current module294#295def option_values_actions(mod)296res = []297if mod.actions298mod.actions.each { |i| res << i.name }299end300return res301end302303#304# Provide valid nops options for the current exploit305#306def option_values_nops307framework.nops.module_refnames308end309310#311# Provide valid encoders options for the current exploit or payload312#313def option_values_encoders314framework.encoders.module_refnames315end316317#318# Provide the target addresses319#320def option_values_target_addrs(mod)321res = [ ]322res << Rex::Socket.source_address323return res if !framework.db.active324325# List only those hosts with matching open ports?326mport = mod.datastore['RPORT']327if mport328mport = mport.to_i329hosts = {}330framework.db.services.each do |service|331if service.port == mport332hosts[service.host.address] = true333end334end335hosts.keys.each do |host|336res << host337end338# List all hosts in the database339else340framework.db.hosts.each do |host|341res << host.address342end343end344return res345end346347#348# Provide the target ports349#350def option_values_target_ports(mod)351return [] unless framework.db.active352return [] if mod.datastore['RHOST'].nil?353354host_addresses = mod.datastore['RHOST'].split.map do |addr|355address, _scope = addr.split('%', 2)356address357end358359hosts = framework.db.hosts({:address => host_addresses, :workspace => framework.db.workspace})360return [] if hosts.empty?361362res = []363hosts.each do |host|364host.services.each do |service|365res << service.port.to_s366end367end368369res.uniq370end371end372end373end374end375376377