metasploit-framework/lib/msf/core/session/interactive.rb

181 lines
3.2 KiB
Ruby

module Msf
module Session
###
#
# Interactive
# -----------
#
# This class implements the stubs that are needed to provide an interactive
# session.
#
###
module Interactive
#
# Interactive sessions by default may interact with the local user input
# and output.
#
include Rex::Ui::Subscriber
#
# Initialize's the session
#
def initialize(rstream)
self.rstream = rstream
end
#
# Returns that, yes, indeed, this session supports going interactive with
# the user.
#
def interactive?
true
end
#
# Returns the local information
#
def tunnel_local
rstream.localinfo
end
#
# Returns the remote peer information
#
def tunnel_peer
rstream.peerinfo
end
#
# Closes rstream.
#
def cleanup
rstream.close if (rstream)
rstream = nil
end
#
# Starts interacting with the session at the most raw level, simply
# forwarding input from user_input to rstream and forwarding input from
# rstream to user_output.
#
def interact
self.interacting = true
eof = false
# Handle suspend notifications
handle_suspend
callcc { |ctx|
# 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
if (user_want_abort? == true)
eof = true
ctx.call
end
# If we reach EOF or the connection is reset...
rescue EOFError, Errno::ECONNRESET
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)
# Return whether or not EOF was reached
return eof
end
#
# The remote stream handle. Must inherit from Rex::IO::Stream.
#
attr_accessor :rstream
#
# 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
#
# Stub method that is meant to handler interaction
#
def _interact
end
#
# Checks to see if the user wants to abort
#
def user_want_abort?
prompt_yesno("Abort session #{name}?")
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}?") == 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
#
# Prompt the user for input if possible.
#
def prompt(query)
if (user_output and user_input)
user_output.print("\n" + query)
user_input.gets
end
end
#
# Check the return value of a yes/no prompt
#
def prompt_yesno(query)
(prompt(query + " [y/N] ") =~ /^y/i) ? true : false
end
end
end
end