Land #7655, Refactor/cleanup core command dispatcher
commit
f734031804
|
@ -1,7 +1,7 @@
|
|||
Feature: Help command
|
||||
|
||||
Background:
|
||||
Given I run `msfconsole --defer-module-loads -x help -x exit`
|
||||
Given I run `msfconsole --defer-module-loads -q -x help -x exit`
|
||||
|
||||
Scenario: The 'help' command's output
|
||||
Then the output should contain:
|
||||
|
@ -12,51 +12,72 @@ Feature: Help command
|
|||
Command Description
|
||||
------- -----------
|
||||
? Help menu
|
||||
advanced Displays advanced options for one or more modules
|
||||
back Move back from the current context
|
||||
banner Display an awesome metasploit banner
|
||||
cd Change the current working directory
|
||||
color Toggle color
|
||||
connect Communicate with a host
|
||||
edit Edit the current module with $VISUAL or $EDITOR
|
||||
exit Exit the console
|
||||
get Gets the value of a context-specific variable
|
||||
getg Gets the value of a global variable
|
||||
grep Grep the output of another command
|
||||
help Help menu
|
||||
info Displays information about one or more modules
|
||||
irb Drop into irb scripting mode
|
||||
jobs Displays and manages jobs
|
||||
kill Kill a job
|
||||
load Load a framework plugin
|
||||
loadpath Searches for and loads modules from a path
|
||||
makerc Save commands entered since start to a file
|
||||
options Displays global options or for one or more modules
|
||||
popm Pops the latest module off the stack and makes it active
|
||||
previous Sets the previously loaded module as the current module
|
||||
pushm Pushes the active or list of modules onto the module stack
|
||||
quit Exit the console
|
||||
reload_all Reloads all modules from all defined module paths
|
||||
rename_job Rename a job
|
||||
resource Run the commands stored in a file
|
||||
route Route traffic through a session
|
||||
save Saves the active datastores
|
||||
search Searches module names and descriptions
|
||||
sess Interact with a given session
|
||||
sessions Dump session listings and display information about sessions
|
||||
set Sets a context-specific variable to a value
|
||||
setg Sets a global variable to a value
|
||||
show Displays modules of a given type, or all modules
|
||||
sleep Do nothing for the specified number of seconds
|
||||
spool Write console output into a file as well the screen
|
||||
threads View and manipulate background threads
|
||||
unload Unload a framework plugin
|
||||
unset Unsets one or more context-specific variables
|
||||
unsetg Unsets one or more global variables
|
||||
use Selects a module by name
|
||||
version Show the framework and console library version numbers
|
||||
|
||||
|
||||
Module Commands
|
||||
===============
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
advanced Displays advanced options for one or more modules
|
||||
back Move back from the current context
|
||||
edit Edit the current module with $VISUAL or $EDITOR
|
||||
info Displays information about one or more modules
|
||||
loadpath Searches for and loads modules from a path
|
||||
options Displays global options or for one or more modules
|
||||
popm Pops the latest module off the stack and makes it active
|
||||
previous Sets the previously loaded module as the current module
|
||||
pushm Pushes the active or list of modules onto the module stack
|
||||
reload_all Reloads all modules from all defined module paths
|
||||
search Searches module names and descriptions
|
||||
show Displays modules of a given type, or all modules
|
||||
use Selects a module by name
|
||||
|
||||
|
||||
Job Commands
|
||||
============
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
jobs Displays and manages jobs
|
||||
kill Kill a job
|
||||
rename_job Rename a job
|
||||
|
||||
|
||||
Resource Script Commands
|
||||
========================
|
||||
|
||||
Command Description
|
||||
------- -----------
|
||||
makerc Save commands entered since start to a file
|
||||
resource Run the commands stored in a file
|
||||
|
||||
|
||||
Database Backend Commands
|
||||
=========================
|
||||
|
||||
|
|
|
@ -74,6 +74,36 @@ module CommandDispatcher
|
|||
dlog("Call stack:\n#{$@.join("\n")}", 'core', LEV_1)
|
||||
end
|
||||
|
||||
#
|
||||
# Generate an array of job or session IDs from a given range String.
|
||||
# Always returns an Array.
|
||||
#
|
||||
# @param id_list [String] Range or list description such as 1-5 or 1,3,5 etc
|
||||
# @return [Array<String>] Representing the range
|
||||
def build_range_array(id_list)
|
||||
item_list = []
|
||||
unless id_list.blank?
|
||||
temp_list = id_list.split(',')
|
||||
temp_list.each do |ele|
|
||||
return if ele.count('-') > 1
|
||||
return if ele.first == '-' || ele[-1] == '-'
|
||||
return if ele.first == '.' || ele[-1] == '.'
|
||||
|
||||
if ele.include? '-'
|
||||
temp_array = (ele.split("-").inject { |s, e| s.to_i..e.to_i }).to_a
|
||||
item_list.concat(temp_array)
|
||||
elsif ele.include? '..'
|
||||
temp_array = (ele.split("..").inject { |s, e| s.to_i..e.to_i }).to_a
|
||||
item_list.concat(temp_array)
|
||||
else
|
||||
item_list.push(ele.to_i)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
item_list.uniq.sort
|
||||
end
|
||||
|
||||
#
|
||||
# The driver that this command dispatcher is associated with.
|
||||
#
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,215 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/ui/text/output/buffer/stdout'
|
||||
|
||||
module Msf
|
||||
module Ui
|
||||
module Console
|
||||
module CommandDispatcher
|
||||
#
|
||||
# {CommandDispatcher} for commands related to background jobs in Metasploit Framework.
|
||||
#
|
||||
class Jobs
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
@@jobs_opts = Rex::Parser::Arguments.new(
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-k" => [ true, "Terminate jobs by job ID and/or range." ],
|
||||
"-K" => [ false, "Terminate all running jobs." ],
|
||||
"-i" => [ true, "Lists detailed information about a running job."],
|
||||
"-l" => [ false, "List all running jobs." ],
|
||||
"-v" => [ false, "Print more detailed info. Use with -i and -l" ]
|
||||
)
|
||||
|
||||
def commands
|
||||
{
|
||||
"jobs" => "Displays and manages jobs",
|
||||
"rename_job" => "Rename a job",
|
||||
"kill" => "Kill a job",
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the name of the command dispatcher.
|
||||
#
|
||||
def name
|
||||
"Job"
|
||||
end
|
||||
|
||||
def cmd_rename_job_help
|
||||
print_line "Usage: rename_job [ID] [Name]"
|
||||
print_line
|
||||
print_line "Example: rename_job 0 \"meterpreter HTTPS special\""
|
||||
print_line
|
||||
print_line "Rename a job that's currently active."
|
||||
print_line "You may use the jobs command to see what jobs are available."
|
||||
print_line
|
||||
end
|
||||
|
||||
def cmd_rename_job(*args)
|
||||
if args.include?('-h') || args.length != 2 || args[0] !~ /^\d+$/
|
||||
cmd_rename_job_help
|
||||
return false
|
||||
end
|
||||
|
||||
job_id = args[0].to_s
|
||||
job_name = args[1].to_s
|
||||
|
||||
unless framework.jobs[job_id]
|
||||
print_error("Job #{job_id} does not exist.")
|
||||
return false
|
||||
end
|
||||
|
||||
# This is not respecting the Protected access control, but this seems to be the only way
|
||||
# to rename a job. If you know a more appropriate way, patches accepted.
|
||||
framework.jobs[job_id].send(:name=, job_name)
|
||||
print_status("Job #{job_id} updated")
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the rename_job command
|
||||
#
|
||||
# @param str [String] the string currently being typed before tab was hit
|
||||
# @param words [Array<String>] the previously completed words on the command line. words is always
|
||||
# at least 1 when tab completion has reached this stage since the command itself has been completed
|
||||
|
||||
def cmd_rename_job_tabs(str, words)
|
||||
return [] if words.length > 1
|
||||
framework.jobs.keys
|
||||
end
|
||||
|
||||
def cmd_jobs_help
|
||||
print_line "Usage: jobs [options]"
|
||||
print_line
|
||||
print_line "Active job manipulation and interaction."
|
||||
print @@jobs_opts.usage
|
||||
end
|
||||
|
||||
#
|
||||
# Displays and manages running jobs for the active instance of the
|
||||
# framework.
|
||||
#
|
||||
def cmd_jobs(*args)
|
||||
# Make the default behavior listing all jobs if there were no options
|
||||
# or the only option is the verbose flag
|
||||
args.unshift("-l") if args.length == 0 || args == ["-v"]
|
||||
|
||||
verbose = false
|
||||
dump_list = false
|
||||
dump_info = false
|
||||
job_id = nil
|
||||
|
||||
# Parse the command options
|
||||
@@jobs_opts.parse(args) do |opt, idx, val|
|
||||
case opt
|
||||
when "-v"
|
||||
verbose = true
|
||||
when "-l"
|
||||
dump_list = true
|
||||
# Terminate the supplied job ID(s)
|
||||
when "-k"
|
||||
job_list = build_range_array(val)
|
||||
if job_list.blank?
|
||||
print_error("Please specify valid job identifier(s)")
|
||||
return false
|
||||
end
|
||||
print_status("Stopping the following job(s): #{job_list.join(', ')}")
|
||||
job_list.map(&:to_s).each do |job|
|
||||
if framework.jobs.has_key?(job)
|
||||
print_status("Stopping job #{job}")
|
||||
framework.jobs.stop_job(job)
|
||||
else
|
||||
print_error("Invalid job identifier: #{job}")
|
||||
end
|
||||
end
|
||||
when "-K"
|
||||
print_line("Stopping all jobs...")
|
||||
framework.jobs.each_key do |i|
|
||||
framework.jobs.stop_job(i)
|
||||
end
|
||||
when "-i"
|
||||
# Defer printing anything until the end of option parsing
|
||||
# so we can check for the verbose flag.
|
||||
dump_info = true
|
||||
job_id = val
|
||||
when "-h"
|
||||
cmd_jobs_help
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
if dump_list
|
||||
print("\n#{Serializer::ReadableText.dump_jobs(framework, verbose)}\n")
|
||||
end
|
||||
if dump_info
|
||||
if job_id && framework.jobs[job_id.to_s]
|
||||
job = framework.jobs[job_id.to_s]
|
||||
mod = job.ctx[0]
|
||||
|
||||
output = '\n'
|
||||
output += "Name: #{mod.name}"
|
||||
output += ", started at #{job.start_time}" if job.start_time
|
||||
print_line(output)
|
||||
|
||||
show_options(mod) if mod.options.has_options?
|
||||
|
||||
if verbose
|
||||
mod_opt = Serializer::ReadableText.dump_advanced_options(mod, ' ')
|
||||
if mod_opt && mod_opt.length > 0
|
||||
print_line("\nModule advanced options:\n\n#{mod_opt}\n")
|
||||
end
|
||||
end
|
||||
else
|
||||
print_line("Invalid Job ID")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the jobs command
|
||||
#
|
||||
# @param str [String] the string currently being typed before tab was hit
|
||||
# @param words [Array<String>] the previously completed words on the command line. words is always
|
||||
# at least 1 when tab completion has reached this stage since the command itself has been completed
|
||||
|
||||
def cmd_jobs_tabs(str, words)
|
||||
if words.length == 1
|
||||
return @@jobs_opts.fmt.keys
|
||||
end
|
||||
|
||||
if words.length == 2 && (@@jobs_opts.fmt[words[1]] || [false])[0]
|
||||
return framework.jobs.keys
|
||||
end
|
||||
|
||||
[]
|
||||
end
|
||||
|
||||
def cmd_kill_help
|
||||
print_line "Usage: kill <job1> [job2 ...]"
|
||||
print_line
|
||||
print_line "Equivalent to 'jobs -k job1 -k job2 ...'"
|
||||
print @@jobs_opts.usage
|
||||
end
|
||||
|
||||
def cmd_kill(*args)
|
||||
cmd_jobs("-k", *args)
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the kill command
|
||||
#
|
||||
# @param str [String] the string currently being typed before tab was hit
|
||||
# @param words [Array<String>] the previously completed words on the command line. words is always
|
||||
# at least 1 when tab completion has reached this stage since the command itself has been completed
|
||||
|
||||
def cmd_kill_tabs(str, words)
|
||||
return [] if words.length > 1
|
||||
framework.jobs.keys
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load Diff
|
@ -3,139 +3,141 @@
|
|||
require 'rex/parser/arguments'
|
||||
|
||||
module Msf
|
||||
module Ui
|
||||
module Console
|
||||
module CommandDispatcher
|
||||
|
||||
module Ui
|
||||
module Console
|
||||
module CommandDispatcher
|
||||
###
|
||||
#
|
||||
# Payload module command dispatcher.
|
||||
#
|
||||
###
|
||||
class Payload
|
||||
class Payload
|
||||
|
||||
include Msf::Ui::Console::ModuleCommandDispatcher
|
||||
include Msf::Ui::Console::ModuleCommandDispatcher
|
||||
|
||||
# Load supported formats
|
||||
supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
||||
# Load supported formats
|
||||
supported_formats = Msf::Simple::Buffer.transform_formats + Msf::Util::EXE.to_executable_fmt_formats
|
||||
|
||||
@@generate_opts = Rex::Parser::Arguments.new(
|
||||
"-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ],
|
||||
"-E" => [ false, "Force encoding." ],
|
||||
"-e" => [ true, "The name of the encoder module to use." ],
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-o" => [ true, "A comma separated list of options in VAR=VAL format." ],
|
||||
"-s" => [ true, "NOP sled length." ],
|
||||
"-f" => [ true, "The output file name (otherwise stdout)" ],
|
||||
"-t" => [ true, "The output format: #{supported_formats.join(',')}" ],
|
||||
"-p" => [ true, "The Platform for output." ],
|
||||
"-k" => [ false, "Keep the template executable functional" ],
|
||||
"-x" => [ true, "The executable template to use" ],
|
||||
"-i" => [ true, "the number of encoding iterations." ])
|
||||
@@generate_opts = Rex::Parser::Arguments.new(
|
||||
"-b" => [ true, "The list of characters to avoid: '\\x00\\xff'" ],
|
||||
"-E" => [ false, "Force encoding." ],
|
||||
"-e" => [ true, "The name of the encoder module to use." ],
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-o" => [ true, "A comma separated list of options in VAR=VAL format." ],
|
||||
"-s" => [ true, "NOP sled length." ],
|
||||
"-f" => [ true, "The output file name (otherwise stdout)" ],
|
||||
"-t" => [ true, "The output format: #{supported_formats.join(',')}" ],
|
||||
"-p" => [ true, "The Platform for output." ],
|
||||
"-k" => [ false, "Keep the template executable functional" ],
|
||||
"-x" => [ true, "The executable template to use" ],
|
||||
"-i" => [ true, "the number of encoding iterations." ])
|
||||
|
||||
#
|
||||
# Returns the hash of commands specific to payload modules.
|
||||
#
|
||||
def commands
|
||||
super.update({
|
||||
"generate" => "Generates a payload",
|
||||
})
|
||||
end
|
||||
#
|
||||
# Returns the hash of commands specific to payload modules.
|
||||
#
|
||||
def commands
|
||||
super.update({
|
||||
"generate" => "Generates a payload",
|
||||
})
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the command dispatcher name.
|
||||
#
|
||||
def name
|
||||
return "Payload"
|
||||
end
|
||||
#
|
||||
# Returns the command dispatcher name.
|
||||
#
|
||||
def name
|
||||
return "Payload"
|
||||
end
|
||||
|
||||
#
|
||||
# Generates a payload.
|
||||
#
|
||||
def cmd_generate(*args)
|
||||
#
|
||||
# Generates a payload.
|
||||
#
|
||||
def cmd_generate(*args)
|
||||
|
||||
# Parse the arguments
|
||||
encoder_name = nil
|
||||
sled_size = nil
|
||||
option_str = nil
|
||||
badchars = nil
|
||||
type = "ruby"
|
||||
ofile = nil
|
||||
iter = 1
|
||||
force = nil
|
||||
template = nil
|
||||
plat = nil
|
||||
keep = false
|
||||
# Parse the arguments
|
||||
encoder_name = nil
|
||||
sled_size = nil
|
||||
option_str = nil
|
||||
badchars = nil
|
||||
type = "ruby"
|
||||
ofile = nil
|
||||
iter = 1
|
||||
force = nil
|
||||
template = nil
|
||||
plat = nil
|
||||
keep = false
|
||||
|
||||
@@generate_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when '-b'
|
||||
badchars = Rex::Text.hex_to_raw(val)
|
||||
when '-e'
|
||||
encoder_name = val
|
||||
when '-E'
|
||||
force = true
|
||||
when '-o'
|
||||
option_str = val
|
||||
when '-s'
|
||||
sled_size = val.to_i
|
||||
when '-t'
|
||||
type = val
|
||||
when '-f'
|
||||
ofile = val
|
||||
when '-i'
|
||||
iter = val
|
||||
when '-k'
|
||||
keep = true
|
||||
when '-p'
|
||||
plat = val
|
||||
when '-x'
|
||||
template = val
|
||||
when '-h'
|
||||
print(
|
||||
"Usage: generate [options]\n\n" +
|
||||
"Generates a payload.\n" +
|
||||
@@generate_opts.usage)
|
||||
return true
|
||||
end
|
||||
}
|
||||
if (encoder_name.nil? and mod.datastore['ENCODER'])
|
||||
encoder_name = mod.datastore['ENCODER']
|
||||
end
|
||||
|
||||
|
||||
# Generate the payload
|
||||
begin
|
||||
buf = mod.generate_simple(
|
||||
'BadChars' => badchars,
|
||||
'Encoder' => encoder_name,
|
||||
'Format' => type,
|
||||
'NopSledSize' => sled_size,
|
||||
'OptionStr' => option_str,
|
||||
'ForceEncode' => force,
|
||||
'Template' => template,
|
||||
'Platform' => plat,
|
||||
'KeepTemplateWorking' => keep,
|
||||
'Iterations' => iter)
|
||||
rescue
|
||||
log_error("Payload generation failed: #{$!}")
|
||||
return false
|
||||
end
|
||||
|
||||
if(not ofile)
|
||||
# Display generated payload
|
||||
print(buf)
|
||||
else
|
||||
print_status("Writing #{buf.length} bytes to #{ofile}...")
|
||||
fd = File.open(ofile, "wb")
|
||||
fd.write(buf)
|
||||
fd.close
|
||||
end
|
||||
|
||||
@@generate_opts.parse(args) { |opt, idx, val|
|
||||
case opt
|
||||
when '-b'
|
||||
badchars = Rex::Text.hex_to_raw(val)
|
||||
when '-e'
|
||||
encoder_name = val
|
||||
when '-E'
|
||||
force = true
|
||||
when '-o'
|
||||
option_str = val
|
||||
when '-s'
|
||||
sled_size = val.to_i
|
||||
when '-t'
|
||||
type = val
|
||||
when '-f'
|
||||
ofile = val
|
||||
when '-i'
|
||||
iter = val
|
||||
when '-k'
|
||||
keep = true
|
||||
when '-p'
|
||||
plat = val
|
||||
when '-x'
|
||||
template = val
|
||||
when '-h'
|
||||
print(
|
||||
"Usage: generate [options]\n\n" +
|
||||
"Generates a payload.\n" +
|
||||
@@generate_opts.usage)
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
}
|
||||
if (encoder_name.nil? and mod.datastore['ENCODER'])
|
||||
encoder_name = mod.datastore['ENCODER']
|
||||
end
|
||||
|
||||
|
||||
# Generate the payload
|
||||
begin
|
||||
buf = mod.generate_simple(
|
||||
'BadChars' => badchars,
|
||||
'Encoder' => encoder_name,
|
||||
'Format' => type,
|
||||
'NopSledSize' => sled_size,
|
||||
'OptionStr' => option_str,
|
||||
'ForceEncode' => force,
|
||||
'Template' => template,
|
||||
'Platform' => plat,
|
||||
'KeepTemplateWorking' => keep,
|
||||
'Iterations' => iter)
|
||||
rescue
|
||||
log_error("Payload generation failed: #{$!}")
|
||||
return false
|
||||
end
|
||||
|
||||
if(not ofile)
|
||||
# Display generated payload
|
||||
print(buf)
|
||||
else
|
||||
print_status("Writing #{buf.length} bytes to #{ofile}...")
|
||||
fd = File.open(ofile, "wb")
|
||||
fd.write(buf)
|
||||
fd.close
|
||||
end
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end end end end
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
#
|
||||
# Rex
|
||||
#
|
||||
|
||||
require 'rex/ui/text/output/buffer/stdout'
|
||||
|
||||
|
||||
module Msf
|
||||
module Ui
|
||||
module Console
|
||||
module CommandDispatcher
|
||||
|
||||
#
|
||||
# {CommandDispatcher} for commands related to background jobs in Metasploit Framework.
|
||||
#
|
||||
class Resource
|
||||
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
|
||||
def commands
|
||||
{
|
||||
"resource" => "Run the commands stored in a file",
|
||||
"makerc" => "Save commands entered since start to a file",
|
||||
}
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the name of the command dispatcher.
|
||||
#
|
||||
def name
|
||||
"Resource Script"
|
||||
end
|
||||
|
||||
def cmd_resource_help
|
||||
print_line "Usage: resource path1 [path2 ...]"
|
||||
print_line
|
||||
print_line "Run the commands stored in the supplied files. Resource files may also contain"
|
||||
print_line "ruby code between <ruby></ruby> tags."
|
||||
print_line
|
||||
print_line "See also: makerc"
|
||||
print_line
|
||||
end
|
||||
|
||||
def cmd_resource(*args)
|
||||
if args.empty?
|
||||
cmd_resource_help
|
||||
return false
|
||||
end
|
||||
|
||||
args.each do |res|
|
||||
good_res = nil
|
||||
if ::File.exist?(res)
|
||||
good_res = res
|
||||
elsif
|
||||
# let's check to see if it's in the scripts/resource dir (like when tab completed)
|
||||
[
|
||||
::Msf::Config.script_directory + ::File::SEPARATOR + "resource",
|
||||
::Msf::Config.user_script_directory + ::File::SEPARATOR + "resource"
|
||||
].each do |dir|
|
||||
res_path = dir + ::File::SEPARATOR + res
|
||||
if ::File.exist?(res_path)
|
||||
good_res = res_path
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
if good_res
|
||||
driver.load_resource(good_res)
|
||||
else
|
||||
print_error("#{res} is not a valid resource file")
|
||||
next
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Tab completion for the resource command
|
||||
#
|
||||
# @param str [String] the string currently being typed before tab was hit
|
||||
# @param words [Array<String>] the previously completed words on the command line. words is always
|
||||
# at least 1 when tab completion has reached this stage since the command itself has been completed
|
||||
|
||||
def cmd_resource_tabs(str, words)
|
||||
tabs = []
|
||||
#return tabs if words.length > 1
|
||||
if ( str and str =~ /^#{Regexp.escape(File::SEPARATOR)}/ )
|
||||
# then you are probably specifying a full path so let's just use normal file completion
|
||||
return tab_complete_filenames(str,words)
|
||||
elsif (not words[1] or not words[1].match(/^\//))
|
||||
# then let's start tab completion in the scripts/resource directories
|
||||
begin
|
||||
[
|
||||
::Msf::Config.script_directory + File::SEPARATOR + "resource",
|
||||
::Msf::Config.user_script_directory + File::SEPARATOR + "resource",
|
||||
"."
|
||||
].each do |dir|
|
||||
next if not ::File.exist? dir
|
||||
tabs += ::Dir.new(dir).find_all { |e|
|
||||
path = dir + File::SEPARATOR + e
|
||||
::File.file?(path) and File.readable?(path)
|
||||
}
|
||||
end
|
||||
rescue Exception
|
||||
end
|
||||
else
|
||||
tabs += tab_complete_filenames(str,words)
|
||||
end
|
||||
return tabs
|
||||
end
|
||||
|
||||
def cmd_makerc_help
|
||||
print_line "Usage: makerc <output rc file>"
|
||||
print_line
|
||||
print_line "Save the commands executed since startup to the specified file."
|
||||
print_line
|
||||
end
|
||||
|
||||
#
|
||||
# Saves commands executed since the ui started to the specified msfrc file
|
||||
#
|
||||
def cmd_makerc(*args)
|
||||
if args.empty?
|
||||
cmd_makerc_help
|
||||
return false
|
||||
end
|
||||
driver.save_recent_history(args[0])
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -26,6 +26,15 @@ class Driver < Msf::Ui::Driver
|
|||
DefaultPrompt = "%undmsf%clr"
|
||||
DefaultPromptChar = "%clr>"
|
||||
|
||||
#
|
||||
# Console Command Dispatchers to be loaded after the Core dispatcher.
|
||||
#
|
||||
CommandDispatchers = [
|
||||
CommandDispatcher::Modules,
|
||||
CommandDispatcher::Jobs,
|
||||
CommandDispatcher::Resource
|
||||
]
|
||||
|
||||
#
|
||||
# The console driver processes various framework notified events.
|
||||
#
|
||||
|
@ -108,6 +117,10 @@ class Driver < Msf::Ui::Driver
|
|||
print_error("***")
|
||||
end
|
||||
|
||||
# Load the other "core" command dispatchers
|
||||
CommandDispatchers.each do |dispatcher|
|
||||
enstack_dispatcher(dispatcher)
|
||||
end
|
||||
|
||||
# Add the database dispatcher if it is usable
|
||||
if (framework.db.usable)
|
||||
|
|
|
@ -12,90 +12,6 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Core do
|
|||
described_class.new(driver)
|
||||
end
|
||||
|
||||
context '#search_modules_sql' do
|
||||
def search_modules_sql
|
||||
core.search_modules_sql(match)
|
||||
end
|
||||
|
||||
let(:match) do
|
||||
''
|
||||
end
|
||||
|
||||
it 'should generate Matching Modules table' do
|
||||
expect(core).to receive(:generate_module_table).with('Matching Modules').and_call_original
|
||||
|
||||
search_modules_sql
|
||||
end
|
||||
|
||||
it 'should call Msf::DBManager#search_modules' do
|
||||
expect(db_manager).to receive(:search_modules).with(match).and_return([])
|
||||
|
||||
search_modules_sql
|
||||
end
|
||||
|
||||
context 'with matching Mdm::Module::Details' do
|
||||
let(:match) do
|
||||
module_detail.fullname
|
||||
end
|
||||
|
||||
let!(:module_detail) do
|
||||
FactoryGirl.create(:mdm_module_detail)
|
||||
end
|
||||
|
||||
context 'printed table' do
|
||||
def cell(table, row, column)
|
||||
row_line_number = 6 + row
|
||||
line_number = 0
|
||||
|
||||
cell = nil
|
||||
|
||||
table.each_line do |line|
|
||||
if line_number == row_line_number
|
||||
# strip prefix and postfix
|
||||
padded_cells = line[3...-1]
|
||||
cells = padded_cells.split(/\s{2,}/)
|
||||
|
||||
cell = cells[column]
|
||||
break
|
||||
end
|
||||
|
||||
line_number += 1
|
||||
end
|
||||
|
||||
cell
|
||||
end
|
||||
|
||||
let(:printed_table) do
|
||||
table = ''
|
||||
|
||||
expect(core).to receive(:print_line) do |string|
|
||||
table = string
|
||||
end
|
||||
|
||||
search_modules_sql
|
||||
|
||||
table
|
||||
end
|
||||
|
||||
it 'should have fullname in first column' do
|
||||
expect(cell(printed_table, 0, 0)).to include(module_detail.fullname)
|
||||
end
|
||||
|
||||
it 'should have disclosure date in second column' do
|
||||
expect(cell(printed_table, 0, 1)).to include(module_detail.disclosure_date.strftime("%Y-%m-%d"))
|
||||
end
|
||||
|
||||
it 'should have rank name in third column' do
|
||||
expect(cell(printed_table, 0, 2)).to include(Msf::RankingName[module_detail.rank])
|
||||
end
|
||||
|
||||
it 'should have name in fourth column' do
|
||||
expect(cell(printed_table, 0, 3)).to include(module_detail.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
it { is_expected.to respond_to :cmd_get }
|
||||
it { is_expected.to respond_to :cmd_getg }
|
||||
|
||||
|
|
|
@ -0,0 +1,98 @@
|
|||
require 'spec_helper'
|
||||
|
||||
require 'msf/ui'
|
||||
require 'msf/ui/console/module_command_dispatcher'
|
||||
require 'msf/ui/console/command_dispatcher/core'
|
||||
|
||||
RSpec.describe Msf::Ui::Console::CommandDispatcher::Modules do
|
||||
include_context 'Msf::DBManager'
|
||||
include_context 'Msf::UIDriver'
|
||||
|
||||
subject(:core) do
|
||||
described_class.new(driver)
|
||||
end
|
||||
|
||||
context '#search_modules_sql' do
|
||||
def search_modules_sql
|
||||
core.search_modules_sql(match)
|
||||
end
|
||||
|
||||
let(:match) do
|
||||
''
|
||||
end
|
||||
|
||||
it 'should generate Matching Modules table' do
|
||||
expect(core).to receive(:generate_module_table).with('Matching Modules').and_call_original
|
||||
|
||||
search_modules_sql
|
||||
end
|
||||
|
||||
it 'should call Msf::DBManager#search_modules' do
|
||||
expect(db_manager).to receive(:search_modules).with(match).and_return([])
|
||||
|
||||
search_modules_sql
|
||||
end
|
||||
|
||||
context 'with matching Mdm::Module::Details' do
|
||||
let(:match) do
|
||||
module_detail.fullname
|
||||
end
|
||||
|
||||
let!(:module_detail) do
|
||||
FactoryGirl.create(:mdm_module_detail)
|
||||
end
|
||||
|
||||
context 'printed table' do
|
||||
def cell(table, row, column)
|
||||
row_line_number = 6 + row
|
||||
line_number = 0
|
||||
|
||||
cell = nil
|
||||
|
||||
table.each_line do |line|
|
||||
if line_number == row_line_number
|
||||
# strip prefix and postfix
|
||||
padded_cells = line[3...-1]
|
||||
cells = padded_cells.split(/\s{2,}/)
|
||||
|
||||
cell = cells[column]
|
||||
break
|
||||
end
|
||||
|
||||
line_number += 1
|
||||
end
|
||||
|
||||
cell
|
||||
end
|
||||
|
||||
let(:printed_table) do
|
||||
table = ''
|
||||
|
||||
expect(core).to receive(:print_line) do |string|
|
||||
table = string
|
||||
end
|
||||
|
||||
search_modules_sql
|
||||
|
||||
table
|
||||
end
|
||||
|
||||
it 'should have fullname in first column' do
|
||||
expect(cell(printed_table, 0, 0)).to include(module_detail.fullname)
|
||||
end
|
||||
|
||||
it 'should have disclosure date in second column' do
|
||||
expect(cell(printed_table, 0, 1)).to include(module_detail.disclosure_date.strftime("%Y-%m-%d"))
|
||||
end
|
||||
|
||||
it 'should have rank name in third column' do
|
||||
expect(cell(printed_table, 0, 2)).to include(Msf::RankingName[module_detail.rank])
|
||||
end
|
||||
|
||||
it 'should have name in fourth column' do
|
||||
expect(cell(printed_table, 0, 3)).to include(module_detail.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue