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/debug.rb
Views: 11780
# -*- coding: binary -*-1# frozen_string_literal: true23module Msf4module Ui5###6#7# Displays Metasploit information useful for Debugging.8#9###10module Debug11COMMAND_HISTORY_TOTAL = 5012FRAMEWORK_LOG_LINE_TOTAL = 5013WEB_SERVICE_LOG_LINE_TOTAL = 1501415# "[mm/dd/yyyy hh:mm:ss] [e([ANY_NUMBER])]" Indicates the start of an error message16# The end of an error message is indicated by the start of the next log message [mm/dd/yyyy hh:mm:ss] [[ANY_LETTER]([ANY_NUMBER])]17#18#19# When using the commented regex, the below example framework.log will only return three separate errors, and their accompanying traces:20#21# [05/15/2020 14:13:38] [e(0)] core: [-] Error during IRB: undefined method `[]' for nil:NilClass22#23# [06/19/2020 12:05:02] [i(0)] core: Trying to continue despite failed database creation: could not connect to server: Connection refused24# Is the server running on host "127.0.0.1" and accepting25# TCP/IP connections on port 5433?26#27# [05/15/2020 14:19:20] [e(0)] core: [-] Error while running command debug: can't modify frozen String28# Call stack:29# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/ui/debug.rb:33:in `get_all'30# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/ui/console/command_dispatcher/core.rb:318:in `cmd_debug'31# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:523:in `run_command'32# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:474:in `block in run_single'33# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:468:in `each'34# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:468:in `run_single'35# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/shell.rb:158:in `run'36# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/metasploit/framework/command/console.rb:48:in `start'37# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/metasploit/framework/command/base.rb:82:in `start'38#39# [06/19/2020 11:51:44] [d(2)] core: Stager osx/armle/reverse_tcp and stage osx/x64/meterpreter have incompatible architectures: armle - x6440#41# [05/15/2020 14:23:55] [e(0)] core: [-] Error during IRB: undefined method `[]' for nil:NilClass42FRAMEWORK_ERROR_REGEX = %r|\[\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}\] \[e\(\d+\)\] (?:(?!\[\d{2}/\d{2}/\d{4} \d{2}:\d{2}:\d{2}\] \[[A-Za-z]\(\d+\)\]).)+|m43FRAMEWORK_ERROR_TOTAL = 104445# "[-]" Indicates the start of an error message46# The end of an error message is indicated by a \n character followed by any non-whitespace character47#48# When using the commented regex, the below example msf-ws.log will only return three separate errors, and their accompanying traces:49#50# [-] Error that does not return a stack trace.51# Writing PID to /Users/agalway/.msf4/msf-ws.pid52# Thin web server (v1.7.2 codename Bachmanity)53# Maximum connections set to 102454# Listening on localhost:5443, CTRL+C to stop55#56#57# [-] Error handling request: wrong number of arguments (given 4, expected 1).58# Call Stack:59# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/db_manager/service.rb:44:in `get_service'60# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/db_manager/note.rb:136:in `block in report_note'61# /Users/agalway/vendor/bundle/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:416:in `with_connection'62# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/db_manager/note.rb:81:in `report_note'63# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/web_services/servlet/note_servlet.rb:42:in `block (2 levels) in report_note'64# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/web_services/servlet_helper.rb:78:in `exec_report_job'65# /Users/agalway/vendor/bundle/gems/thin-1.7.2/bin/thin:6:in `<top (required)>'66# /Users/agalway/vendor/bundle/bin/thin:23:in `load'67# /Users/agalway/vendor/bundle/bin/thin:23:in `<main>'68# [-] Error handling request: wrong number of arguments (given 4, expected 1).69# Call Stack:70# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/db_manager/service.rb:44:in `get_service'71# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/core/db_manager/note.rb:136:in `block in report_note'72# /Users/agalway/vendor/bundle/gems/activerecord-5.2.4.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:416:in `with_connection'73# /Users/agalway/vendor/bundle/gems/thin-1.7.2/bin/thin:6:in `<top (required)>'74# /Users/agalway/vendor/bundle/bin/thin:23:in `load'75# /Users/agalway/vendor/bundle/bin/thin:23:in `<main>'76WEB_SERVICE_ERROR_REGEX = %r|\[-\].+?\n(?!\s)|m77WEB_SERVICE_ERROR_TOTAL = 107879ISSUE_LINK = 'https://github.com/rapid7/metasploit-framework/issues/new/choose'80PREAMBLE = <<~PREMABLE81Please provide the below information in any Github issues you open. New issues can be opened here #{ISSUE_LINK.dup}82%red%undENSURE YOU HAVE REMOVED ANY SENSITIVE INFORMATION BEFORE SUBMITTING!%clr8384===8<=== CUT AND PASTE EVERYTHING BELOW THIS LINE ===8<===858687PREMABLE8889ERROR_BLURB = 'An error occurred when trying to build this section:'9091def self.issue_link92return ISSUE_LINK.dup93end9495def self.preamble96return PREAMBLE.dup97end9899def self.all(framework, driver)100all_information = preamble101all_information << datastore(framework, driver)102all_information << database_configuration(framework)103all_information << framework_config(framework)104all_information << history(driver)105all_information << errors106all_information << logs107all_information << versions(framework)108109all_information110end111112def self.datastore(framework, driver)113114# Generate an ini with the existing config file115ini = Rex::Parser::Ini.new(Msf::Config.config_file)116117# Delete all groups from the config ini that potentially have more up to date information118ini.keys.each do |key|119unless key.start_with?("framework/database") || key.start_with?("framework/features")120ini.delete(key)121end122end123124# Retrieve and add more up to date information125add_hash_to_ini_group(ini, framework.datastore.to_h, driver.get_config_core)126add_hash_to_ini_group(ini, driver.get_config, driver.get_config_group)127128if driver.active_module129add_hash_to_ini_group(ini, driver.active_module.datastore.to_h, driver.active_module.refname)130end131132# Filter credentials133ini.each do |key, value|134if key =~ %r{^framework/database/}135value.transform_values! { '[Filtered]' }136end137end138139if ini.to_s.empty?140content = 'The local config file is empty, no global variables are set, and there is no active module.'141else142content = ini.to_s143end144145build_section(146'Module/Datastore',147'The following global/module datastore, and database setup was configured before the issue occurred:',148content149)150rescue StandardError => e151build_section(152'Module/Datastore',153ERROR_BLURB,154section_build_error('Failed to extract Datastore', e)155)156end157158def self.database_configuration(framework)159output = "```\nSession Type: #{db_connection_info(framework)}\n```\n\n"160161if framework.db&.active162current_workspace = framework.db.workspace163example_workspaces = ::Mdm::Workspace.order(id: :desc).take(10)164ordered_workspaces = ([current_workspace] + example_workspaces).uniq.sort_by(&:id)165workspace_rows = ordered_workspaces.map do |workspace|166id = current_workspace.id == workspace.id ? "#{workspace.id.to_fs(:delimited)} **(Current)**" : workspace.id.to_fs(:delimited)167[168id,169workspace.hosts.count.to_fs(:delimited),170workspace.vulns.count.to_fs(:delimited),171workspace.notes.count.to_fs(:delimited),172workspace.services.count.to_fs(:delimited)173]174end175176totals_row = [177"**Total (#{::Mdm::Workspace.count.to_fs(:delimited)})**",178"**#{::Mdm::Host.count.to_fs(:delimited)}**",179"**#{::Mdm::Vuln.count.to_fs(:delimited)}**",180"**#{::Mdm::Note.count.to_fs(:delimited)}**",181"**#{::Mdm::Service.count.to_fs(:delimited)}**"182]183184table = "| ID | Hosts | Vulnerabilities | Notes | Services |\n"185table += "|-:|-:|-:|-:|-:|\n"186table += (workspace_rows + [totals_row]).map { |x| "| #{x.join(" | ")} |" }.join("\n")187output += table188end189190# The markdown table can't be placed in a code block or it will not render as a table.191build_section_no_block(192'Database Configuration',193'The database contains the following information:',194output195)196rescue StandardError => e197build_section(198'Database Configuration',199ERROR_BLURB,200section_build_error('Failed to extract Database configuration', e)201)202end203204def self.framework_config(framework)205required_features = framework.features.all.map { |feature| [feature[:name], feature[:enabled].to_s] }206markdown_formatted_features = required_features.map { |feature| "| #{feature.join(' | ')} |" }207required_fields = %w[name enabled]208209table = "| #{required_fields.join(' | ')} |\n"210table += '|' + '-:|' * required_fields.count + "\n"211table += markdown_formatted_features.join("\n").to_s212213# The markdown table can't be placed in a code block or it will not render as a table.214build_section_no_block(215'Framework Configuration',216'The features are configured as follows:',217table218)219end220221def self.history(driver)222end_pos = Readline::HISTORY.length - 1223start_pos = end_pos - COMMAND_HISTORY_TOTAL > driver.hist_last_saved ? end_pos - (COMMAND_HISTORY_TOTAL - 1) : driver.hist_last_saved224225commands = ''226while start_pos <= end_pos227# Formats command position in history to 6 characters in length228commands += "#{'%-6.6s' % start_pos.to_s} #{Readline::HISTORY[start_pos]}\n"229start_pos += 1230end231232build_section(233'History',234'The following commands were ran during the session and before this issue occurred:',235commands236)237rescue StandardError => e238build_section(239'History',240ERROR_BLURB,241section_build_error('Failed to extract History', e)242)243end244245def self.errors246errors = build_regex_file_section(Pathname.new(Msf::Config.log_directory).join('framework.log'),247FRAMEWORK_ERROR_TOTAL,248FRAMEWORK_ERROR_REGEX,249'Framework Errors',250'The following framework errors occurred before the issue occurred:')251252errors += build_regex_file_section(Pathname.new(Msf::Config.log_directory).join('msf-ws.log'),253WEB_SERVICE_ERROR_TOTAL,254WEB_SERVICE_ERROR_REGEX,255'Web Service Errors',256'The following web service errors occurred before the issue occurred:')257errors258end259260def self.logs261logs = build_file_section(Pathname.new(Msf::Config.log_directory).join('framework.log'),262FRAMEWORK_LOG_LINE_TOTAL,263'Framework Logs',264'The following framework logs were recorded before the issue occurred:')265266logs += build_file_section(Pathname.new(Msf::Config.log_directory).join('msf-ws.log'),267WEB_SERVICE_LOG_LINE_TOTAL,268'Web Service Logs',269'The following web service logs were recorded before the issue occurred:')270logs271end272273def self.versions(framework)274275str = <<~VERSIONS276Framework: #{framework.version}277Ruby: #{RUBY_DESCRIPTION}278OpenSSL: #{OpenSSL::OPENSSL_VERSION}279Install Root: #{Msf::Config.install_root}280Session Type: #{db_connection_info(framework)}281Install Method: #{installation_method}282VERSIONS283284build_section('Version/Install', 'The versions and install method of your Metasploit setup:', str)285rescue StandardError => e286build_section(287'Version/Install',288ERROR_BLURB,289section_build_error('Failed to extract Versions', e)290)291end292293class << self294295private296297def build_regex_file_section(path, match_total, regex, header_name, blurb)298unless File.file?(path)299return build_section(300header_name,301blurb,302"#{path.basename.to_s} does not exist."303)304end305306file_contents = File.read(path)307matches = file_contents.scan(regex)308309if matches.empty?310return build_section(311header_name,312blurb,313"No matching patterns were found in #{path.basename}."314)315end316317# +.scan+ can sometimes return each match as a single item array318matches.flatten!319320# create a string consisting of the last +match_total+ matches321# if +matches.length+ < +match_total+ then concat all matches322str = concat_str_array_from_last_idx(matches, match_total)323324build_section(325header_name,326blurb,327str328)329rescue StandardError => e330build_section(331header_name,332ERROR_BLURB,333section_build_error("Failed to extract matches from #{path.basename}", e)334)335end336337def build_file_section(path, line_total, header_name, blurb)338unless File.file?(path)339return build_section(340header_name,341blurb,342"#{path.basename.to_s} does not exist."343)344end345346log_lines = File.readlines(path)347348# create a string consisting of the last +line_total+ lines349# if +log_lines.length+ < +line_total+ then concat all lines350str = concat_str_array_from_last_idx(log_lines, line_total)351352build_section(353header_name,354blurb,355str356)357rescue StandardError => e358build_section(359header_name,360ERROR_BLURB,361section_build_error("Failed to extract contents of #{path.basename.to_s}", e)362)363end364365def add_hash_to_ini_group(ini, hash, group_name)366if hash.empty?367return368end369370unless ini.group?(group_name)371ini.add_group(group_name)372end373374hash.each_pair do |k, v|375ini[group_name][k] = v376end377end378379def concat_str_array_from_last_idx(array, concat_total)380start_pos = array.length > concat_total ? array.length - concat_total : 0381end_pos = array.length - 1382383str = array[start_pos..end_pos].join('')384385str.strip386end387388# Copy pasta of the print_connection_info method in console/command_dispatcher/db.rb389def db_connection_info(framework)390unless framework.db.connection_established?391return "#{framework.db.driver} selected, no connection"392end393394cdb = ''395if framework.db.driver == 'http'396cdb = framework.db.name397else398::ApplicationRecord.connection_pool.with_connection do |conn|399if conn.respond_to?(:current_database)400cdb = conn.current_database401end402end403end404405if cdb.empty?406output = "Connected Database Name could not be extracted. DB Connection type: #{framework.db.driver}."407else408output = "Connected to #{cdb}. Connection type: #{framework.db.driver}."409end410411output412end413414def build_section(header_name, blurb, content)415<<~SECTION416## %grn#{header_name.strip}%clr417418#{blurb.strip}419#{with_collapsible_wrapper(content.strip)}420421SECTION422end423424def with_collapsible_wrapper(content)425<<~WRAPPER426<details>427<summary>Collapse</summary>428429```430#{content}431```432433</details>434WRAPPER435end436437def with_collapsible_wrapper_no_block(content)438<<~WRAPPER439<details>440<summary>Collapse</summary>441442#{content}443444</details>445WRAPPER446end447448# Useful for building tables or other content that can't be placed inside a code block.449def build_section_no_block(header_name, blurb, content)450<<~SECTION451## %grn#{header_name.strip}%clr452453#{blurb.strip}454#{with_collapsible_wrapper_no_block(content.strip)}455456SECTION457end458459def installation_method460if File.exist?(File.join(Msf::Config.install_root, 'version.yml'))461'Omnibus Installer'462elsif File.directory?(File.join(Msf::Config.install_root, '.git'))463'Git Clone'464else465'Other - Please specify'466end467end468469def section_build_error(msg, error)470"#{msg}: #{error.class} - #{error.message} \n Call stack:\n#{error.backtrace.join("\n")}"471end472end473end474end475end476477478