adds debug command with spec tests

This commit is contained in:
Adam Galway 2020-05-11 10:22:20 +01:00
parent 39a5c6aa37
commit babaee8c8e
No known key found for this signature in database
GPG Key ID: EC5AF4B99AB94D70
16 changed files with 1437 additions and 6 deletions

View File

@ -7,6 +7,7 @@ end
require 'rex/ui'
require 'msf/ui/banner'
require 'msf/ui/tip'
require 'msf/ui/debug'
require 'msf/ui/driver'
require 'msf/ui/common'
require 'msf/ui/console'

View File

@ -72,6 +72,14 @@ class Core
@@tip_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ])
@@debug_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-d" => [ false, "Display the Datastore Information." ],
"-c" => [ false, "Display command history." ],
"-e" => [ false, "Display the most recent Error and Stack Trace." ],
"-l" => [ false, "Display the most recent logs." ],
"-v" => [ false, "Display versions and install info." ])
@@connect_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-p" => [ true, "List of proxies to use." ],
@ -103,6 +111,7 @@ class Core
"cd" => "Change the current working directory",
"connect" => "Communicate with a host",
"color" => "Toggle color",
"debug" => "Display information useful for debugging",
"exit" => "Exit the console",
"get" => "Gets the value of a context-specific variable",
"getg" => "Gets the value of a global variable",
@ -291,6 +300,54 @@ class Core
alias cmd_tip cmd_tips
def cmd_debug_help
print_line "Usage: debug [options]"
print_line
print_line("Print a set of information in a Markdown format to be included when opening an Issue on Github. " +
"This information helps us fix problems you encounter and should be included when you open a new issue: " +
Debug.issue_link)
print @@debug_opts.usage
end
#
# Display information useful for debugging errors.
#
def cmd_debug(*args)
if args.empty?
print_line Debug.all(framework, driver)
return
end
if args.include?("-h")
cmd_debug_help
else
output = ""
@@debug_opts.parse(args) do |opt|
case opt
when '-d'
output << Debug.datastore(framework, driver)
when '-c'
output << Debug.history(driver)
when '-e'
output << Debug.errors
when '-l'
output << Debug.logs
when '-v'
output << Debug.versions(framework)
end
end
if output.empty?
print_line("Valid argument was not given.")
cmd_debug_help
else
output = Debug.preamble + output
print_line output
end
end
end
def cmd_connect_help
print_line "Usage: connect [options] <host> <port>"
print_line

View File

@ -285,9 +285,9 @@ class Driver < Msf::Ui::Driver
end
#
# Saves configuration for the console.
# Generate configuration for the console.
#
def save_config
def get_config
# Build out the console config group
group = {}
@ -301,9 +301,23 @@ class Driver < Msf::Ui::Driver
end
end
# Save it
group
end
def get_config_core
ConfigCore
end
def get_config_group
ConfigGroup
end
#
# Saves configuration for the console.
#
def save_config
begin
Msf::Config.save(ConfigGroup => group)
Msf::Config.save(ConfigGroup => get_config)
rescue ::Exception
print_error("Failed to save console config: #{$!}")
end

287
lib/msf/ui/debug.rb Normal file
View File

@ -0,0 +1,287 @@
# -*- coding: binary -*-
# frozen_string_literal: true
module Msf
module Ui
###
#
# Displays Metasploit information useful for Debugging.
#
###
module Debug
COMMAND_HISTORY_TOTAL = 50
ERROR_TOTAL = 10
LOG_LINE_TOTAL = 50
ISSUE_LINK = 'https://github.com/rapid7/metasploit-framework/issues/new/choose'
PREAMBLE = <<~PREMABLE
Please provide the below information in any Github issues you open. New issues can be opened here #{ISSUE_LINK.dup}
%red%undENSURE YOU HAVE REMOVED ANY SENSITIVE INFORMATION BEFORE SUBMITTING!%clr
===8<=== CUT AND PASTE EVERYTHING BELOW THIS LINE ===8<===
PREMABLE
def self.issue_link
return ISSUE_LINK.dup
end
def self.preamble
return PREAMBLE.dup
end
def self.all(framework, driver)
all_information = preamble
all_information << datastore(framework, driver)
all_information << history(driver)
all_information << errors
all_information << logs
all_information << versions(framework)
all_information
end
def self.datastore(framework, driver)
# Generate an ini with the existing config file
ini = Rex::Parser::Ini.new(Msf::Config.config_file)
# Delete all groups from the config ini that potentially have more up to date information
ini.keys.each do |key|
unless key =~ %r{^framework/database}
ini.delete(k)
end
end
# Retrieve and add more up to date information
add_hash_to_ini_group(ini, framework.datastore, driver.get_config_core)
add_hash_to_ini_group(ini, driver.get_config, driver.get_config_group)
if driver.active_module
add_hash_to_ini_group(ini, driver.active_module.datastore.dup, driver.active_module.refname)
end
# Filter credentials
ini.each do |key, value|
if key =~ %r{^framework/database/}
value.transform_values! { '[Filtered]' }
end
end
if ini.to_s.empty?
content = 'The local config file is empty, no global variables are set, and there is no active module.'
else
content = ini.to_s
end
build_section(
'Module/Datastore',
'The following global/module datastore, and database setup was configured before the issue occurred:',
content
)
rescue StandardError => e
section_build_error('Failed to extract Datastore', e)
end
def self.history(driver)
end_pos = Readline::HISTORY.length - 1
start_pos = end_pos - COMMAND_HISTORY_TOTAL > driver.hist_last_saved ? end_pos - (COMMAND_HISTORY_TOTAL - 1) : driver.hist_last_saved
commands = ''
while start_pos <= end_pos
# Formats command position in history to 6 characters in length
commands += "#{'%-6.6s' % start_pos.to_s} #{Readline::HISTORY[start_pos]}\n"
start_pos += 1
end
build_section(
'History',
'The following commands were ran during the session and before this issue occurred:',
commands
)
rescue StandardError => e
section_build_error('Failed to extract History', e)
end
def self.errors
errors = File.read(File.join(Msf::Config.log_directory, 'framework.log'))
# Returns any error logs in framework.log file as an array
# "[mm/dd/yyyy hh:mm:ss] [e([ANY_NUMBER])]" Indicates the start of an error message
# 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])]
#
#
# The below example framework.log will only return three separate errors, and their accompanying traces:
#
# [05/15/2020 14:13:38] [e(0)] core: [-] Error during IRB: undefined method `[]' for nil:NilClass
#
# [06/19/2020 12:05:02] [i(0)] core: Trying to continue despite failed database creation: could not connect to server: Connection refused
# Is the server running on host "127.0.0.1" and accepting
# TCP/IP connections on port 5433?
#
# [05/15/2020 14:19:20] [e(0)] core: [-] Error while running command debug: can't modify frozen String
# Call stack:
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/ui/debug.rb:33:in `get_all'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/msf/ui/console/command_dispatcher/core.rb:318:in `cmd_debug'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:523:in `run_command'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:474:in `block in run_single'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:468:in `each'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/dispatcher_shell.rb:468:in `run_single'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/rex/ui/text/shell.rb:158:in `run'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/metasploit/framework/command/console.rb:48:in `start'
# /Users/Shared/Relocated_Items/Security/rapid7/metasploit-framework/lib/metasploit/framework/command/base.rb:82:in `start'
#
# [06/19/2020 11:51:44] [d(2)] core: Stager osx/armle/reverse_tcp and stage osx/x64/meterpreter have incompatible architectures: armle - x64
#
# [05/15/2020 14:23:55] [e(0)] core: [-] Error during IRB: undefined method `[]' for nil:NilClass
res = errors.scan(%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+\)\]).)+|m)
if res.empty?
return build_section(
'Errors',
'The following errors occurred before the issue occurred:',
'The error log file was empty'
)
end
# Scan returns each error as a single item array
res.flatten!
errors_str = concat_str_array_from_last_idx(res, ERROR_TOTAL)
build_section(
'Errors',
'The following errors occurred before the issue occurred:',
errors_str
)
rescue StandardError => e
section_build_error('Failed to extract Errors', e)
end
def self.logs
log_lines = File.readlines(File.join(Msf::Config.log_directory, 'framework.log'))
logs_str = concat_str_array_from_last_idx(log_lines, LOG_LINE_TOTAL)
build_section(
'Logs',
'The following logs were recorded before the issue occurred:',
logs_str
)
rescue StandardError => e
section_build_error('Failed to extract Logs', e)
end
def self.versions(framework)
str = <<~VERSIONS
Framework: #{framework.version}
Ruby: #{RUBY_DESCRIPTION}
Install Root: #{Msf::Config.install_root}
Session Type: #{db_connection_info(framework)}
Install Method: #{installation_method}
VERSIONS
build_section('Version/Install', 'The versions and install method of your Metasploit setup:', str)
rescue StandardError => e
section_build_error('Failed to extract Versions', e)
end
class << self
private
def add_hash_to_ini_group(ini, hash, group_name)
if hash.empty?
return
end
unless ini.group?(group_name)
ini.add_group(group_name)
end
hash.each_pair do |k, v|
ini[group_name][k] = v
end
end
def concat_str_array_from_last_idx(array, concat_total)
start_pos = array.length > concat_total ? array.length - concat_total : 0
end_pos = array.length - 1
str = array[start_pos..end_pos].join('')
str.strip
end
# Copy pasta of the print_connection_info method in console/command_dispatcher/db.rb
def db_connection_info(framework)
unless framework.db.connection_established?
return "#{framework.db.driver} selected, no connection"
end
cdb = ''
if framework.db.driver == 'http'
cdb = framework.db.name
else
::ActiveRecord::Base.connection_pool.with_connection do |conn|
if conn.respond_to?(:current_database)
cdb = conn.current_database
end
end
end
if cdb.empty?
output = "Connected Database Name could not be extracted. DB Connection type: #{framework.db.driver}."
else
output = "Connected to #{cdb}. Connection type: #{framework.db.driver}."
end
output += " Connection name: #{framework.db.get_data_service}." if framework.db.get_data_service
output
end
def build_section(header_name, blurb, content)
<<~SECTION
## %grn#{header_name.strip}%clr
#{blurb.strip}
#{with_collapsible_wrapper(content.strip)}
SECTION
end
def with_collapsible_wrapper(content)
<<~WRAPPER
<details>
<summary>Collapse</summary>
```
#{content}
```
</details>
WRAPPER
end
def installation_method
if File.exist?(File.join(Msf::Config.install_root, 'version.yml'))
'Omnibus Installer'
elsif File.directory?(File.join(Msf::Config.install_root, '.git'))
'Git Clone'
else
'Other - Please specify'
end
end
def section_build_error(msg, error)
"#{msg}: #{error.class} - #{error.message} \n Call stack:\n#{error.backtrace.join("\n")}"
end
end
end
end
end

