Land #10203, Add command for persistent job handler when msf restart

4.x 4.17.7
Jeffrey Martin 2018-08-16 15:37:10 -05:00 committed by Metasploit
parent c91eff48fb
commit ce1fe7fe77
No known key found for this signature in database
GPG Key ID: CDFB5FA52007B954
4 changed files with 140 additions and 16 deletions

View File

@ -198,6 +198,13 @@ class Config < Hash
self.new.history_file self.new.history_file
end end
# Returns the full path to the handler file.
#
# @return [String] path the handler file.
def self.persist_file
self.new.persist_file
end
# Initializes configuration, creating directories as necessary. # Initializes configuration, creating directories as necessary.
# #
# @return [void] # @return [void]
@ -274,6 +281,13 @@ class Config < Hash
config_directory + FileSep + "history" config_directory + FileSep + "history"
end end
# Returns the full path to the handler file.
#
# @return [String] path the handler file.
def persist_file
config_directory + FileSep + "persist"
end
# Returns the global module directory. # Returns the global module directory.
# #
# @return [String] path to global module directory. # @return [String] path to global module directory.

View File

@ -776,7 +776,7 @@ class ReadableText
columns = [ 'Id', 'Name', "Payload", "Payload opts"] columns = [ 'Id', 'Name', "Payload", "Payload opts"]
if (verbose) if (verbose)
columns += [ "URIPATH", "Start Time", "Handler opts" ] columns += [ "URIPATH", "Start Time", "Handler opts", "Persist" ]
end end
tbl = Rex::Text::Table.new( tbl = Rex::Text::Table.new(
@ -785,6 +785,15 @@ class ReadableText
'Columns' => columns 'Columns' => columns
) )
# Get the persistent job info.
if verbose
begin
persist_list = JSON.parse(File.read(Msf::Config.persist_file))
rescue Errno::ENOENT, JSON::ParserError
persist_list = []
end
end
# jobs are stored as a hash with the keys being a numeric String job_id. # jobs are stored as a hash with the keys being a numeric String job_id.
framework.jobs.keys.sort_by(&:to_i).each do |job_id| framework.jobs.keys.sort_by(&:to_i).each do |job_id|
# Job context is stored as an Array with the 0th element being # Job context is stored as an Array with the 0th element being
@ -819,11 +828,17 @@ class ReadableText
row[4] = uripath row[4] = uripath
row[5] = framework.jobs[job_id].start_time row[5] = framework.jobs[job_id].start_time
row[6] = '' row[6] = ''
row[7] = 'false'
if pinst.respond_to?(:listener_uri) if pinst.respond_to?(:listener_uri)
listener_uri = pinst.listener_uri.strip listener_uri = pinst.listener_uri.strip
row[6] = listener_uri unless listener_uri == payload_uri row[6] = listener_uri unless listener_uri == payload_uri
end end
persist_list.each do |e|
row[7] = 'true' if e['mod_options']['Options'] == framework.jobs[job_id.to_s].ctx[1].datastore
end
end end
tbl << row tbl << row
end end

View File

@ -35,7 +35,9 @@ module Msf
"-i" => [ true, "Lists detailed information about a running job."], "-i" => [ true, "Lists detailed information about a running job."],
"-l" => [ false, "List all running jobs." ], "-l" => [ false, "List all running jobs." ],
"-v" => [ false, "Print more detailed info. Use with -i and -l" ], "-v" => [ false, "Print more detailed info. Use with -i and -l" ],
"-S" => [ true, "Row search filter." ], "-p" => [ true, "Add persistence to job by job ID" ],
"-P" => [ false, "Persist all running jobs on restart." ],
"-S" => [ true, "Row search filter." ]
) )
def commands def commands
@ -117,7 +119,9 @@ module Msf
verbose = false verbose = false
dump_list = false dump_list = false
dump_info = false dump_info = false
kill_job = false
job_id = nil job_id = nil
job_list = nil
# Parse the command options # Parse the command options
@@jobs_opts.parse(args) do |opt, _idx, val| @@jobs_opts.parse(args) do |opt, _idx, val|
@ -129,29 +133,31 @@ module Msf
# Terminate the supplied job ID(s) # Terminate the supplied job ID(s)
when "-k" when "-k"
job_list = build_range_array(val) job_list = build_range_array(val)
if job_list.blank? kill_job = true
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.key?(job)
print_status("Stopping job #{job}")
framework.jobs.stop_job(job)
else
print_error("Invalid job identifier: #{job}")
end
end
when "-K" when "-K"
print_line("Stopping all jobs...") print_line("Stopping all jobs...")
framework.jobs.each_key do |i| framework.jobs.each_key do |i|
framework.jobs.stop_job(i) framework.jobs.stop_job(i)
end end
File.truncate(Msf::Config.persist_file,0)
when "-i" when "-i"
# Defer printing anything until the end of option parsing # Defer printing anything until the end of option parsing
# so we can check for the verbose flag. # so we can check for the verbose flag.
dump_info = true dump_info = true
job_id = val job_id = val
when "-p"
job_list = build_range_array(val)
job_list.each do |job_id|
add_persist_job(job_id)
end
when "-P"
print_line("Making all jobs persistent ...")
job_list = framework.jobs.map do |k,v|
v.jid.to_s
end
job_list.each do |job_id|
add_persist_job(job_id)
end
when "-S", "--search" when "-S", "--search"
search_term = val search_term = val
dump_list = true dump_list = true
@ -159,6 +165,7 @@ module Msf
cmd_jobs_help cmd_jobs_help
return false return false
end end
end end
if dump_list if dump_list
@ -186,6 +193,77 @@ module Msf
print_line("Invalid Job ID") print_line("Invalid Job ID")
end end
end end
if kill_job
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(', ')}")
# Remove the persistent job when match the option of payload.
begin
persist_list = JSON.parse(File.read(Msf::Config.persist_file))
rescue Errno::ENOENT, JSON::ParserError
persist_list = []
end
job_list.map(&:to_s).each do |job|
payload_option = framework.jobs[job.to_s].ctx[1].datastore
persist_list.delete_if{|pjob|pjob['mod_options']['Options'] == payload_option}
end
# Write persist job back to config file.
File.open(Msf::Config.persist_file,"w") do |file|
file.puts(JSON.pretty_generate(persist_list))
end
# Stop the job by job id.
job_list.map(&:to_s).each do |job|
if framework.jobs.key?(job)
print_status("Stopping job #{job}")
framework.jobs.stop_job(job)
else
print_error("Invalid job identifier: #{job}")
end
end
end
end
#
# Add a persistent job by job id.
# Persistent job would restore on console restarted.
def add_persist_job(job_id)
if job_id && framework.jobs.has_key?(job_id.to_s)
mod = framework.jobs[job_id.to_s].ctx[0].replicant
payload = framework.jobs[job_id.to_s].ctx[1].replicant
payload_opts = {
'Payload' => payload.refname,
'Options' => payload.datastore,
'RunAsJob' => true
}
mod_opts = {
'mod_name' => mod.fullname,
'mod_options' => payload_opts
}
begin
persist_list = JSON.parse(File.read(Msf::Config.persist_file))
rescue Errno::ENOENT, JSON::ParserError
persist_list = []
end
persist_list << mod_opts
File.open(Msf::Config.persist_file,"w") do |file|
file.puts(JSON.pretty_generate(persist_list))
end
print_line("Added persistence to job #{job_id}.")
else
print_line("Invalid Job ID")
end
end end
# #
@ -354,6 +432,7 @@ module Msf
} }
tab_complete_generic(fmt, str, words) tab_complete_generic(fmt, str, words)
end end
end end
end end
end end

View File

@ -220,6 +220,22 @@ class Driver < Msf::Ui::Driver
} }
end end
# Process persistent job handler
begin
restore_handlers = JSON.parse(File.read(Msf::Config.persist_file))
rescue Errno::ENOENT, JSON::ParserError
restore_handlers = nil
end
unless restore_handlers.nil?
print_status("Starting persistent handler(s)...")
restore_handlers.each do |handler_opts|
handler = framework.modules.create(handler_opts['mod_name'])
handler.exploit_simple(handler_opts['mod_options'])
end
end
# Process any additional startup commands # Process any additional startup commands
if opts['XCommands'] and opts['XCommands'].kind_of? Array if opts['XCommands'] and opts['XCommands'].kind_of? Array
opts['XCommands'].each { |c| opts['XCommands'].each { |c|