2005-07-16 07:32:11 +00:00
|
|
|
module Msf
|
|
|
|
module Session
|
|
|
|
|
|
|
|
###
|
|
|
|
#
|
|
|
|
# Interactive
|
|
|
|
# -----------
|
|
|
|
#
|
|
|
|
# This class implements the stubs that are needed to provide an interactive
|
|
|
|
# session.
|
|
|
|
#
|
|
|
|
###
|
|
|
|
module Interactive
|
|
|
|
|
2005-07-17 07:06:05 +00:00
|
|
|
#
|
|
|
|
# Interactive sessions by default may interact with the local user input
|
|
|
|
# and output.
|
|
|
|
#
|
|
|
|
include Rex::Ui::Subscriber
|
|
|
|
|
2005-07-18 04:07:56 +00:00
|
|
|
#
|
|
|
|
# Initialize's the session
|
|
|
|
#
|
|
|
|
def initialize(rstream)
|
|
|
|
self.rstream = rstream
|
|
|
|
end
|
|
|
|
|
2005-07-16 07:32:11 +00:00
|
|
|
#
|
|
|
|
# Returns that, yes, indeed, this session supports going interactive with
|
|
|
|
# the user.
|
|
|
|
#
|
|
|
|
def interactive?
|
|
|
|
true
|
|
|
|
end
|
|
|
|
|
|
|
|
#
|
2005-07-18 04:07:56 +00:00
|
|
|
# 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.
|
2005-07-16 07:32:11 +00:00
|
|
|
#
|
|
|
|
def interact
|
2005-07-17 02:04:39 +00:00
|
|
|
self.interacting = true
|
2005-07-18 04:07:56 +00:00
|
|
|
|
|
|
|
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
|
2005-07-16 07:32:11 +00:00
|
|
|
end
|
|
|
|
|
2005-07-18 04:07:56 +00:00
|
|
|
#
|
|
|
|
# The remote stream handle. Must inherit from Rex::IO::Stream.
|
|
|
|
#
|
|
|
|
attr_accessor :rstream
|
2005-07-17 02:04:39 +00:00
|
|
|
#
|
|
|
|
# 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
|
|
|
|
|
2005-07-18 04:07:56 +00:00
|
|
|
#
|
|
|
|
# Stub method that is meant to handler interaction
|
|
|
|
#
|
|
|
|
def _interact
|
|
|
|
end
|
|
|
|
|
2005-07-17 02:04:39 +00:00
|
|
|
#
|
|
|
|
# Checks to see if the user wants to abort
|
|
|
|
#
|
|
|
|
def user_want_abort?
|
2005-07-18 23:32:34 +00:00
|
|
|
prompt_yesno("Abort session #{name}?")
|
2005-07-17 02:04:39 +00:00
|
|
|
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
|
2005-07-18 23:32:34 +00:00
|
|
|
if (prompt_yesno("Background session #{name}?") == true)
|
2005-07-17 02:04:39 +00:00
|
|
|
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
|
|
|
|
|
2005-07-18 23:32:34 +00:00
|
|
|
#
|
|
|
|
# Prompt the user for input if possible.
|
|
|
|
#
|
2005-07-17 02:04:39 +00:00
|
|
|
def prompt(query)
|
2005-07-17 07:06:05 +00:00
|
|
|
if (user_output and user_input)
|
|
|
|
user_output.print("\n" + query)
|
|
|
|
user_input.gets
|
|
|
|
end
|
2005-07-17 02:04:39 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
#
|
2005-07-18 23:32:34 +00:00
|
|
|
# Check the return value of a yes/no prompt
|
2005-07-17 02:04:39 +00:00
|
|
|
#
|
|
|
|
def prompt_yesno(query)
|
2005-07-18 23:32:34 +00:00
|
|
|
(prompt(query + " [y/N] ") =~ /^y/i) ? true : false
|
2005-07-17 02:04:39 +00:00
|
|
|
end
|
2005-07-16 07:32:11 +00:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|