View File

@ -290,8 +290,9 @@ module Shell
attr_accessor :on_command_proc
attr_accessor :on_print_proc
attr_accessor :framework
attr_accessor :hist_last_saved # the number of history lines when last saved/loaded
protected
protected
def supports_color?
true
@ -481,7 +482,6 @@ protected
attr_accessor :stop_flag, :cont_prompt # :nodoc:
attr_accessor :tab_complete_proc # :nodoc:
attr_accessor :histfile # :nodoc:
attr_accessor :hist_last_saved # the number of history lines when last saved/loaded
attr_accessor :log_source, :stop_count # :nodoc:
attr_accessor :local_hostname, :local_username # :nodoc:
attr_reader :cont_flag # :nodoc:

View File

@ -0,0 +1,7 @@
[framework/database/1]
key10=val10
key11=val11
[framework/database/2]
key12=val12
key13=val13

View File

@ -0,0 +1,16 @@
[00/00/0000 00:00:00] [e(0)] core: [-] Error 1
[11/11/1111 11:11:11] [e(0)] core: [-] Error 2
Call stack:
Stack_Trace
stack trace
STACK-TRACE
[99/99/9999 99:99:99] [d(0)] NOT_AN_ERROR_TO_BE_IGNORED
[99/99/9999 99:99:99] [d(0)] NOT_AN_ERROR_TO_BE_IGNORED
[99/99/9999 99:99:99] [d(0)] NOT_AN_ERROR_TO_BE_IGNORED
[99/99/9999 99:99:99] [d(0)] NOT_AN_ERROR_TO_BE_IGNORED
[22/22/2222 22:22:22] [e(0)] core: [-] Error 3

View File

@ -0,0 +1,39 @@
[00/00/0000 00:00:00] [e(0)] core: [-] Error 1
[00/00/0000 00:00:00] [e(0)] core: [-] Error 2
[00/00/0000 00:00:00] [e(0)] core: [-] Error 3
[00/00/0000 00:00:00] [e(0)] core: [-] Error 4
[00/00/0000 00:00:00] [e(0)] core: [-] Error 5
[00/00/0000 00:00:00] [e(0)] core: [-] Error 6
[00/00/0000 00:00:00] [e(0)] core: [-] Error 7
[00/00/0000 00:00:00] [e(0)] core: [-] Error 8
[00/00/0000 00:00:00] [e(0)] core: [-] Error 9
[00/00/0000 00:00:00] [e(0)] core: [-] Error 10
[00/00/0000 00:00:00] [e(0)] core: [-] Error 11
[00/00/0000 00:00:00] [e(0)] core: [-] Error 12
[00/00/0000 00:00:00] [e(0)] core: [-] Error 13
[00/00/0000 00:00:00] [e(0)] core: [-] Error 14
[00/00/0000 00:00:00] [e(0)] core: [-] Error 15
[00/00/0000 00:00:00] [e(0)] core: [-] Error 16
[00/00/0000 00:00:00] [e(0)] core: [-] Error 17
[00/00/0000 00:00:00] [e(0)] core: [-] Error 18
[00/00/0000 00:00:00] [e(0)] core: [-] Error 19
[00/00/0000 00:00:00] [e(0)] core: [-] Error 20

View File

