Path: blob/master/lib/msf/base/serializer/readable_text.rb
21546 views
# -*- coding: binary -*-123module Msf4module Serializer56# This class formats information in a plain-text format that7# is meant to be displayed on a console or some other non-GUI8# medium.9class ReadableText1011#Default number of characters to wrap at.12DefaultColumnWrap = 7013#Default number of characters to indent.14DefaultIndent = 21516# Returns a formatted string that contains information about17# the supplied module instance.18#19# @param mod [Msf::Module] the module to dump information for.20# @param indent [String] the indentation to use.21# @return [String] formatted text output of the dump.22def self.dump_module(mod, indent = " ")23case mod.type24when Msf::MODULE_PAYLOAD25return dump_payload_module(mod, indent)26when Msf::MODULE_NOP27return dump_basic_module(mod, indent)28when Msf::MODULE_ENCODER29return dump_basic_module(mod, indent)30when Msf::MODULE_EXPLOIT31return dump_exploit_module(mod, indent)32when Msf::MODULE_AUX33return dump_auxiliary_module(mod, indent)34when Msf::MODULE_POST35return dump_post_module(mod, indent)36when Msf::MODULE_EVASION37return dump_evasion_module(mod, indent)38else39return dump_generic_module(mod, indent)40end41end4243# Dumps an exploit's targets.44#45# @param mod [Msf::Exploit] the exploit module to dump targets46# for.47# @param indent [String] the indentation to use (only the length48# matters).49# @param h [String] the string to display as the table heading.50# @return [String] the string form of the table.51def self.dump_exploit_targets(mod, indent = '', h = nil)52tbl = Rex::Text::Table.new(53'Indent' => indent.length,54'Header' => h,55'Columns' =>56[57'IsTarget',58'Id',59'Name',60],61'SortIndex' => 1,62'ColProps' => {63'IsTarget' => {64'Stylers' => [Msf::Ui::Console::TablePrint::RowIndicatorStyler.new],65'ColumnStylers' => [Msf::Ui::Console::TablePrint::OmitColumnHeader.new],66'Width' => 267}68}69)7071mod.targets.each_with_index do |target, idx|72is_target = mod.target == target7374tbl << [is_target, idx.to_s, target.name || 'All' ]75end7677tbl.to_s + "\n"78end7980def self.dump_evasion_targets(mod, indent = '', h = nil)81tbl = Rex::Text::Table.new(82'Indent' => indent.length,83'Header' => h,84'Columns' =>85[86'IsTarget',87'Id',88'Name',89],90'SortIndex' => 1,91'ColProps' => {92'IsTarget' => {93'Stylers' => [Msf::Ui::Console::TablePrint::RowIndicatorStyler.new],94'ColumnStylers' => [Msf::Ui::Console::TablePrint::OmitColumnHeader.new],95'Width' => 296}97}98)99100mod.targets.each_with_index do |target, idx|101is_target = mod.target == target102103tbl << [is_target, idx.to_s, target.name || 'All' ]104end105106tbl.to_s + "\n"107end108109# Dumps the exploit's selected target110#111# @param mod [Msf::Exploit] the exploit module.112# @param indent [String] the indentation to use (only the length113# matters)114# @param h [String] the string to display as the table heading.115# @return [String] the string form of the table.116def self.dump_exploit_target(mod, indent = '', h = nil)117tbl = Rex::Text::Table.new(118'Indent' => indent.length,119'Header' => h,120'Columns' =>121[122'Id',123'Name',124])125126tbl << [ mod.target_index, mod.target.name || 'All' ]127128tbl.to_s + "\n"129end130131# Dumps the evasion module's selected target132#133# @param mod [Msf::Evasion] The evasion module.134# @param indent [String] The indentation to use (only the length matters)135# @param h [String] The string to display as the table heading.136# @return [String] The strong form of the table.137def self.dump_evasion_target(mod, indent = '', h = nil)138tbl = Rex::Text::Table.new(139'Indent' => indent.length,140'Header' => h,141'Columns' =>142[143'Id',144'Name',145])146147tbl << [ mod.target_index, mod.target.name || 'All' ]148149tbl.to_s + "\n"150end151152# Dumps a module's actions153#154# @param mod [Msf::Module] the module.155# @param indent [String] the indentation to use (only the length156# matters)157# @param h [String] the string to display as the table heading.158# @return [String] the string form of the table.159def self.dump_module_actions(mod, indent = '', h = nil)160tbl = Rex::Text::Table.new(161'Indent' => indent.length,162'Header' => h,163'Columns' =>164[165'ActionEnabled',166'Name',167'Description'168],169'SortIndex' => 1,170'ColProps' => {171'ActionEnabled' => {172'Stylers' => [Msf::Ui::Console::TablePrint::RowIndicatorStyler.new],173'ColumnStylers' => [Msf::Ui::Console::TablePrint::OmitColumnHeader.new],174'Width' => 2175}176}177)178179mod.actions.each_with_index { |target, idx|180action_enabled = mod.action == target181182tbl << [ action_enabled, target.name || 'All' , target.description || '' ]183}184185tbl.to_s + "\n"186end187188# Dumps the module's selected action189#190# @param mod [Msf::Module] the module.191# @param indent [String] the indentation to use (only the length192# matters)193# @param h [String] the string to display as the table heading.194# @return [String] the string form of the table.195def self.dump_module_action(mod, indent = '', h = nil)196tbl = Rex::Text::Table.new(197'Indent' => indent.length,198'Header' => h,199'Columns' =>200[201'Name',202'Description',203])204205tbl << [ mod.action.name || 'All', mod.action.description || '' ]206207tbl.to_s + "\n"208end209210# Dumps the table of payloads that are compatible with the supplied211# exploit.212#213# @param exploit [Msf::Exploit] the exploit module.214# @param indent [String] the indentation to use (only the length215# matters)216# @param h [String] the string to display as the table heading.217# @return [String] the string form of the table.218def self.dump_compatible_payloads(exploit, indent = '', h = nil)219tbl = Rex::Text::Table.new(220'Indent' => indent.length,221'Header' => h,222'Columns' =>223[224'Name',225'Description',226])227228exploit.compatible_payloads.each { |entry|229tbl << [ entry[0], entry[1].new.description ]230}231232tbl.to_s + "\n"233end234235def self.dump_traits(mod, indent=' ')236output = ''237238unless mod.side_effects.empty?239output << "Module side effects:\n"240mod.side_effects.each { |side_effect|241output << indent + side_effect + "\n"242}243output << "\n"244end245246unless mod.stability.empty?247output << "Module stability:\n"248mod.stability.each { |stability|249output << indent + stability + "\n"250}251output << "\n"252end253254unless mod.reliability.empty?255output << "Module reliability:\n"256mod.reliability.each { |reliability|257output << indent + reliability + "\n"258}259output << "\n"260end261262output263end264265# Dumps information about an exploit module.266#267# @param mod [Msf::Exploit] the exploit module.268# @param indent [String] the indentation to use.269# @return [String] the string form of the information.270def self.dump_exploit_module(mod, indent = '')271output = "\n"272output << " Name: #{mod.name}\n"273output << " Module: #{mod.fullname}\n"274output << " Platform: #{mod.platform_to_s}\n"275output << " Arch: #{mod.arch_to_s}\n"276output << " Privileged: " + (mod.privileged? ? "Yes" : "No") + "\n"277output << " License: #{mod.license}\n"278output << " Rank: #{mod.rank_to_s.capitalize}\n"279output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date280output << "\n"281282# Authors283output << "Provided by:\n"284mod.each_author { |author|285output << indent + author.to_s + "\n"286}287output << "\n"288289output << dump_traits(mod)290291# Targets292output << "Available targets:\n"293output << dump_exploit_targets(mod, indent)294295# Check296output << "Check supported:\n"297output << "#{indent}#{mod.has_check? ? 'Yes' : 'No'}\n\n"298299# Options300if (mod.options.has_options?)301output << "Basic options:\n"302output << dump_options(mod, indent)303output << "\n"304end305306# Payload information307if (mod.payload_info.length)308output << "Payload information:\n"309if (mod.payload_space)310output << indent + "Space: " + mod.payload_space.to_s + "\n"311end312if (mod.payload_badchars)313output << indent + "Avoid: " + mod.payload_badchars.length.to_s + " characters\n"314end315output << "\n"316end317318# Description319output << dump_description(mod, indent)320321# References322output << dump_references(mod, indent)323324# Notes325output << dump_notes(mod, indent)326327return output328329end330331# Dumps information about an auxiliary module.332#333# @param mod [Msf::Auxiliary] the auxiliary module.334# @param indent [String] the indentation to use.335# @return [String] the string form of the information.336def self.dump_auxiliary_module(mod, indent = '')337output = "\n"338output << " Name: #{mod.name}\n"339output << " Module: #{mod.fullname}\n"340output << " License: #{mod.license}\n"341output << " Rank: #{mod.rank_to_s.capitalize}\n"342output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date343output << "\n"344345# Authors346output << "Provided by:\n"347mod.each_author { |author|348output << indent + author.to_s + "\n"349}350output << "\n"351352output << dump_traits(mod)353354# Actions355if mod.actions.any?356output << "Available actions:\n"357output << dump_module_actions(mod)358end359360# Check361has_check = mod.has_check?362output << "Check supported:\n"363output << "#{indent}#{has_check ? 'Yes' : 'No'}\n\n"364365# Options366if (mod.options.has_options?)367output << "Basic options:\n"368output << dump_options(mod, indent)369output << "\n"370end371372# Description373output << dump_description(mod, indent)374375# References376output << dump_references(mod, indent)377378# Notes379output << dump_notes(mod, indent)380381return output382end383384# Dumps information about a post module.385#386# @param mod [Msf::Post] the post module.387# @param indent [String] the indentation to use.388# @return [String] the string form of the information.389def self.dump_post_module(mod, indent = '')390output = "\n"391output << " Name: #{mod.name}\n"392output << " Module: #{mod.fullname}\n"393output << " Platform: #{mod.platform_to_s}\n"394output << " Arch: #{mod.arch_to_s}\n"395output << " Rank: #{mod.rank_to_s.capitalize}\n"396output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date397output << "\n"398399# Authors400output << "Provided by:\n"401mod.each_author.each do |author|402output << indent + author.to_s + "\n"403end404output << "\n"405406output << dump_traits(mod)407408# Compatible session types409if mod.session_types410output << "Compatible session types:\n"411mod.session_types.sort.each do |type|412output << indent + type.capitalize + "\n"413end414output << "\n"415end416417# Actions418if mod.actions.any?419output << "Available actions:\n"420output << dump_module_actions(mod)421end422423# Options424if (mod.options.has_options?)425output << "Basic options:\n"426output << dump_options(mod, indent)427output << "\n"428end429430# Description431output << dump_description(mod, indent)432433# References434output << dump_references(mod, indent)435436# Notes437output << dump_notes(mod, indent)438439return output440end441442# Dumps information about an evasion module.443#444# @param mod [Msf::Evasion] The evasion module instance.445# @param indent [String] The indentation to use.446# @return [String] The string form of the information447def self.dump_evasion_module(mod, indent = '')448output = "\n"449output << " Name: #{mod.name}\n"450output << " Module: #{mod.fullname}\n"451output << " Platform: #{mod.platform_to_s}\n"452output << " Arch: #{mod.arch_to_s}\n"453output << " Privileged: " + (mod.privileged? ? "Yes" : "No") + "\n"454output << " License: #{mod.license}\n"455output << " Rank: #{mod.rank_to_s.capitalize}\n"456output << " Disclosed: #{mod.disclosure_date}\n" if mod.disclosure_date457output << "\n"458459# Authors460output << "Provided by:\n"461mod.each_author { |author|462output << indent + author.to_s + "\n"463}464output << "\n"465466# Check467output << "Check supported:\n"468output << "#{indent}#{mod.has_check? ? 'Yes' : 'No'}\n\n"469470# Options471if (mod.options.has_options?)472output << "Basic options:\n"473output << dump_options(mod, indent)474output << "\n"475end476477# Description478output << dump_description(mod, indent)479480# References481output << dump_references(mod, indent)482483return output484end485486# Dumps information about a payload module.487#488# @param mod [Msf::Payload] the payload module.489# @param indent [String] the indentation to use.490# @return [String] the string form of the information.491def self.dump_payload_module(mod, indent = '')492# General493output = "\n"494output << " Name: #{mod.name}\n"495output << " Module: #{mod.fullname}\n"496output << " Platform: #{mod.platform_to_s}\n"497output << " Arch: #{mod.arch_to_s}\n"498output << "Needs Admin: " + (mod.privileged? ? "Yes" : "No") + "\n"499output << " Total size: #{mod.size}\n"500output << " Rank: #{mod.rank_to_s.capitalize}\n"501output << "\n"502503# Authors504output << "Provided by:\n"505mod.each_author { |author|506output << indent + author.to_s + "\n"507}508output << "\n"509510# Options511if (mod.options.has_options?)512output << "Basic options:\n"513output << dump_options(mod)514output << "\n"515end516517# Description518output << dump_description(mod, indent)519output << "\n"520521return output522end523524# Dumps information about a module, just the basics.525#526# @param mod [Msf::Module] the module.527# @param indent [String] the indentation to use.528# @return [String] the string form of the information.529def self.dump_basic_module(mod, indent = '')530# General531output = "\n"532output << " Name: #{mod.name}\n"533output << " Module: #{mod.fullname}\n"534output << " Platform: #{mod.platform_to_s}\n"535output << " Arch: #{mod.arch_to_s}\n"536output << " Rank: #{mod.rank_to_s.capitalize}\n"537output << "\n"538539# Authors540output << "Provided by:\n"541mod.each_author { |author|542output << indent + author.to_s + "\n"543}544output << "\n"545546output << dump_traits(mod)547548# Description549output << dump_description(mod, indent)550551output << dump_references(mod, indent)552553output << "\n"554555return output556557end558559#No current use560def self.dump_generic_module(mod, indent = '')561end562563# Dumps the list of options associated with the564# supplied module.565#566# @param mod [Msf::Module] the module.567# @param indent [String] the indentation to use.568# @param missing [Boolean] dump only empty required options.569# @return [String] the string form of the information.570def self.dump_options(mod, indent = '', missing = false, advanced: false, evasion: false)571filtered_options = mod.options.select { |_name, opt| opt.advanced? == advanced && opt.evasion? == evasion }572573option_groups = mod.options.groups.values.select { |group| group.option_names.any? { |name| filtered_options.keys.include?(name) } }574options_by_group = option_groups.map do |group|575[group, group.option_names.map { |name| filtered_options[name] }.compact]576end.to_h577grouped_option_names = option_groups.flat_map(&:option_names)578remaining_options = filtered_options.reject { |_name, option| grouped_option_names.include?(option.name) }579options_grouped_by_conditions = remaining_options.values.group_by(&:conditions)580581option_tables = []582583sort_by_empty_then_lexicographical = proc do |(conditions_a, _options_a), (conditions_b, _options_b)|584next -1 if conditions_a.empty?585next 1 if conditions_b.empty?586conditions_a.to_s <=> conditions_b.to_s587end588589options_grouped_by_conditions.sort(&sort_by_empty_then_lexicographical).each do |conditions, options|590tbl = options_table(missing, mod, options, indent)591592next if tbl.rows.empty?593594if conditions.any?595option_tables << "#{indent}When #{Msf::OptCondition.format_conditions(mod, options.first)}:\n\n#{tbl}"596else597option_tables << tbl.to_s598end599end600601options_by_group.each do |group, options|602tbl = options_table(missing, mod, options, indent)603option_tables << "#{indent}#{group.description}:\n\n#{tbl}"604end605606result = option_tables.join("\n\n")607result608end609610# Creates the table for the given module options611#612# @param missing [Boolean] dump only empty required options.613# @param mod [Msf::Module] the module.614# @param options [Array<Msf::OptBase>] The options to be added to the table615# @param indent [String] the indentation to use.616#617# @return [String] the string form of the table.618def self.options_table(missing, mod, options, indent)619tbl = Rex::Text::Table.new(620'Indent' => indent.length,621'Columns' =>622[623'Name',624'Current Setting',625'Required',626'Description'627]628)629options.sort_by(&:name).each do |opt|630name = opt.name631if mod.datastore.is_a?(Msf::DataStore)632val = mod.datastore[name]633else634val = mod.datastore[name].nil? ? opt.default : mod.datastore[name]635end636next if (missing && opt.valid?(val))637638desc = opt.desc.dup639640# Hint at RPORT proto by regexing mixins641if name == 'RPORT' && opt.kind_of?(Msf::OptPort)642mod.class.included_modules.each do |m|643case m.name644when /tcp/i, /HttpClient$/645desc << ' (TCP)'646break647when /udp/i648desc << ' (UDP)'649break650end651end652end653654tbl << [name, opt.display_value(val), opt.required? ? "yes" : "no", desc]655end656tbl657end658659# Dumps the advanced options associated with the supplied module.660#661# @param mod [Msf::Module] the module.662# @param indent [String] the indentation to use.663# @return [String] the string form of the information.664def self.dump_advanced_options(mod, indent = '')665return dump_options(mod, indent, advanced: true)666end667668# Dumps the evasion options associated with the supplied module.669#670# @param mod [Msf::Module] the module.671# @param indent [String] the indentation to use.672# @return [String] the string form of the information.673def self.dump_evasion_options(mod, indent = '')674return dump_options(mod, indent, evasion: true)675end676677# Dumps the references associated with the supplied module.678#679# @param mod [Msf::Module] the module.680# @param indent [String] the indentation to use.681# @return [String] the string form of the information.682def self.dump_references(mod, indent = '')683output = ''684685if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)686output << "References:\n"687688mod.references.each do |ref|689case ref.ctx_id690when 'LOGO', 'SOUNDTRACK'691output << indent + ref.to_s + "\n"692Rex::Compat.open_browser(ref.ctx_val) if Rex::Compat.getenv('FUEL_THE_HYPE_MACHINE')693else694output << indent + ref.to_s + "\n"695end696end697698output << "\n"699end700701output702end703704# Dumps the notes associated with the supplied module.705#706# @param mod [Msf::Module] the module.707# @param indent [String] the indentation to use.708# @return [String] the string form of the information.709def self.dump_notes(mod, indent = '')710output = ''711712mod.notes.each do |name, val|713next unless val.present?714715case name716when 'AKA'717output << "Also known as:\n"718val.each { |aka| output << "#{indent}#{aka}\n" }719when 'NOCVE'720output << "CVE not available for the following reason:\n" \721"#{indent}#{val}\n"722when 'RelatedModules'723output << "Related modules:\n"724val.each { |related| output << "#{indent}#{related}\n" }725when 'Stability', 'SideEffects', 'Reliability'726# Handled by dump_traits727next728else729output << "#{name}:\n"730731case val732when Array733val.each { |v| output << "#{indent}#{v}\n" }734when Hash735val.each { |k, v| output << "#{indent}#{k}: #{v}\n" }736else737# Display the raw note738output << "#{indent}#{val}\n"739end740end741742output << "\n"743end744745output746end747748# Dumps the contents of a datastore.749#750# @param name [String] displayed as the table header.751# @param ds [Msf::DataStore] the DataStore to dump.752# @param indent [Integer] the indentation size.753# @param col [Integer] the column width.754# @return [String] the formatted DataStore contents.755def self.dump_datastore(name, ds, indent = DefaultIndent, col = DefaultColumnWrap)756tbl = Rex::Text::Table.new(757'Indent' => indent,758'Header' => name,759'Columns' =>760[761'Name',762'Value'763])764765ds.keys.sort.each { |k|766tbl << [ k, (ds[k] != nil) ? ds[k].to_s : '' ]767}768769return ds.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No entries in data store.\n"770end771772# Dumps the list of sessions.773#774# @param framework [Msf::Framework] the framework to dump.775# @param opts [Hash] the options to dump with.776# @option opts :verbose [Boolean] gives more information if set to777# true.778# @option opts :indent [Integer] set the indentation amount.779# @return [String] the formatted list of sessions.780def self.dump_sessions(framework, opts={})781output = ""782verbose = opts[:verbose] || false783sessions = opts[:sessions] || framework.sessions784show_active = opts[:show_active] || false785show_inactive = opts[:show_inactive] || false786# if show_active and show_inactive are false the caller didn't787# specify either flag; default to displaying active sessions788show_active = true if !(show_active || show_inactive)789show_extended = opts[:show_extended] || false790indent = opts[:indent] || DefaultIndent791792return dump_sessions_verbose(framework, opts) if verbose793794if show_active795columns = []796columns << 'Id'797columns << 'Name'798columns << 'Type'799columns << 'Checkin?' if show_extended800columns << 'Enc?' if show_extended801columns << 'Local URI' if show_extended802columns << 'Information'803columns << 'Connection'804805tbl = Rex::Text::Table.new(806'Header' => "Active sessions",807'Columns' => columns,808'Indent' => indent)809810sessions.each do |session_id, session|811row = create_msf_session_row(session, show_extended)812tbl << row813end814815output << (tbl.rows.count > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n")816end817818if show_inactive819output << "\n" if show_active820821columns = []822columns << 'Closed'823columns << 'Opened'824columns << 'Reason Closed'825columns << 'Type'826columns << 'Address'827828tbl = Rex::Text::Table.new(829'Header' => "Inactive sessions",830'Columns' => columns,831'Indent' => indent,832'SortIndex' => 1)833834if framework.db.active835framework.db.sessions.each do |session|836unless session.closed_at.nil?837row = create_mdm_session_row(session, show_extended)838tbl << row839end840end841end842843output << (tbl.rows.count > 0 ? tbl.to_s : "#{tbl.header_to_s}No inactive sessions.\n")844end845846# return formatted listing of sessions847output848end849850# Creates a table row that represents the specified session.851#852# @param session [Msf::Session] session used to create a table row.853# @param show_extended [Boolean] Indicates if extended information will be included in the row.854# @return [Array] table row of session data.855def self.create_msf_session_row(session, show_extended)856row = []857row << session.sid.to_s858row << session.sname.to_s859row << session.type.to_s860if session.respond_to?(:session_type)861row[-1] << " #{session.session_type}"862elsif session.respond_to?(:platform)863row[-1] << " #{session.platform}"864end865866if show_extended867if session.respond_to?(:last_checkin) && session.last_checkin868row << "#{(Time.now.to_i - session.last_checkin.to_i)}s ago"869else870row << '?'871end872873if session.respond_to?(:tlv_enc_key) && session.tlv_enc_key && session.tlv_enc_key[:key]874row << 'Y'875else876row << 'N'877end878879if session.exploit_datastore && session.exploit_datastore.has_key?('LURI') && !session.exploit_datastore['LURI'].empty?880row << "(#{session.exploit_datastore['LURI']})"881else882row << '?'883end884end885886sinfo = session.info.to_s887sinfo = sinfo.gsub(/[\r\n\t]+/, ' ')888# Arbitrarily cut info at 80 columns889if sinfo.length > 80890sinfo = "#{sinfo[0,77]}..."891end892row << sinfo893894row << "#{session.tunnel_to_s} (#{session.session_host})"895896# return complete row897row898end899900# Creates a table row that represents the specified session.901#902# @param session [Mdm::Session] session used to create a table row.903# @param show_extended [Boolean] Indicates if extended information will be included in the row.904# @return [Array] table row of session data.905def self.create_mdm_session_row(session, show_extended)906row = []907row << session.closed_at.to_s908row << session.opened_at.to_s909row << session.close_reason910row << session.stype911if session.respond_to?(:platform) && !session.platform.nil?912row[-1] << " #{session.platform}"913end914row << (!session.host.nil? ? session.host.address : nil)915916# return complete row917row918end919920# Dumps the list of active sessions in verbose mode921#922# @param framework [Msf::Framework] the framework to dump.923# @param opts [Hash] the options to dump with.924# @return [String] the formatted list of sessions.925def self.dump_sessions_verbose(framework, opts={})926out = "Active sessions\n" +927"===============\n\n"928929if framework.sessions.length == 0930out << "No active sessions.\n"931return out932end933934sessions = opts[:sessions] || framework.sessions935936sessions.each do |session_id, session|937sess_info = session.info.to_s938sess_id = session.sid.to_s939sess_name = session.sname.to_s940sess_tunnel = session.tunnel_to_s + " (#{session.session_host})"941sess_via = session.via_exploit.to_s942sess_type = session.type.to_s943sess_uuid = session.payload_uuid.to_s944sess_luri = session.exploit_datastore['LURI'] || "" if session.exploit_datastore945sess_enc = 'No'946if session.respond_to?(:tlv_enc_key) && session.tlv_enc_key && session.tlv_enc_key[:key]947sess_enc = "Yes (AES-#{session.tlv_enc_key[:key].length * 8}-CBC)"948end949950sess_checkin = "<none>"951sess_registration = "No"952953if session.respond_to?(:platform) && session.platform954sess_type << " #{session.platform}"955end956957if session.respond_to?(:last_checkin) && session.last_checkin958sess_checkin = "#{(Time.now.to_i - session.last_checkin.to_i)}s ago @ #{session.last_checkin.to_s}"959end960961if !session.payload_uuid.nil? && session.payload_uuid.registered962sess_registration = "Yes"963if session.payload_uuid.name964sess_registration << " - Name=\"#{session.payload_uuid.name}\""965end966end967968out << " Session ID: #{sess_id}\n"969out << " Name: #{sess_name}\n"970out << " Type: #{sess_type}\n"971out << " Info: #{sess_info}\n"972out << " Tunnel: #{sess_tunnel}\n"973out << " Via: #{sess_via}\n"974out << " Encrypted: #{sess_enc}\n"975out << " UUID: #{sess_uuid}\n"976out << " CheckIn: #{sess_checkin}\n"977out << " Registered: #{sess_registration}\n"978unless (sess_luri || '').empty?979out << " LURI: #{sess_luri}\n"980end981982out << "\n"983end984985out << "\n"986return out987end988989# Dumps the list of running jobs.990#991# @param framework [Msf::Framework] the framework.992# @param verbose [Boolean] if true, also prints the payload, LPORT, URIPATH993# and start time, if they exist, for each job.994# @param indent [Integer] the indentation amount.995# @param col [Integer] the column wrap width.996# @return [String] the formatted list of running jobs.997def self.dump_jobs(framework, verbose = false, indent = DefaultIndent, col = DefaultColumnWrap)998columns = [ 'Id', 'Name', "Payload", "Payload opts"]9991000if (verbose)1001columns += [ "URIPATH", "Start Time", "Handler opts", "Persist" ]1002end10031004tbl = Rex::Text::Table.new(1005'Indent' => indent,1006'Header' => "Jobs",1007'Columns' => columns1008)10091010# Get the persistent job info.1011if verbose1012begin1013persist_list = JSON.parse(File.read(Msf::Config.persist_file))1014rescue Errno::ENOENT, JSON::ParserError1015persist_list = []1016end1017end10181019# jobs are stored as a hash with the keys being a numeric String job_id.1020framework.jobs.keys.sort_by(&:to_i).each do |job_id|1021# Job context is stored as an Array with the 0th element being1022# the running module. If that module is an exploit, ctx will also1023# contain its payload.1024exploit_mod, _payload_mod = framework.jobs[job_id].ctx1025row = []1026row[0] = job_id1027row[1] = framework.jobs[job_id].name10281029pinst = exploit_mod.respond_to?(:payload_instance) ? exploit_mod.payload_instance : nil1030payload_uri = ''10311032if pinst.nil?1033row[2] = ""1034row[3] = ""1035else1036row[2] = pinst.refname1037row[3] = ""1038if pinst.respond_to?(:payload_uri)1039payload_uri = pinst.payload_uri.strip1040row[3] << payload_uri1041end1042if pinst.respond_to?(:luri)1043row[3] << pinst.luri1044end1045if pinst.respond_to?(:comm_string)1046via = pinst.comm_string1047if via1048row[3] << " #{via}"1049end1050end1051end10521053if verbose1054uripath = exploit_mod.get_resource if exploit_mod.respond_to?(:get_resource)1055uripath ||= exploit_mod.datastore['URIPATH']1056row[4] = uripath1057row[5] = framework.jobs[job_id].start_time1058row[6] = ''1059row[7] = 'false'10601061if pinst.respond_to?(:listener_uri)1062listener_uri = pinst.listener_uri.strip1063row[6] = listener_uri unless listener_uri == payload_uri1064end10651066persist_list.each do |e|1067handler_ctx = framework.jobs[job_id.to_s].ctx[1]1068if handler_ctx && handler_ctx.respond_to?(:datastore)1069row[7] = 'true' if e['mod_options']['Options'] == handler_ctx.datastore.to_h1070end1071end10721073end1074tbl << row1075end10761077return framework.jobs.keys.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active jobs.\n"1078end10791080# Dumps the module description1081#1082# @param mod [Msf::Module] the module.1083# @param indent [String] the indentation string1084# @return [String] the string description1085def self.dump_description(mod, indent)1086description = mod.description10871088output = "Description:\n"1089output << word_wrap_description(description, indent)1090output << "\n\n"1091end10921093# @param str [String] the string to wrap.1094# @param indent [String] the indentation string1095# @return [String] the wrapped string.1096def self.word_wrap_description(str, indent = '')1097return '' if str.blank?10981099str_lines = str.strip.lines(chomp: true)1100# Calculate the preceding whitespace length of each line1101smallest_preceding_whitespace = nil1102str_lines[1..].to_a.each do |line|1103preceding_whitespace = line[/^\s+/]1104if preceding_whitespace && (smallest_preceding_whitespace.nil? || preceding_whitespace.length < smallest_preceding_whitespace)1105smallest_preceding_whitespace = preceding_whitespace.length1106end1107end11081109# Normalize any existing left-most whitespace on each line; Ignoring the first line which won't have any preceding whitespace1110result = str_lines.map.with_index do |line, index|1111next if line.blank?11121113"#{indent}#{index == 0 || smallest_preceding_whitespace.nil? ? line : line[smallest_preceding_whitespace..]}"1114end.join("\n")11151116result1117end1118end11191120end end112111221123