Land #4063 allow session lists

Note: the parsing for cmd_sessions  needs to be revamped and DRYd up in
a separate PR.
bug/bundler_fix
Joshua Smith 2014-11-09 22:40:53 -06:00
commit 1844b3956d
No known key found for this signature in database
GPG Key ID: ADDEAD3D3D609E33
1 changed files with 279 additions and 264 deletions

View File

@ -41,7 +41,7 @@ class Core
"-v" => [ false, "List verbose fields" ],
"-q" => [ false, "Quiet mode" ],
"-d" => [ true, "Detach an interactive session" ],
"-k" => [ true, "Terminate session" ],
"-k" => [ true, "Terminate sessions by session ID and/or range" ],
"-K" => [ false, "Terminate all sessions" ],
"-s" => [ true, "Run a script on the session given with -i, or all"],
"-r" => [ false, "Reset the ring buffer for the session given with -i, or all"],
@ -49,7 +49,7 @@ class Core
@@jobs_opts = Rex::Parser::Arguments.new(
"-h" => [ false, "Help banner." ],
"-k" => [ true, "Terminate the specified job name." ],
"-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." ],
@ -778,9 +778,7 @@ class Core
def cmd_jobs(*args)
# Make the default behavior listing all jobs if there were no options
# or the only option is the verbose flag
if (args.length == 0 or args == ["-v"])
args.unshift("-l")
end
args.unshift("-l") if args.length == 0 || args == ["-v"]
verbose = false
dump_list = false
@ -788,20 +786,27 @@ class Core
job_id = nil
# Parse the command options
@@jobs_opts.parse(args) { |opt, idx, val|
@@jobs_opts.parse(args) do |opt, idx, val|
case opt
when "-v"
verbose = true
when "-l"
dump_list = true
# Terminate the supplied job name
# Terminate the supplied job ID(s)
when "-k"
if (not framework.jobs.has_key?(val))
print_error("No such job")
else
print_line("Stopping job: #{val}...")
framework.jobs.stop_job(val)
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...")
@ -817,28 +822,28 @@ class Core
cmd_jobs_help
return false
end
}
if (dump_list)
print("\n" + Serializer::ReadableText.dump_jobs(framework, verbose) + "\n")
end
if (dump_info)
if (job_id and framework.jobs[job_id.to_s])
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 = '\n'
output += "Name: #{mod.name}"
output += ", started at #{job.start_time}" if job.start_time
print_line(output)
if (mod.options.has_options?)
show_options(mod)
end
show_options(mod) if mod.options.has_options?
if (verbose)
if verbose
mod_opt = Serializer::ReadableText.dump_advanced_options(mod,' ')
print_line("\nModule advanced options:\n\n#{mod_opt}\n") if (mod_opt and mod_opt.length > 0)
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")
@ -1563,7 +1568,11 @@ class Core
print_line "Usage: sessions [options]"
print_line
print_line "Active session manipulation and interaction."
print(@@sessions_opts.usage())
print(@@sessions_opts.usage)
print_line
print_line "Many options allow specifying session ranges using commas and dashes."
print_line "For example: sessions -s checkvm -i 1,3-5 or sessions -k 1-2,5,6"
print_line
end
#
@ -1584,250 +1593,220 @@ class Core
extra = []
# Parse the command options
@@sessions_opts.parse(args) { |opt, idx, val|
@@sessions_opts.parse(args) do |opt, idx, val|
case opt
when "-q"
quiet = true
# Run a command on all sessions, or the session given with -i
when "-c"
method = 'cmd'
if (val)
cmds << val
end
when "-v"
verbose = true
# Do something with the supplied session identifier instead of
# all sessions.
when "-i"
sid = val
# Display the list of active sessions
when "-l"
method = 'list'
when "-k"
method = 'kill'
sid = val if val
if not sid
print_error("Specify a session to kill")
return false
end
when "-K"
method = 'killall'
when "-d"
method = 'detach'
sid = val
# Run a script on all meterpreter sessions
when "-s"
if not script
method = 'scriptall'
script = val
end
# Upload and exec to the specific command session
when "-u"
method = 'upexec'
sid = val
# Reset the ring buffer read pointer
when "-r"
reset_ring = true
method = 'reset_ring'
# Display help banner
when "-h"
cmd_sessions_help
return false
else
extra << val
when "-q"
quiet = true
# Run a command on all sessions, or the session given with -i
when "-c"
method = 'cmd'
cmds << val if val
when "-v"
verbose = true
# Do something with the supplied session identifier instead of
# all sessions.
when "-i"
sid = val
# Display the list of active sessions
when "-l"
method = 'list'
when "-k"
method = 'kill'
sid = val || false
when "-K"
method = 'killall'
when "-d"
method = 'detach'
sid = val || false
# Run a script on all meterpreter sessions
when "-s"
unless script
method = 'scriptall'
script = val
end
# Upload and exec to the specific command session
when "-u"
method = 'upexec'
sid = val || false
# Reset the ring buffer read pointer
when "-r"
reset_ring = true
method = 'reset_ring'
# Display help banner
when "-h"
cmd_sessions_help
return false
else
extra << val
end
}
if sid and not framework.sessions.get(sid)
print_error("Invalid session id")
return false
end
if method.nil? and sid
if !method && sid
method = 'interact'
end
unless sid.nil? || method == 'interact'
session_list = build_range_array(sid)
if session_list.blank?
print_error("Please specify valid session identifier(s)")
return false
end
end
# Now, perform the actual method
case method
when 'cmd'
if (cmds.length < 1)
print_error("No command specified!")
return false
end
cmds.each do |cmd|
if sid
sessions = [ sid ]
else
sessions = framework.sessions.keys.sort
end
sessions.each do |s|
session = framework.sessions.get(s)
print_status("Running '#{cmd}' on #{session.type} session #{s} (#{session.session_host})")
if (session.type == "meterpreter")
# If session.sys is nil, dont even try..
if not (session.sys)
print_error("Session #{s} does not have stdapi loaded, skipping...")
next
end
c, c_args = cmd.split(' ', 2)
begin
process = session.sys.process.execute(c, c_args,
{
'Channelized' => true,
'Hidden' => true
})
rescue ::Rex::Post::Meterpreter::RequestError
print_error("Failed: #{$!.class} #{$!}")
end
if process and process.channel and (data = process.channel.read)
print_line(data)
end
elsif session.type == "shell"
if (output = session.shell_command(cmd))
print_line(output)
end
end
# If the session isn't a meterpreter or shell type, it
# could be a VNC session (which can't run commands) or
# something custom (which we don't know how to run
# commands on), so don't bother.
end
end
when 'kill'
if ((session = framework.sessions.get(sid)))
print_status("Killing session #{sid}")
session.kill
else
print_error("Invalid session identifier: #{sid}")
end
when 'killall'
print_status("Killing all sessions...")
framework.sessions.each_sorted do |s|
if ((session = framework.sessions.get(s)))
session.kill
end
end
when 'detach'
if ((session = framework.sessions.get(sid)))
print_status("Detaching session #{sid}")
if (session.interactive?)
session.detach()
end
else
print_error("Invalid session identifier: #{sid}")
end
when 'interact'
if ((session = framework.sessions.get(sid)))
if (session.interactive?)
print_status("Starting interaction with #{session.name}...\n") if (quiet == false)
self.active_session = session
session.interact(driver.input.dup, driver.output)
self.active_session = nil
if (driver.input.supports_readline)
driver.input.reset_tab_completion
end
else
print_error("Session #{sid} is non-interactive.")
end
else
print_error("Invalid session identifier: #{sid}")
end
when 'scriptall'
if (script.nil?)
print_error("No script specified!")
return false
end
script_paths = {}
script_paths['meterpreter'] = Msf::Sessions::Meterpreter.find_script_path(script)
script_paths['shell'] = Msf::Sessions::CommandShell.find_script_path(script)
when 'cmd'
if cmds.length < 1
print_error("No command specified!")
return false
end
cmds.each do |cmd|
if sid
print_status("Running script #{script} on session #{sid}...")
sessions = [ sid ]
sessions = session_list
else
print_status("Running script #{script} on all sessions...")
sessions = framework.sessions.keys.sort
end
sessions.each do |s|
if ((session = framework.sessions.get(s)))
if (script_paths[session.type])
print_status("Session #{s} (#{session.session_host}):")
begin
session.execute_file(script_paths[session.type], extra)
rescue ::Exception => e
log_error("Error executing script: #{e.class} #{e}")
end
end
end
if sessions.blank?
print_error("Please specify valid session identifier(s) using -i")
return false
end
sessions.each do |s|
session = verify_session(s)
next unless session
print_status("Running '#{cmd}' on #{session.type} session #{s} (#{session.session_host})")
when 'upexec'
session_list = build_sessions_array(sid)
print_status("Executing 'post/multi/manage/shell_to_meterpreter' on session(s): #{session_list}")
session_list.each do |sess|
if ((session = framework.sessions.get(sess)))
if (session.interactive?)
if (session.type == "shell")
session.init_ui(driver.input, driver.output)
session.execute_script('post/multi/manage/shell_to_meterpreter')
session.reset_ui
else
print_error("Session #{sess} is not a command shell session, skipping...")
next
end
else
print_error("Session #{sess} is non-interactive, skipping...")
if session.type == 'meterpreter'
# If session.sys is nil, dont even try..
unless session.sys
print_error("Session #{s} does not have stdapi loaded, skipping...")
next
end
c, c_args = cmd.split(' ', 2)
begin
process = session.sys.process.execute(c, c_args,
{
'Channelized' => true,
'Hidden' => true
})
rescue ::Rex::Post::Meterpreter::RequestError
print_error("Failed: #{$!.class} #{$!}")
end
if process && process.channel
data = process.channel.read
print_line(data) if data
end
elsif session.type == 'shell'
output = session.shell_command(cmd)
print_line(output) if output
end
# If the session isn't a meterpreter or shell type, it
# could be a VNC session (which can't run commands) or
# something custom (which we don't know how to run
# commands on), so don't bother.
end
end
when 'kill'
print_status("Killing the following session(s): #{session_list.join(', ')}")
session_list.each do |sess_id|
session = framework.sessions.get(sess_id)
if session
print_status("Killing session #{sess_id}")
session.kill
else
print_error("Invalid session identifier: #{sess_id}")
end
end
when 'killall'
print_status("Killing all sessions...")
framework.sessions.each_sorted do |s|
session = framework.sessions.get(s)
session.kill if session
end
when 'detach'
print_status("Detaching the following session(s): #{session_list.join(', ')}")
session_list.each do |sess_id|
session = verify_session(sess_id)
# if session is interactive, it's detachable
if session
print_status("Detaching session #{sess_id}")
session.detach
end
end
when 'interact'
session = verify_session(sid)
if session
print_status("Starting interaction with #{session.name}...\n") unless quiet
self.active_session = session
session.interact(driver.input.dup, driver.output)
self.active_session = nil
driver.input.reset_tab_completion if driver.input.supports_readline
end
when 'scriptall'
unless script
print_error("No script specified!")
return false
end
script_paths = {}
script_paths['meterpreter'] = Msf::Sessions::Meterpreter.find_script_path(script)
script_paths['shell'] = Msf::Sessions::CommandShell.find_script_path(script)
sessions = sid ? session_list : framework.sessions.keys.sort
sessions.each do |sess_id|
session = verify_session(sess_id, true)
# @TODO: Not interactive sessions can or cannot have scripts run on them?
if session == false # specifically looking for false
# if verify_session returned false, sess_id is valid, but not interactive
session = framework.sessions.get(sess_id)
end
if session
if script_paths[session.type]
print_status("Session #{sess_id} (#{session.session_host}):")
print_status("Running script #{script} on #{session.type} session" +
" #{sess_id} (#{session.session_host})")
begin
session.execute_file(script_paths[session.type], extra)
rescue ::Exception => e
log_error("Error executing script: #{e.class} #{e}")
end
end
else
print_error("Invalid session identifier: #{sess_id}")
end
end
when 'upexec'
print_status("Executing 'post/multi/manage/shell_to_meterpreter' on " +
"session(s): #{session_list}")
session_list.each do |sess_id|
session = verify_session(sess_id)
if session
if session.type == 'shell'
session.init_ui(driver.input, driver.output)
session.execute_script('post/multi/manage/shell_to_meterpreter')
session.reset_ui
else
print_error("Invalid session identifier: #{sess}")
print_error("Session #{sess_id} is not a command shell session, skipping...")
next
end
if session_list.count > 1
print_status("Sleeping 5 seconds to allow the previous handler to finish..")
sleep(5)
end
end
when 'reset_ring'
sessions = sid ? [ sid ] : framework.sessions.keys
sessions.each do |sidx|
s = framework.sessions[sidx]
next if not (s and s.respond_to?(:ring_seq))
s.reset_ring_sequence
print_status("Reset the ring buffer pointer for Session #{sidx}")
if session_list.count > 1
print_status("Sleeping 5 seconds to allow the previous handler to finish..")
sleep(5)
end
when 'list',nil
print_line
print(Serializer::ReadableText.dump_sessions(framework, :verbose => verbose))
print_line
end
when 'reset_ring'
sessions = sid ? [sid] : framework.sessions.keys
sessions.each do |sidx|
s = framework.sessions[sidx]
next unless (s && s.respond_to?(:ring_seq))
s.reset_ring_sequence
print_status("Reset the ring buffer pointer for Session #{sidx}")
end
when 'list',nil
print_line
print(Serializer::ReadableText.dump_sessions(framework, :verbose => verbose))
print_line
end
rescue IOError, EOFError, Rex::StreamClosedError
@ -1841,7 +1820,7 @@ class Core
# Reset the active session
self.active_session = nil
return true
true
end
#
@ -3001,6 +2980,33 @@ class Core
protected
#
# verifies that a given session_id is valid and that the session is interactive.
# The various return values allow the caller to make better decisions on what
# action can & should be taken depending on the capabilities of the session
# and the caller's objective while making it simple to use in the nominal case
# where the caller needs session_id to match an interactive session
#
# @param session_id [String] A session id, which is an integer as a string
# @param quiet [Boolean] True means the method will produce no error messages
# @return [session] if the given session_id is valid and session is interactive
# @return [false] if the given session_id is valid, but not interactive
# @return [nil] if the given session_id is not valid at all
def verify_session(session_id, quiet = false)
session = framework.sessions.get(session_id)
if session
if session.interactive?
session
else
print_error("Session #{session_id} is non-interactive.") unless quiet
false
end
else
print_error("Invalid session identifier: #{session_id}") unless quiet
nil
end
end
#
# Go_pro methods -- these are used to start and connect to
# Metasploit Community / Pro.
@ -3352,30 +3358,39 @@ class Core
start = line_num - before
start = 0 if start < 0
finish = line_num + after
return all_lines.slice(start..finish)
all_lines.slice(start..finish)
end
# Generate an array of session IDs when presented with input such as '1' or '1,2,4-6,10' or '1,2,4..6,10'
def build_sessions_array(sid_list)
session_list = Array.new
temp_list = sid_list.split(",")
#
# 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] == '.'
temp_list.each do |ele|
if ele.include? '-'
temp_array = (ele.split("-").inject {|s,e| s.to_i..e.to_i}).to_a
session_list.concat(temp_array)
elsif ele.include? '..'
temp_array = (ele.split("..").inject {|s,e| s.to_i..e.to_i}).to_a
session_list.concat(temp_array)
else
session_list.push(ele.to_i)
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
return session_list.uniq.sort
item_list.uniq.sort
end
end
end end end end