@ -0,0 +1,50 @@
[00/00/0000 00:00:00] [e(0)] core: Log Line 1
[00/00/0000 00:00:00] [e(0)] core: Log Line 2
[00/00/0000 00:00:00] [e(0)] core: Log Line 3
[00/00/0000 00:00:00] [e(0)] core: Log Line 4
[00/00/0000 00:00:00] [e(0)] core: Log Line 5
[00/00/0000 00:00:00] [e(0)] core: Log Line 6
[00/00/0000 00:00:00] [e(0)] core: Log Line 7
[00/00/0000 00:00:00] [e(0)] core: Log Line 8
[00/00/0000 00:00:00] [e(0)] core: Log Line 9
[00/00/0000 00:00:00] [e(0)] core: Log Line 10
[00/00/0000 00:00:00] [e(0)] core: Log Line 11
[00/00/0000 00:00:00] [e(0)] core: Log Line 12
[00/00/0000 00:00:00] [e(0)] core: Log Line 13
[00/00/0000 00:00:00] [e(0)] core: Log Line 14
[00/00/0000 00:00:00] [e(0)] core: Log Line 15
[00/00/0000 00:00:00] [e(0)] core: Log Line 16
[00/00/0000 00:00:00] [e(0)] core: Log Line 17
[00/00/0000 00:00:00] [e(0)] core: Log Line 18
[00/00/0000 00:00:00] [e(0)] core: Log Line 19
[00/00/0000 00:00:00] [e(0)] core: Log Line 20
[00/00/0000 00:00:00] [e(0)] core: Log Line 21
[00/00/0000 00:00:00] [e(0)] core: Log Line 22
[00/00/0000 00:00:00] [e(0)] core: Log Line 23
[00/00/0000 00:00:00] [e(0)] core: Log Line 24
[00/00/0000 00:00:00] [e(0)] core: Log Line 25
[00/00/0000 00:00:00] [e(0)] core: Log Line 26
[00/00/0000 00:00:00] [e(0)] core: Log Line 27
[00/00/0000 00:00:00] [e(0)] core: Log Line 28
[00/00/0000 00:00:00] [e(0)] core: Log Line 29
[00/00/0000 00:00:00] [e(0)] core: Log Line 30
[00/00/0000 00:00:00] [e(0)] core: Log Line 31
[00/00/0000 00:00:00] [e(0)] core: Log Line 32
[00/00/0000 00:00:00] [e(0)] core: Log Line 33
[00/00/0000 00:00:00] [e(0)] core: Log Line 34
[00/00/0000 00:00:00] [e(0)] core: Log Line 35
[00/00/0000 00:00:00] [e(0)] core: Log Line 36
[00/00/0000 00:00:00] [e(0)] core: Log Line 37
[00/00/0000 00:00:00] [e(0)] core: Log Line 38
[00/00/0000 00:00:00] [e(0)] core: Log Line 39
[00/00/0000 00:00:00] [e(0)] core: Log Line 40
[00/00/0000 00:00:00] [e(0)] core: Log Line 41
[00/00/0000 00:00:00] [e(0)] core: Log Line 42
[00/00/0000 00:00:00] [e(0)] core: Log Line 43
[00/00/0000 00:00:00] [e(0)] core: Log Line 44
[00/00/0000 00:00:00] [e(0)] core: Log Line 45
[00/00/0000 00:00:00] [e(0)] core: Log Line 46
[00/00/0000 00:00:00] [e(0)] core: Log Line 47
[00/00/0000 00:00:00] [e(0)] core: Log Line 48
[00/00/0000 00:00:00] [e(0)] core: Log Line 49
[00/00/0000 00:00:00] [e(0)] core: Log Line 50

View File

