minor improvements to session interaction, dumping sessions, interacting with sessions that are backgrounded
git-svn-id: file:///home/svn/incoming/trunk@2772 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
5ee93e6be6
commit
3d976dc22c
|
@ -13,6 +13,9 @@ module Serializer
|
|||
###
|
||||
class ReadableText
|
||||
|
||||
DefaultColumnWrap = 60
|
||||
DefaultIndent = 4
|
||||
|
||||
#
|
||||
# Returns a formatted string that contains information about
|
||||
# the supplied module instance.
|
||||
|
@ -195,7 +198,7 @@ class ReadableText
|
|||
# Dumps the list of options associated with the
|
||||
# supplied module.
|
||||
#
|
||||
def self.dump_options(mod, indent = 4)
|
||||
def self.dump_options(mod, indent = DefaultIndent)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent,
|
||||
'Columns' =>
|
||||
|
@ -219,7 +222,7 @@ class ReadableText
|
|||
return tbl.to_s
|
||||
end
|
||||
|
||||
def self.dump_advanced_options(mod, indent = 4)
|
||||
def self.dump_advanced_options(mod, indent = DefaultIndent)
|
||||
output = ''
|
||||
pad = ' ' * indent
|
||||
|
||||
|
@ -241,7 +244,7 @@ class ReadableText
|
|||
#
|
||||
# Dumps the contents of a datastore
|
||||
#
|
||||
def self.dump_datastore(name, ds, indent = 4, col = 60)
|
||||
def self.dump_datastore(name, ds, indent = DefaultIndent, col = DefaultColumnWrap)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent,
|
||||
'Header' => name,
|
||||
|
@ -258,11 +261,34 @@ class ReadableText
|
|||
return ds.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No entries in data store.\n"
|
||||
end
|
||||
|
||||
#
|
||||
# Dumps the list of active sessions
|
||||
#
|
||||
def self.dump_sessions(framework, indent = DefaultIndent, col = DefaultColumnWrap)
|
||||
tbl = Rex::Ui::Text::Table.new(
|
||||
'Indent' => indent,
|
||||
'Header' => "Active sessions",
|
||||
'Columns' =>
|
||||
[
|
||||
'Id',
|
||||
'Description',
|
||||
'Tunnel'
|
||||
])
|
||||
|
||||
framework.sessions.each_sorted { |k|
|
||||
session = framework.sessions[k]
|
||||
|
||||
tbl << [ session.sid.to_s, session.desc, session.tunnel_to_s ]
|
||||
}
|
||||
|
||||
return framework.sessions.length > 0 ? tbl.to_s : "#{tbl.header_to_s}No active sessions.\n"
|
||||
end
|
||||
|
||||
#
|
||||
# Jacked from Ernest Ellingson <erne [at] powernav.com>, modified
|
||||
# a bit to add indention
|
||||
#
|
||||
def self.word_wrap(str, indent = 4, col = 60)
|
||||
def self.word_wrap(str, indent = 4, col = DefaultColumnWrap)
|
||||
return Rex::Text.wordwrap(str, indent, col)
|
||||
end
|
||||
|
||||
|
|
|
@ -72,31 +72,38 @@ module Basic
|
|||
# rstream to loutput.
|
||||
#
|
||||
def interact
|
||||
# Call the parent in case it has some work to do
|
||||
super
|
||||
|
||||
eof = false
|
||||
|
||||
# Handle suspend notifications
|
||||
handle_suspend
|
||||
|
||||
callcc { |ctx|
|
||||
while true
|
||||
# As long as we're interacting...
|
||||
while (self.interacting == true)
|
||||
begin
|
||||
_interact
|
||||
# If we get an interrupt exception, ask the user if they want to
|
||||
# abort the interaction. If they do, then we return out of
|
||||
# the interact function and call it a day.
|
||||
rescue Interrupt
|
||||
loutput.print("\nStop interacting with session #{name}? [y/N] ")
|
||||
|
||||
r = linput.gets
|
||||
|
||||
# Break out of the continuation
|
||||
ctx.call if (r =~ /^y/i)
|
||||
if (user_want_abort? == true)
|
||||
eof = true
|
||||
ctx.call
|
||||
end
|
||||
rescue EOFError
|
||||
dlog("Session #{name} got EOF, closing.", 'core', LEV_1)
|
||||
|
||||
eof = true
|
||||
ctx.call
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
# Restore the suspend handler
|
||||
restore_suspend
|
||||
|
||||
# If we hit end-of-file, then that means we should finish off this
|
||||
# session and call it a day.
|
||||
framework.sessions.deregister(self) if (eof == true)
|
||||
|
@ -122,7 +129,7 @@ protected
|
|||
# overriden by derived classes if they wish to do this another way.
|
||||
#
|
||||
def _interact
|
||||
while true
|
||||
while self.interacting
|
||||
# Select input and rstream
|
||||
sd = Rex::ThreadSafe.select([ linput.fd, rstream.fd ])
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ module Interactive
|
|||
# Starts interacting with the session.
|
||||
#
|
||||
def interact
|
||||
self.interacting = true
|
||||
end
|
||||
|
||||
#
|
||||
|
@ -34,6 +35,63 @@ module Interactive
|
|||
# The local output handle. Must inherit from Rex::Ui::Output.
|
||||
#
|
||||
attr_accessor :loutput
|
||||
#
|
||||
# Whether or not the session is currently being interacted with
|
||||
#
|
||||
attr_reader :interacting
|
||||
|
||||
protected
|
||||
|
||||
attr_writer :interacting
|
||||
#
|
||||
# The original suspend proc.
|
||||
#
|
||||
attr_accessor :orig_suspend
|
||||
|
||||
#
|
||||
# Checks to see if the user wants to abort
|
||||
#
|
||||
def user_want_abort?
|
||||
prompt_yesno("Abort session #{name}? [y/N] ")
|
||||
end
|
||||
|
||||
#
|
||||
# Installs a signal handler to monitor suspend signal notifications.
|
||||
#
|
||||
def handle_suspend
|
||||
if (orig_suspend == nil)
|
||||
self.orig_suspend = Signal.trap("TSTP") {
|
||||
# Ask the user if they would like to background the session
|
||||
if (prompt_yesno("Background session #{name}? [y/N] ") == true)
|
||||
self.interacting = false
|
||||
end
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Restores the previously installed signal handler for suspend
|
||||
# notifications.
|
||||
#
|
||||
def restore_suspend
|
||||
if (orig_suspend)
|
||||
Signal.trap("TSTP", orig_suspend)
|
||||
|
||||
self.orig_suspend = nil
|
||||
end
|
||||
end
|
||||
|
||||
def prompt(query)
|
||||
loutput.print("\n" + query)
|
||||
linput.gets
|
||||
end
|
||||
|
||||
#
|
||||
# Check the return value of the prompt
|
||||
#
|
||||
def prompt_yesno(query)
|
||||
(prompt(query) =~ /^y/i) ? true : false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -25,6 +25,13 @@ class SessionManager < Hash
|
|||
self.sid_pool = 0
|
||||
end
|
||||
|
||||
#
|
||||
# Enumerates the sorted list of keys.
|
||||
#
|
||||
def each_sorted(&block)
|
||||
self.keys.sort.each(&block)
|
||||
end
|
||||
|
||||
#
|
||||
# Registers the supplied session object with the framework and returns
|
||||
# a unique session identifier to the caller.
|
||||
|
@ -38,7 +45,7 @@ class SessionManager < Hash
|
|||
next_sid = (self.sid_pool += 1)
|
||||
|
||||
# Insert the session into the session hash table
|
||||
self[next_sid] = session
|
||||
self[next_sid.to_i] = session
|
||||
|
||||
# Initialize the session's sid and framework instance pointer
|
||||
session.sid = next_sid
|
||||
|
@ -58,7 +65,7 @@ class SessionManager < Hash
|
|||
framework.events.on_session_close(session)
|
||||
|
||||
# Remove it from the hash
|
||||
self.delete(session.sid)
|
||||
self.delete(session.sid.to_i)
|
||||
|
||||
# Close it down
|
||||
session.cleanup
|
||||
|
@ -68,7 +75,7 @@ class SessionManager < Hash
|
|||
# Returns the session associated with the supplied sid, if any
|
||||
#
|
||||
def get(sid)
|
||||
return self[sid]
|
||||
return self[sid.to_i]
|
||||
end
|
||||
|
||||
protected
|
||||
|
|
|
@ -12,6 +12,11 @@ class Core
|
|||
|
||||
include Msf::Ui::Console::CommandDispatcher
|
||||
|
||||
@@session_opts = Rex::Parser::Arguments.new(
|
||||
"-i" => [ true, "Interact with the supplied session identifier." ],
|
||||
"-h" => [ false, "Help banner." ],
|
||||
"-l" => [ false, "List all active sessions." ])
|
||||
|
||||
# Returns the list of commands supported by this command dispatcher
|
||||
def commands
|
||||
{
|
||||
|
@ -24,6 +29,7 @@ class Core
|
|||
"quit" => "Exit the console",
|
||||
"save" => "Saves the active datastores",
|
||||
"search" => "Adds one or more module search paths",
|
||||
"session" => "Dump session listings and display information about sessions",
|
||||
"set" => "Sets a variable to a value",
|
||||
"setg" => "Sets a global variable to a value",
|
||||
"show" => "Displays modules of a given type, or all modules",
|
||||
|
@ -216,6 +222,54 @@ class Core
|
|||
recalculate_tab_complete
|
||||
end
|
||||
|
||||
#
|
||||
# Provides an interface to the sessions currently active in the framework.
|
||||
#
|
||||
def cmd_session(*args)
|
||||
if (args.length == 0)
|
||||
args.unshift("-h")
|
||||
end
|
||||
|
||||
begin
|
||||
@@session_opts.parse(args) { |opt, idx, val|
|
||||
sid = nil
|
||||
|
||||
case opt
|
||||
# Interact with the supplied session identifier
|
||||
when "-i"
|
||||
if ((session = framework.sessions.get(val)))
|
||||
if (session.interactive?)
|
||||
print_status("Starting interaction with #{session.name}...\n")
|
||||
|
||||
session.interact
|
||||
else
|
||||
print_error("Session #{val} is non-interactive.")
|
||||
end
|
||||
else
|
||||
print_error("Invalid session identifier: #{val}")
|
||||
end
|
||||
|
||||
# Display the list of active sessions
|
||||
when "-l"
|
||||
print("\n" +
|
||||
Serializer::ReadableText.dump_sessions(framework) + "\n")
|
||||
|
||||
# Display help banner
|
||||
when "-h"
|
||||
print(
|
||||
"Usage: session [options]\n\n" +
|
||||
"Active session manipulation and interaction.\n" +
|
||||
@@session_opts.usage())
|
||||
return false
|
||||
end
|
||||
}
|
||||
rescue
|
||||
log_error("Session manipulation failed: #{$!}")
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
#
|
||||
# Sets a name to a value in a context aware environment
|
||||
#
|
||||
|
|
|
@ -14,6 +14,10 @@ module Ui
|
|||
###
|
||||
class Output
|
||||
|
||||
# General output
|
||||
require 'rex/ui/output/none'
|
||||
|
||||
# Text-based output
|
||||
require 'rex/ui/text/output'
|
||||
|
||||
#
|
||||
|
@ -41,11 +45,17 @@ class Output
|
|||
end
|
||||
|
||||
#
|
||||
# Prints a message with no decoration
|
||||
# Prints a message with no decoration.
|
||||
#
|
||||
def print(msg)
|
||||
end
|
||||
|
||||
#
|
||||
# Flushes any buffered output.
|
||||
#
|
||||
def flush
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
require 'rex/ui'
|
||||
|
||||
module Rex
|
||||
module Ui
|
||||
|
||||
###
|
||||
#
|
||||
# None
|
||||
# ----
|
||||
#
|
||||
# This output medium implements all the required output routines but does not
|
||||
# back them against any sort of device. This is basically meant to be put in
|
||||
# place of something that expects to be able to deal with a functional output
|
||||
# device when one does not actually exist.
|
||||
#
|
||||
###
|
||||
class Output::None < Rex::Ui::Output
|
||||
end
|
||||
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue