Land #10625, repeat command to repeat commands
parent
8fbbff30db
commit
058eabbd24
|
@ -106,6 +106,7 @@ class Core
|
||||||
"history" => "Show command history",
|
"history" => "Show command history",
|
||||||
"load" => "Load a framework plugin",
|
"load" => "Load a framework plugin",
|
||||||
"quit" => "Exit the console",
|
"quit" => "Exit the console",
|
||||||
|
"repeat" => "Repeat a list of commands",
|
||||||
"route" => "Route traffic through a session",
|
"route" => "Route traffic through a session",
|
||||||
"save" => "Saves the active datastores",
|
"save" => "Saves the active datastores",
|
||||||
"sessions" => "Dump session listings and display information about sessions",
|
"sessions" => "Dump session listings and display information about sessions",
|
||||||
|
@ -2013,8 +2014,8 @@ class Core
|
||||||
end
|
end
|
||||||
|
|
||||||
# OptionParser#order allows us to take the rest of the line for the command
|
# OptionParser#order allows us to take the rest of the line for the command
|
||||||
pattern, *cmd = opts.order(args)
|
pattern, *rest = opts.order(args)
|
||||||
cmd = cmd.join(" ")
|
cmd = Shellwords.shelljoin(rest)
|
||||||
return print(opts.help) if !pattern || cmd.empty?
|
return print(opts.help) if !pattern || cmd.empty?
|
||||||
|
|
||||||
rx = Regexp.new(pattern, match_mods[:insensitive])
|
rx = Regexp.new(pattern, match_mods[:insensitive])
|
||||||
|
@ -2038,7 +2039,7 @@ class Core
|
||||||
|
|
||||||
# Bail if the command failed
|
# Bail if the command failed
|
||||||
if cmd_output =~ /Unknown command:/
|
if cmd_output =~ /Unknown command:/
|
||||||
print_error("Unknown command: #{args[0]}.")
|
print_error("Unknown command: '#{rest[0]}'.")
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
# put lines into an array so we can access them more easily and split('\n') doesn't work on the output obj.
|
# put lines into an array so we can access them more easily and split('\n') doesn't work on the output obj.
|
||||||
|
@ -2087,6 +2088,81 @@ class Core
|
||||||
tabs
|
tabs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def cmd_repeat_help
|
||||||
|
cmd_repeat '-h'
|
||||||
|
end
|
||||||
|
|
||||||
|
#
|
||||||
|
# Repeats (loops) a given list of commands
|
||||||
|
#
|
||||||
|
def cmd_repeat(*args)
|
||||||
|
looper = method :loop
|
||||||
|
|
||||||
|
opts = OptionParser.new do |opts|
|
||||||
|
opts.banner = 'Usage: repeat [OPTIONS] COMMAND...'
|
||||||
|
opts.separator 'Repeat (loop) a ;-separated list of msfconsole commands indefinitely, or for a'
|
||||||
|
opts.separator 'number of iterations or a certain amount of time.'
|
||||||
|
opts.separator ''
|
||||||
|
|
||||||
|
opts.on '-t SECONDS', '--time SECONDS', 'Number of seconds to repeat COMMAND...', Integer do |n|
|
||||||
|
looper = ->(&block) do
|
||||||
|
# While CLOCK_MONOTONIC is a Linux thing, Ruby emulates it for *BSD, MacOS, and Windows
|
||||||
|
ending_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) + n
|
||||||
|
while Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) < ending_time
|
||||||
|
block.call
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on '-n TIMES', '--number TIMES', 'Number of times to repeat COMMAND..', Integer do |n|
|
||||||
|
looper = n.method(:times)
|
||||||
|
end
|
||||||
|
|
||||||
|
opts.on '-h', '--help', 'Help banner.' do
|
||||||
|
return print(opts.help)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Internal use
|
||||||
|
opts.on '--generate-completions str', 'Return possible tab completions for given string.' do |str|
|
||||||
|
return opts.candidate str
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
cmds = opts.order(args).slice_when do |prev, _|
|
||||||
|
# If the last character of a shellword was a ';' it's probably to
|
||||||
|
# delineate commands and we can remove it
|
||||||
|
prev[-1] == ';' && prev[-1] = ''
|
||||||
|
end.map do |c|
|
||||||
|
Shellwords.shelljoin(c)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Print help if we have no commands, or all the commands are empty
|
||||||
|
return cmd_repeat '-h' if cmds.all? &:empty?
|
||||||
|
|
||||||
|
begin
|
||||||
|
looper.call do
|
||||||
|
cmds.each do |c|
|
||||||
|
driver.run_single c, propagate_errors: true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue ::Exception
|
||||||
|
# Stop looping on exception
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Almost the exact same as grep
|
||||||
|
def cmd_repeat_tabs(str, words)
|
||||||
|
str = '-' if str.empty? # default to use repeat's options
|
||||||
|
tabs = cmd_repeat '--generate-completions', str
|
||||||
|
|
||||||
|
# if not an opt, use normal tab comp.
|
||||||
|
# @todo uncomment out next line when tab_completion normalization is complete RM7649 or
|
||||||
|
# replace with new code that permits "nested" tab completion
|
||||||
|
# tabs = driver.get_all_commands if (str and str =~ /\w/)
|
||||||
|
tabs
|
||||||
|
end
|
||||||
|
|
||||||
#
|
#
|
||||||
# Provide tab completion for option values
|
# Provide tab completion for option values
|
||||||
#
|
#
|
||||||
|
|
|
@ -432,7 +432,7 @@ module DispatcherShell
|
||||||
#
|
#
|
||||||
# Run a single command line.
|
# Run a single command line.
|
||||||
#
|
#
|
||||||
def run_single(line)
|
def run_single(line, propagate_errors: false)
|
||||||
arguments = parse_line(line)
|
arguments = parse_line(line)
|
||||||
method = arguments.shift
|
method = arguments.shift
|
||||||
found = false
|
found = false
|
||||||
|
@ -453,17 +453,27 @@ module DispatcherShell
|
||||||
run_command(dispatcher, method, arguments)
|
run_command(dispatcher, method, arguments)
|
||||||
found = true
|
found = true
|
||||||
end
|
end
|
||||||
|
rescue ::Interrupt
|
||||||
|
print_error("#{method}: Interrupted")
|
||||||
|
raise if propagate_errors
|
||||||
|
rescue OptionParser::ParseError => e
|
||||||
|
print_error("#{method}: #{e.message}")
|
||||||
|
raise if propagate_errors
|
||||||
rescue
|
rescue
|
||||||
error = $!
|
error = $!
|
||||||
|
|
||||||
print_error(
|
print_error(
|
||||||
"Error while running command #{method}: #{$!}" +
|
"Error while running command #{method}: #{$!}" +
|
||||||
"\n\nCall stack:\n#{$@.join("\n")}")
|
"\n\nCall stack:\n#{$@.join("\n")}")
|
||||||
rescue ::Exception
|
|
||||||
|
raise if propagate_errors
|
||||||
|
rescue ::Exception => e
|
||||||
error = $!
|
error = $!
|
||||||
|
|
||||||
print_error(
|
print_error(
|
||||||
"Error while running command #{method}: #{$!}")
|
"Error while running command #{method}: #{$!}")
|
||||||
|
|
||||||
|
raise if propagate_errors
|
||||||
end
|
end
|
||||||
|
|
||||||
# If the dispatcher stack changed as a result of this command,
|
# If the dispatcher stack changed as a result of this command,
|
||||||
|
@ -490,8 +500,6 @@ module DispatcherShell
|
||||||
else
|
else
|
||||||
dispatcher.send('cmd_' + method, *arguments)
|
dispatcher.send('cmd_' + method, *arguments)
|
||||||
end
|
end
|
||||||
rescue OptionParser::ParseError => e
|
|
||||||
print_error("#{method}: #{e.message}")
|
|
||||||
ensure
|
ensure
|
||||||
self.busy = false
|
self.busy = false
|
||||||
end
|
end
|
||||||
|
|
|
@ -109,7 +109,10 @@ class MetasploitModule < Msf::Auxiliary
|
||||||
STARTTLS may also be vulnerable.
|
STARTTLS may also be vulnerable.
|
||||||
|
|
||||||
The module supports several actions, allowing for scanning, dumping of
|
The module supports several actions, allowing for scanning, dumping of
|
||||||
memory contents, and private key recovery.
|
memory contents, and private key recovery. The `repeat` command can be
|
||||||
|
used to make running the `DUMP` many times more convenient. As in:
|
||||||
|
repeat -t 60 run; sleep 2
|
||||||
|
To run every two seconds for one minute.
|
||||||
},
|
},
|
||||||
'Author' => [
|
'Author' => [
|
||||||
'Neel Mehta', # Vulnerability discovery
|
'Neel Mehta', # Vulnerability discovery
|
||||||
|
|
Loading…
Reference in New Issue