@ -0,0 +1,100 @@
[00/00/0000 00:00:00] [e(0)] core: Log Line 1
[00/00/0000 00:00:00] [e(0)] core: Log Line 2
[00/00/0000 00:00:00] [e(0)] core: Log Line 3
[00/00/0000 00:00:00] [e(0)] core: Log Line 4
[00/00/0000 00:00:00] [e(0)] core: Log Line 5
[00/00/0000 00:00:00] [e(0)] core: Log Line 6
[00/00/0000 00:00:00] [e(0)] core: Log Line 7
[00/00/0000 00:00:00] [e(0)] core: Log Line 8
[00/00/0000 00:00:00] [e(0)] core: Log Line 9
[00/00/0000 00:00:00] [e(0)] core: Log Line 10
[00/00/0000 00:00:00] [e(0)] core: Log Line 11
[00/00/0000 00:00:00] [e(0)] core: Log Line 12
[00/00/0000 00:00:00] [e(0)] core: Log Line 13
[00/00/0000 00:00:00] [e(0)] core: Log Line 14
[00/00/0000 00:00:00] [e(0)] core: Log Line 15
[00/00/0000 00:00:00] [e(0)] core: Log Line 16
[00/00/0000 00:00:00] [e(0)] core: Log Line 17
[00/00/0000 00:00:00] [e(0)] core: Log Line 18
[00/00/0000 00:00:00] [e(0)] core: Log Line 19
[00/00/0000 00:00:00] [e(0)] core: Log Line 20
[00/00/0000 00:00:00] [e(0)] core: Log Line 21
[00/00/0000 00:00:00] [e(0)] core: Log Line 22
[00/00/0000 00:00:00] [e(0)] core: Log Line 23
[00/00/0000 00:00:00] [e(0)] core: Log Line 24
[00/00/0000 00:00:00] [e(0)] core: Log Line 25
[00/00/0000 00:00:00] [e(0)] core: Log Line 26
[00/00/0000 00:00:00] [e(0)] core: Log Line 27
[00/00/0000 00:00:00] [e(0)] core: Log Line 28
[00/00/0000 00:00:00] [e(0)] core: Log Line 29
[00/00/0000 00:00:00] [e(0)] core: Log Line 30
[00/00/0000 00:00:00] [e(0)] core: Log Line 31
[00/00/0000 00:00:00] [e(0)] core: Log Line 32
[00/00/0000 00:00:00] [e(0)] core: Log Line 33
[00/00/0000 00:00:00] [e(0)] core: Log Line 34
[00/00/0000 00:00:00] [e(0)] core: Log Line 35
[00/00/0000 00:00:00] [e(0)] core: Log Line 36
[00/00/0000 00:00:00] [e(0)] core: Log Line 37
[00/00/0000 00:00:00] [e(0)] core: Log Line 38
[00/00/0000 00:00:00] [e(0)] core: Log Line 39
[00/00/0000 00:00:00] [e(0)] core: Log Line 40
[00/00/0000 00:00:00] [e(0)] core: Log Line 41
[00/00/0000 00:00:00] [e(0)] core: Log Line 42
[00/00/0000 00:00:00] [e(0)] core: Log Line 43
[00/00/0000 00:00:00] [e(0)] core: Log Line 44
[00/00/0000 00:00:00] [e(0)] core: Log Line 45
[00/00/0000 00:00:00] [e(0)] core: Log Line 46
[00/00/0000 00:00:00] [e(0)] core: Log Line 47
[00/00/0000 00:00:00] [e(0)] core: Log Line 48
[00/00/0000 00:00:00] [e(0)] core: Log Line 49
[00/00/0000 00:00:00] [e(0)] core: Log Line 50
[00/00/0000 00:00:00] [e(0)] core: Log Line 51
[00/00/0000 00:00:00] [e(0)] core: Log Line 52
[00/00/0000 00:00:00] [e(0)] core: Log Line 53
[00/00/0000 00:00:00] [e(0)] core: Log Line 54
[00/00/0000 00:00:00] [e(0)] core: Log Line 55
[00/00/0000 00:00:00] [e(0)] core: Log Line 56
[00/00/0000 00:00:00] [e(0)] core: Log Line 57
[00/00/0000 00:00:00] [e(0)] core: Log Line 58
[00/00/0000 00:00:00] [e(0)] core: Log Line 59
[00/00/0000 00:00:00] [e(0)] core: Log Line 60
[00/00/0000 00:00:00] [e(0)] core: Log Line 61
[00/00/0000 00:00:00] [e(0)] core: Log Line 62
[00/00/0000 00:00:00] [e(0)] core: Log Line 63
[00/00/0000 00:00:00] [e(0)] core: Log Line 64
[00/00/0000 00:00:00] [e(0)] core: Log Line 65
[00/00/0000 00:00:00] [e(0)] core: Log Line 66
[00/00/0000 00:00:00] [e(0)] core: Log Line 67
[00/00/0000 00:00:00] [e(0)] core: Log Line 68
[00/00/0000 00:00:00] [e(0)] core: Log Line 69
[00/00/0000 00:00:00] [e(0)] core: Log Line 70
[00/00/0000 00:00:00] [e(0)] core: Log Line 71
[00/00/0000 00:00:00] [e(0)] core: Log Line 72
[00/00/0000 00:00:00] [e(0)] core: Log Line 73
[00/00/0000 00:00:00] [e(0)] core: Log Line 74
[00/00/0000 00:00:00] [e(0)] core: Log Line 75
[00/00/0000 00:00:00] [e(0)] core: Log Line 76
[00/00/0000 00:00:00] [e(0)] core: Log Line 77
[00/00/0000 00:00:00] [e(0)] core: Log Line 78
[00/00/0000 00:00:00] [e(0)] core: Log Line 79
[00/00/0000 00:00:00] [e(0)] core: Log Line 80
[00/00/0000 00:00:00] [e(0)] core: Log Line 81
[00/00/0000 00:00:00] [e(0)] core: Log Line 82
[00/00/0000 00:00:00] [e(0)] core: Log Line 83
[00/00/0000 00:00:00] [e(0)] core: Log Line 84
[00/00/0000 00:00:00] [e(0)] core: Log Line 85
[00/00/0000 00:00:00] [e(0)] core: Log Line 86
[00/00/0000 00:00:00] [e(0)] core: Log Line 87
[00/00/0000 00:00:00] [e(0)] core: Log Line 88
[00/00/0000 00:00:00] [e(0)] core: Log Line 89
[00/00/0000 00:00:00] [e(0)] core: Log Line 90
[00/00/0000 00:00:00] [e(0)] core: Log Line 91
[00/00/0000 00:00:00] [e(0)] core: Log Line 92
[00/00/0000 00:00:00] [e(0)] core: Log Line 93
[00/00/0000 00:00:00] [e(0)] core: Log Line 94
[00/00/0000 00:00:00] [e(0)] core: Log Line 95
[00/00/0000 00:00:00] [e(0)] core: Log Line 96
[00/00/0000 00:00:00] [e(0)] core: Log Line 97
[00/00/0000 00:00:00] [e(0)] core: Log Line 98
[00/00/0000 00:00:00] [e(0)] core: Log Line 99
[00/00/0000 00:00:00] [e(0)] core: Log Line 100

View File

@ -0,0 +1,30 @@
[00/00/0000 00:00:00] [e(0)] core: Log Line 1
[00/00/0000 00:00:00] [e(0)] core: Log Line 2
[00/00/0000 00:00:00] [e(0)] core: Log Line 3
[00/00/0000 00:00:00] [e(0)] core: Log Line 4
[00/00/0000 00:00:00] [e(0)] core: Log Line 5
[00/00/0000 00:00:00] [e(0)] core: Log Line 6
[00/00/0000 00:00:00] [e(0)] core: Log Line 7
[00/00/0000 00:00:00] [e(0)] core: Log Line 8
[00/00/0000 00:00:00] [e(0)] core: Log Line 9
[00/00/0000 00:00:00] [e(0)] core: Log Line 10
[00/00/0000 00:00:00] [e(0)] core: Log Line 11
[00/00/0000 00:00:00] [e(0)] core: Log Line 12
[00/00/0000 00:00:00] [e(0)] core: Log Line 13
[00/00/0000 00:00:00] [e(0)] core: Log Line 14
[00/00/0000 00:00:00] [e(0)] core: Log Line 15
[00/00/0000 00:00:00] [e(0)] core: Log Line 16
[00/00/0000 00:00:00] [e(0)] core: Log Line 17
[00/00/0000 00:00:00] [e(0)] core: Log Line 18
[00/00/0000 00:00:00] [e(0)] core: Log Line 19
[00/00/0000 00:00:00] [e(0)] core: Log Line 20
[00/00/0000 00:00:00] [e(0)] core: Log Line 21
[00/00/0000 00:00:00] [e(0)] core: Log Line 22
[00/00/0000 00:00:00] [e(0)] core: Log Line 23
[00/00/0000 00:00:00] [e(0)] core: Log Line 24
[00/00/0000 00:00:00] [e(0)] core: Log Line 25
[00/00/0000 00:00:00] [e(0)] core: Log Line 26
[00/00/0000 00:00:00] [e(0)] core: Log Line 27
[00/00/0000 00:00:00] [e(0)] core: Log Line 28
[00/00/0000 00:00:00] [e(0)] core: Log Line 29
[00/00/0000 00:00:00] [e(0)] core: Log Line 30

830
spec/lib/msf/debug_spec.rb Normal file
View File

@ -0,0 +1,830 @@
require 'spec_helper'
require 'msf/ui/debug'
require 'msf/base/config'
require 'msf/ui/console/driver'
RSpec.describe Msf::Ui::Debug do
let(:file_fixtures_path) { File.join(Msf::Config.install_root, 'spec', 'file_fixtures') }
it 'correctly parses an error log' do
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'error_logs', 'basic'))
error_log_output = <<~LOG
## %grnErrors%clr
The following errors occurred before the issue occurred:
<details>
<summary>Collapse</summary>
```
[00/00/0000 00:00:00] [e(0)] core: [-] Error 1
[11/11/1111 11:11:11] [e(0)] core: [-] Error 2
Call stack:
Stack_Trace
stack trace
STACK-TRACE
[22/22/2222 22:22:22] [e(0)] core: [-] Error 3
```
</details>
LOG
expect(subject.errors).to eql(error_log_output)
end
it 'correctly parses an error log file larger than the log line total' do
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'error_logs', 'long'))
logs = ''
digits = 11..20
digits.each do |d|
logs += "[00/00/0000 00:00:00] [e(0)] core: [-] Error #{d}\n\n"
end
error_log_output = <<~LOG
## %grnErrors%clr
The following errors occurred before the issue occurred:
<details>
<summary>Collapse</summary>
```
#{logs.strip}
```
</details>
LOG
expect(subject.errors).to eql(error_log_output)
end
it 'correctly parses an empty error log file' do
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'error_logs', 'empty'))
error_log_output = <<~EMPTY
## %grnErrors%clr
The following errors occurred before the issue occurred:
<details>
<summary>Collapse</summary>
```
The error log file was empty
```
</details>
EMPTY
expect(subject.errors).to eql(error_log_output)
end
it 'correctly retrieves and parses a command history shorter than the command total' do
stub_const('Readline::HISTORY', Array.new(4) { |i| "Command #{i + 1}" })
driver = instance_double(
Msf::Ui::Console::Driver,
hist_last_saved: 0
)
stub_const('Msf::Ui::Debug::COMMAND_HISTORY_TOTAL', 10)
history_output = <<~E_LOG
## %grnHistory%clr
The following commands were ran during the session and before this issue occurred:
<details>
<summary>Collapse</summary>
```
0 Command 1
1 Command 2
2 Command 3
3 Command 4
```
</details>
E_LOG
expect(subject.history(driver)).to eql(history_output)
end
it 'correctly retrieves and parses a command history equal in length to the command total' do
driver = instance_double(
::Msf::Ui::Console::Driver,
hist_last_saved: 0
)
stub_const('Msf::Ui::Debug::COMMAND_HISTORY_TOTAL', 10)
stub_const('Readline::HISTORY', Array.new(10) { |i| "Command #{i + 1}" })
history_output = <<~E_LOG
## %grnHistory%clr
The following commands were ran during the session and before this issue occurred:
<details>
<summary>Collapse</summary>
```
0 Command 1
1 Command 2
2 Command 3
3 Command 4
4 Command 5
5 Command 6
6 Command 7
7 Command 8
8 Command 9
9 Command 10
```
</details>
E_LOG
expect(subject.history(driver)).to eql(history_output)
end
it 'correctly retrieves and parses a command history larger than the command total' do
driver = instance_double(
::Msf::Ui::Console::Driver,
hist_last_saved: 0
)
stub_const('Msf::Ui::Debug::COMMAND_HISTORY_TOTAL', 10)
stub_const('Readline::HISTORY', Array.new(15) { |i| "Command #{i + 1}" })
history_output = <<~E_LOG
## %grnHistory%clr
The following commands were ran during the session and before this issue occurred:
<details>
<summary>Collapse</summary>
```
5 Command 6
6 Command 7
7 Command 8
8 Command 9
9 Command 10
10 Command 11
11 Command 12
12 Command 13
13 Command 14
14 Command 15
```
</details>
E_LOG
expect(subject.history(driver)).to eql(history_output)
end
it 'correctly retrieves and parses a command history larger than the command total and a session command count smaller than the command total' do
driver = instance_double(
::Msf::Ui::Console::Driver,
hist_last_saved: 10
)
stub_const('Msf::Ui::Debug::COMMAND_HISTORY_TOTAL', 10)
stub_const('Readline::HISTORY', Array.new(15) { |i| "Command #{i + 1}" })
history_output = <<~E_LOG
## %grnHistory%clr
The following commands were ran during the session and before this issue occurred:
<details>
<summary>Collapse</summary>
```
10 Command 11
11 Command 12
12 Command 13
13 Command 14
14 Command 15
```
</details>
E_LOG
expect(subject.history(driver)).to eql(history_output)
end
it 'correctly retrieves and parses an empty config file and datastore' do
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
framework = instance_double(
::Msf::Framework,
datastore: {}
)
driver = instance_double(
::Msf::Ui::Console::Driver,
get_config_core: 'config_core',
get_config: {},
get_config_group: 'config_group',
active_module: nil
)
expected_output = <<~OUTPUT
## %grnModule/Datastore%clr
The following global/module datastore, and database setup was configured before the issue occurred:
<details>
<summary>Collapse</summary>
```
The local config file is empty, no global variables are set, and there is no active module.
```
</details>
OUTPUT
expect(subject.datastore(framework, driver)).to eql(expected_output)
end
it 'correctly retrieves and parses a populated global datastore' do
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
framework = instance_double(
::Msf::Framework,
datastore: {
'key1' => 'val1',
'key2' => 'val2',
'key3' => 'val3'
}
)
driver = instance_double(
::Msf::Ui::Console::Driver,
get_config_core: 'group/name/1',
get_config: {},
get_config_group: 'config_group',
active_module: nil
)
expected_output = <<~OUTPUT
## %grnModule/Datastore%clr
The following global/module datastore, and database setup was configured before the issue occurred:
<details>
<summary>Collapse</summary>
```
[group/name/1]
key1=val1
key2=val2
key3=val3
```
</details>
OUTPUT
expect(subject.datastore(framework, driver)).to eql(expected_output)
end
it 'correctly retrieves and parses a populated global datastore and current module' do
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
framework = instance_double(
::Msf::Framework,
datastore: {
'key1' => 'val1',
'key2' => 'val2',
'key3' => 'val3'
}
)
driver = instance_double(
::Msf::Ui::Console::Driver,
get_config_core: 'group/name/1',
get_config: {
'key4' => 'val4',
'key5' => 'val5',
'key6' => 'val6'
},
get_config_group: 'group/name/2',
active_module: nil
)
expected_output = <<~OUTPUT
## %grnModule/Datastore%clr
The following global/module datastore, and database setup was configured before the issue occurred:
<details>
<summary>Collapse</summary>
```
[group/name/1]
key1=val1
key2=val2
key3=val3
[group/name/2]
key4=val4
key5=val5
key6=val6
```
</details>
OUTPUT
expect(subject.datastore(framework, driver)).to eql(expected_output)
end
it 'correctly retrieves and parses active module variables ' do
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'empty.ini'))
framework = instance_double(
::Msf::Framework,
datastore: {}
)
active_module = instance_double(
Msf::Module,
datastore: {
'key7' => 'val7',
'key8' => 'default_val8',
'key9' => 'val9'
},
refname: 'active/module/variables'
)
driver = instance_double(
::Msf::Ui::Console::Driver,
get_config_core: 'group/name/1',
get_config: {},
get_config_group: 'config_group',
active_module: active_module
)
expected_output = <<~OUTPUT
## %grnModule/Datastore%clr
The following global/module datastore, and database setup was configured before the issue occurred:
<details>
<summary>Collapse</summary>
```
[active/module/variables]
key7=val7
key8=default_val8
key9=val9
```
</details>
OUTPUT
expect(subject.datastore(framework, driver)).to eql(expected_output)
end
it 'correctly retrieves and parses Database information' do
allow(::Msf::Config).to receive(:config_file).and_return(File.join(file_fixtures_path, 'debug', 'config_files', 'db.ini'))
framework = instance_double(
::Msf::Framework,
datastore: {}
)
driver = instance_double(
::Msf::Ui::Console::Driver,
get_config_core: 'group/name/1',
get_config: {},
get_config_group: 'group/name/2',
active_module: nil
)
expected_output = <<~OUTPUT
## %grnModule/Datastore%clr
The following global/module datastore, and database setup was configured before the issue occurred:
<details>
<summary>Collapse</summary>
```
[framework/database/1]
key10=[Filtered]
key11=[Filtered]
[framework/database/2]
key12=[Filtered]
key13=[Filtered]
```
</details>
OUTPUT
expect(subject.datastore(framework, driver)).to eql(expected_output)
end
it 'correctly retrieves and parses logs shorter than the log line total' do
range = 1..30
logs = ''
range.each do |i|
logs += "[00/00/0000 00:00:00] [e(0)] core: Log Line #{i}\n"
end
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'framework_logs', 'short'))
error_log_output = <<~E_LOG
## %grnLogs%clr
The following logs were recorded before the issue occurred:
<details>
<summary>Collapse</summary>
```
#{logs.strip}
```
</details>
E_LOG
expect(subject.logs).to eql(error_log_output)
end
it 'correctly retrieves and parses logs equal to the log line total' do
range = 1..50
logs = ''
range.each do |i|
logs += "[00/00/0000 00:00:00] [e(0)] core: Log Line #{i}\n"
end
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'framework_logs', 'equal'))
error_log_output = <<~E_LOG
## %grnLogs%clr
The following logs were recorded before the issue occurred:
<details>
<summary>Collapse</summary>
```
#{logs.strip}
```
</details>
E_LOG
expect(subject.logs).to eql(error_log_output)
end
it 'correctly retrieves and parses logs larger than the log line total' do
range = 51..100
logs = ''
range.each do |i|
logs += "[00/00/0000 00:00:00] [e(0)] core: Log Line #{i}\n"
end
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'framework_logs', 'long'))
error_log_output = <<~E_LOG
## %grnLogs%clr
The following logs were recorded before the issue occurred:
<details>
<summary>Collapse</summary>
```
#{logs.strip}
```
</details>
E_LOG
expect(subject.logs).to eql(error_log_output)
end
it 'correctly retrieves and parses an empty log file' do
allow(::Msf::Config).to receive(:log_directory).and_return(File.join(file_fixtures_path, 'debug', 'framework_logs', 'empty'))
error_log_output = <<~E_LOG
## %grnLogs%clr
The following logs were recorded before the issue occurred:
<details>
<summary>Collapse</summary>
```
```
</details>
E_LOG
expect(subject.logs).to eql(error_log_output)
end
it 'correctly retrieves version information with no connected DB' do
db = instance_double(
Msf::DBManager,
connection_established?: false,
driver: 'driver'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
allow(::Msf::Config).to receive(:install_root).and_return('bad/path')
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: bad/path
Session Type: driver selected, no connection
Install Method: Other - Please specify
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
it 'correctly retrieves version information with DB connected via http' do
db = double(
'Metasploit::Framework::DataService::DataProxy',
name: 'db_name',
driver: 'http',
connection_established?: true,
get_data_service: 'db_data_service'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
allow(::Msf::Config).to receive(:install_root).and_return('bad/path')
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: bad/path
Session Type: Connected to db_name. Connection type: http. Connection name: db_data_service.
Install Method: Other - Please specify
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
it 'correctly retrieves version information with DB connected via local connection' do
db = double(
'Metasploit::Framework::DataService::DataProxy',
connection_established?: true,
driver: 'local',
get_data_service: 'db_data_service'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
connection_pool = instance_double(ActiveRecord::ConnectionAdapters::ConnectionPool)
connection = double(
'connection',
current_database: 'current_db_connection',
respond_to?: true
)
allow(connection_pool).to receive(:with_connection).and_yield(connection)
allow(::ActiveRecord::Base).to receive(:connection_pool).and_return(connection_pool)
allow(::Msf::Config).to receive(:install_root).and_return('bad/path')
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: bad/path
Session Type: Connected to current_db_connection. Connection type: local. Connection name: db_data_service.
Install Method: Other - Please specify
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
it 'correctly retrieves version information with no connected DB and a Kali Install' do
db = instance_double(
Msf::DBManager,
connection_established?: false,
driver: 'driver'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
allow(::Msf::Config).to receive(:install_root).and_return(File.join(File::SEPARATOR, 'usr', 'share', 'metasploit-framework'))
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: #{File.join(File::SEPARATOR, 'usr', 'share', 'metasploit-framework')}
Session Type: driver selected, no connection
Install Method: Other - Please specify
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
it 'correctly retrieves version information with no connected DB and an Omnibus Install' do
db = instance_double(
Msf::DBManager,
connection_established?: false,
driver: 'driver'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
allow(::Msf::Config).to receive(:install_root).and_return(File.join(file_fixtures_path, 'debug', 'installs', 'omnibus'))
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: #{File.join(file_fixtures_path, 'debug', 'installs', 'omnibus')}
Session Type: driver selected, no connection
Install Method: Omnibus Installer
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
it 'correctly retrieves version information with no connected DB and a Git Clone' do
db = instance_double(
Msf::DBManager,
connection_established?: false,
driver: 'driver'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
allow(::Msf::Config).to receive(:install_root).and_return(File.join(file_fixtures_path, 'debug', 'installs'))
allow(File).to receive(:directory?).with(File.join(Msf::Config.install_root, '.git')).and_return(true)
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: #{File.join(file_fixtures_path, 'debug', 'installs')}
Session Type: driver selected, no connection
Install Method: Git Clone
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
it 'correctly retrieves version information with no connected DB and a Arch Pacman install' do
db = instance_double(
Msf::DBManager,
connection_established?: false,
driver: 'driver'
)
framework = instance_double(
::Msf::Framework,
version: 'VERSION',
db: db
)
allow(::Msf::Config).to receive(:install_root).times.and_return(File.join(File::SEPARATOR, 'opt', 'metasploit'))
expected_output = <<~OUTPUT
## %grnVersion/Install%clr
The versions and install method of your Metasploit setup:
<details>
<summary>Collapse</summary>
```
Framework: VERSION
Ruby: #{RUBY_DESCRIPTION}
Install Root: #{File.join(File::SEPARATOR, 'opt', 'metasploit')}
Session Type: driver selected, no connection
Install Method: Other - Please specify
```
</details>
OUTPUT
expect(subject.versions(framework)).to eql(expected_output)
end
end