From 2f2363d141beb61a046d288aa61abfadf8a7e716 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Sat, 16 Jul 2005 07:32:11 +0000 Subject: [PATCH] it lives, major changes, fixed bugs, exploiting works with the test exploit git-svn-id: file:///home/svn/incoming/trunk@2763 4d416f70-5f16-0410-b530-b9f4589650da --- lib/msf/base.rb | 5 +- lib/msf/base/sessions/command_shell.rb | 59 +++++++++ lib/msf/base/sessions/command_shell.rb.ut.rb | 9 ++ lib/msf/core/exploit.rb | 3 + lib/msf/core/exploit/tcp.rb | 4 +- lib/msf/core/exploit_driver.rb | 9 +- lib/msf/core/handler.rb | 62 +++++++++- lib/msf/core/handler/reverse_tcp.rb | 23 ++-- lib/msf/core/payload.rb | 8 ++ lib/msf/core/session.rb | 47 ++++++-- lib/msf/core/session/basic.rb | 113 ++++++++++++++++++ lib/msf/core/session/interactive.rb | 41 +++++++ .../provider/multi_command_execution.rb | 58 +++++++++ .../session/provider/multi_command_shell.rb | 64 ++++++++++ .../provider/single_command_execution.rb | 44 +++++++ .../session/provider/single_command_shell.rb | 48 ++++++++ .../ui/console/command_dispatcher/exploit.rb | 4 +- lib/rex.rb | 2 + lib/rex/io/stream.rb | 26 ++-- lib/rex/socket/comm/local.rb | 2 + lib/rex/socket/tcp.rb | 21 ++-- lib/rex/socket/tcp_server.rb | 9 +- lib/rex/sync/event.rb | 83 +++++++++++++ lib/rex/ui/text/input.rb | 8 ++ lib/rex/ui/text/input/readline.rb | 17 +++ lib/rex/ui/text/input/stdio.rb | 4 + lib/rex/ui/text/shell.rb | 10 +- modules/exploits/test/multi/aggressive.rb | 5 + .../singles/linux/x86/shell_reverse_tcp.rb | 2 + 29 files changed, 728 insertions(+), 62 deletions(-) create mode 100644 lib/msf/base/sessions/command_shell.rb create mode 100644 lib/msf/base/sessions/command_shell.rb.ut.rb create mode 100644 lib/msf/core/session/basic.rb create mode 100644 lib/msf/core/session/interactive.rb create mode 100644 lib/msf/core/session/provider/multi_command_execution.rb create mode 100644 lib/msf/core/session/provider/multi_command_shell.rb create mode 100644 lib/msf/core/session/provider/single_command_execution.rb create mode 100644 lib/msf/core/session/provider/single_command_shell.rb create mode 100644 lib/rex/sync/event.rb diff --git a/lib/msf/base.rb b/lib/msf/base.rb index 315396a8dd..346b009486 100644 --- a/lib/msf/base.rb +++ b/lib/msf/base.rb @@ -25,10 +25,7 @@ require 'msf/base/config' require 'msf/base/simple' # Sessions -require 'msf/base/session/command_shell' -require 'msf/base/session/meterpreter' -require 'msf/base/session/dispatch_ninja' -require 'msf/base/session/vnc' +require 'msf/base/sessions/command_shell' # Serialization require 'msf/base/serializer/readable_text' diff --git a/lib/msf/base/sessions/command_shell.rb b/lib/msf/base/sessions/command_shell.rb new file mode 100644 index 0000000000..164c107e16 --- /dev/null +++ b/lib/msf/base/sessions/command_shell.rb @@ -0,0 +1,59 @@ +require 'msf/base' + +module Msf +module Sessions + +### +# +# CommandShell +# ------------ +# +# This class provides basic interaction with a command shell on the remote +# endpoint. This session is initialized with a stream that will be used +# as the pipe for reading and writing the command shell. +# +### +class CommandShell + + # + # This interface supports basic interaction. + # + include Msf::Session::Basic + + # + # This interface supports interacting with a single command shell. + # + include Msf::Session::Provider::SingleCommandShell + + # + # The shell will have been initialized by default + # + def init_shell + return true + end + + # + # Read from the command shell + # + def read_shell(length = nil) + return rstream.read(length) + end + + # + # Writes to the command shell + # + def write_shell(buf) + rstream.write(buf) + end + + # + # Closes the shell + # + def close_shell() + rstream.close + end + +end + +end +end diff --git a/lib/msf/base/sessions/command_shell.rb.ut.rb b/lib/msf/base/sessions/command_shell.rb.ut.rb new file mode 100644 index 0000000000..98fe1d4037 --- /dev/null +++ b/lib/msf/base/sessions/command_shell.rb.ut.rb @@ -0,0 +1,9 @@ +#!/usr/bin/ruby + +require 'test/unit' +require 'msf/base' + +class Msf::Sessions::CommandShell::UnitTest < Test::Unit::TestCase + def test_cmdshell + end +end diff --git a/lib/msf/core/exploit.rb b/lib/msf/core/exploit.rb index a3f3a9057f..7000a42a8d 100644 --- a/lib/msf/core/exploit.rb +++ b/lib/msf/core/exploit.rb @@ -206,6 +206,9 @@ class Exploit < Msf::Module # Set the encoded payload to the result of the encoding process self.payload = EncodedPayload.create(real_payload, reqs) + # Save the payload instance + self.payload_instance = real_payload + return self.payload end diff --git a/lib/msf/core/exploit/tcp.rb b/lib/msf/core/exploit/tcp.rb index 841bb2c3ae..92512e7e65 100644 --- a/lib/msf/core/exploit/tcp.rb +++ b/lib/msf/core/exploit/tcp.rb @@ -28,8 +28,8 @@ module Exploit::Remote::Tcp nsock = Rex::Socket::Tcp.create( 'PeerHost' => datastore['RHOST'], 'PeerPort' => datastore['RPORT'].to_i, - 'LocalHost' => datastore['LHOST'] || "0.0.0.0", - 'LocalPort' => datastore['LPORT'] ? datastore['LPORT'].to_i : 0) + 'LocalHost' => datastore['CHOST'] || "0.0.0.0", + 'LocalPort' => datastore['CPORT'] ? datastore['CPORT'].to_i : 0) # Set this socket to the global socket as necessary self.sock = nsock if (global) diff --git a/lib/msf/core/exploit_driver.rb b/lib/msf/core/exploit_driver.rb index 89069207e3..7a12dcc0b3 100644 --- a/lib/msf/core/exploit_driver.rb +++ b/lib/msf/core/exploit_driver.rb @@ -104,6 +104,9 @@ class ExploitDriver # module instance exploit.generate_payload(payload) + # Default the session to nil + session = nil + begin # Set the exploit up the bomb exploit.setup @@ -111,8 +114,8 @@ class ExploitDriver # Launch the exploit exploit.exploit - # Give the payload some extra time after the exploit completes - payload.extra_delay + # Wait the payload to acquire a session + session = payload.wait_for_session ensure # Ensure that, no matter what, clean up of the handler occurs payload.stop_handler @@ -121,7 +124,7 @@ class ExploitDriver exploit.cleanup end - return nil + return session end attr_reader :target_idx diff --git a/lib/msf/core/handler.rb b/lib/msf/core/handler.rb index 3c0c4304d5..1900730d82 100644 --- a/lib/msf/core/handler.rb +++ b/lib/msf/core/handler.rb @@ -45,6 +45,17 @@ module Handler return "none" end + # + # Initializes the session waiter event and other fun stuff. + # + def initialize(info = {}) + super + + # Create the waiter event with auto_reset set to false so that + # if a session is ever created, waiting on it returns immediately. + self.session_waiter_event = Rex::Sync::Event.new(false, false) + end + # # Sets up the connection handler # @@ -81,20 +92,61 @@ module Handler # Handles an established connection supplied in the in and out # handles. The handles are passed as parameters in case this # handler is capable of handling multiple simultaneous - # connections. + # connections. The default implementation simply creates a + # session using the payload's session factory reference and + # the supplied stream. # - def handle_connection(stream) + def handle_connection(conn) + # If the payload we merged in with has an associated session factory, + # allocate a new session. + if (self.session) + s = self.session.new(conn) + + # If the session is valid, register it with the framework and + # notify any waiters we may have. + if (s) + register_session(s) + end + end end # - # Wait just one second there! + # The amount of time to wait for a session to come in. # - def extra_delay - sleep(1) + def wfs_delay + 1 + end + + # + # Waits for a session to be created as the result of a handler connection + # coming in. The return value is a session object instance on success or + # nil if the timeout expires + # + def wait_for_session(t = wfs_delay) + session = nil + + begin + session = session_waiter_event.wait(t) + rescue ::TimeoutError + end + + return session end protected + # + # Registers a session with the framework and notifies any waiters of the + # new session. + # + def register_session(session) + session_waiter_event.notify(session) + + # TODO: register with the framework + end + + attr_accessor :session_waiter_event + end end diff --git a/lib/msf/core/handler/reverse_tcp.rb b/lib/msf/core/handler/reverse_tcp.rb index a017a8cf72..0135ece1c8 100644 --- a/lib/msf/core/handler/reverse_tcp.rb +++ b/lib/msf/core/handler/reverse_tcp.rb @@ -40,20 +40,19 @@ module ReverseTcp # if it fails to start the listener # def setup_handler - listener_sock = comm.create( + self.listener_sock = Rex::Socket::TcpServer.create( 'LocalHost' => datastore['LHOST'], 'LocalPort' => datastore['LPORT'].to_i, - 'Server' => true, - 'Proto' => 'tcp') + 'Comm' => comm) end # # Closes the listener socket if one was created # def cleanup_handler - if (listener_sock) - listener_sock.close - listener_sock = nil + if (self.listener_sock) + self.listener_sock.close + self.listener_sock = nil end # Kill any remaining handle_connection threads that might @@ -68,13 +67,15 @@ module ReverseTcp # def start_handler listener_thread = Thread.new { + client = nil # Accept a client connection begin - client = listener_sock.accept + client = self.listener_sock.accept rescue wlog("Exception raised during listener accept: #{$!}") + return nil end - + # Start a new thread and pass the client connection # as the input and output pipe. Client's are expected # to implement the Stream interface. @@ -89,9 +90,9 @@ module ReverseTcp # def stop_handler # Terminate the listener thread - if (listener_thread and listener_thread.alive? == true) - listener_thread.kill - listener_thread = nil + if (self.listener_thread and self.listener_thread.alive? == true) + self.listener_thread.kill + self.listener_thread = nil end end diff --git a/lib/msf/core/payload.rb b/lib/msf/core/payload.rb index 549d6af75f..243bfbea8b 100644 --- a/lib/msf/core/payload.rb +++ b/lib/msf/core/payload.rb @@ -96,6 +96,14 @@ class Payload < Msf::Module return module_info['Handler'] || Msf::Handler end + # + # Returns the session class that is associated with this payload and will + # be used to create a session as necessary. + # + def session + return module_info['Session'] + end + ## # # Generation & variable substitution diff --git a/lib/msf/core/session.rb b/lib/msf/core/session.rb index 47e93b5678..a44b287b0a 100644 --- a/lib/msf/core/session.rb +++ b/lib/msf/core/session.rb @@ -28,7 +28,7 @@ end # ------- # # The session class represents a post-exploitation, uh, session. -# Sessions can be written from, read to, and interacted with. The +# Sessions can be written to, read from, and interacted with. The # underlying medium on which they are backed is arbitrary. For # instance, when an exploit is provided with a command shell, # either through a network connection or locally, the session's @@ -39,9 +39,25 @@ end # tied to a network connection. # ### -class Session +module Session - def initialize() + include Framework::Offspring + + # Direct descendents + require 'msf/core/session/interactive' + require 'msf/core/session/basic' + + # Provider interfaces + require 'msf/core/session/provider/single_command_execution' + require 'msf/core/session/provider/multi_command_execution' + require 'msf/core/session/provider/single_command_shell' + require 'msf/core/session/provider/multi_command_shell' + + # + # By default, sessions are not interactive. + # + def interactive? + false end # @@ -50,18 +66,25 @@ class Session def cleanup end - attr_accessor :framework, :sid + # + # Returns the session's name if it's been assigned one, otherwise + # the sid is returned. + # + def sname + return name || sid + end + + # + # Sets the session's name + # + def sname=(name) + self.name = name + end + + attr_accessor :framework, :sid, :name protected end end - -# -# Require the individual provider interfaces -# -require 'msf/core/session_provider/single_command_execution' -require 'msf/core/session_provider/multi_command_execution' -require 'msf/core/session_provider/single_command_shell' -require 'msf/core/session_provider/multi_command_shell' diff --git a/lib/msf/core/session/basic.rb b/lib/msf/core/session/basic.rb new file mode 100644 index 0000000000..25a1684eb2 --- /dev/null +++ b/lib/msf/core/session/basic.rb @@ -0,0 +1,113 @@ +module Msf +module Session + +### +# +# Basic +# ----- +# +# This class implements an interactive session using raw input/output in +# only the most basic fashion. +# +### +module Basic + + include Session + include Interactive + + # + # Initialize's the raw session + # + def initialize(rstream) + self.rstream = rstream + end + + # + # Returns that, yes, indeed, this session supports going interactive with + # the user. + # + def interactive? + true + 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 linput to rstream and forwarding input from + # rstream to loutput. + # + def interact + callcc { |ctx| + while 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 #{sname}? [y/N] ") + + r = linput.gets + + # Break out of the continuation + ctx.call if (r =~ /^y/i) + rescue EOFError + loutput.print_line("Session #{sname} terminating...") + ctx.call + end + end + } + end + + # + # The local input handle. Must inherit from Rex::Ui::Text::Input. + # + attr_accessor :linput + # + # The local output handle. Must inherit from Rex::Ui::Output. + # + attr_accessor :loutput + # + # The remote stream handle. Must inherit from Rex::IO::Stream. + # + attr_accessor :rstream + +protected + + # + # Performs the actual raw interaction with the remote side. This can be + # overriden by derived classes if they wish to do this another way. + # + def _interact + while true + # Select input and rstream + sd = select([ linput.fd, rstream.fd ], nil, nil, 0.5) + + # Cycle through the items that have data + # From the rstream? Write to linput. + sd[0].each { |s| + if (s == rstream.fd) + data = rstream.get + + loutput.print(data) + # From linput? Write to rstream. + elsif (s == linput.fd) + data = linput.gets + + rstream.put(data) + end + } if (sd) + end + end + +end + +end +end diff --git a/lib/msf/core/session/interactive.rb b/lib/msf/core/session/interactive.rb new file mode 100644 index 0000000000..c31ba92c58 --- /dev/null +++ b/lib/msf/core/session/interactive.rb @@ -0,0 +1,41 @@ +module Msf +module Session + +### +# +# Interactive +# ----------- +# +# This class implements the stubs that are needed to provide an interactive +# session. +# +### +module Interactive + + # + # Returns that, yes, indeed, this session supports going interactive with + # the user. + # + def interactive? + true + end + + # + # Starts interacting with the session. + # + def interact + end + + # + # The local input handle. Must inherit from Rex::Ui::Text::Input. + # + attr_accessor :linput + # + # The local output handle. Must inherit from Rex::Ui::Output. + # + attr_accessor :loutput + +end + +end +end diff --git a/lib/msf/core/session/provider/multi_command_execution.rb b/lib/msf/core/session/provider/multi_command_execution.rb new file mode 100644 index 0000000000..b4e04a85a1 --- /dev/null +++ b/lib/msf/core/session/provider/multi_command_execution.rb @@ -0,0 +1,58 @@ +module Msf +module Session +module Provider + +### +# +# MultiCommandExecution +# --------------------- +# +# Executes multiple commands and optionally allows for reading/writing I/O +# to the new processes. +# +### +module MultiCommandExecution + + # + # Initializes the single command instance that will be used + # implicitly if no command is supplied to any of the functions. + # + def init_cmd(command, arguments = nil, opts = nil) + end + + # + # Executes a command with the supplied options, returning a context + # that should be supplied to future calls. Supported options: + # + # - Hidden + # Launch the command hidden from view. + # + def exec_cmd(command, arguments = nil, opts = nil) + end + + # + # Reads output from a command. If no command is supplied, the default + # command is used. + # + def read_cmd(length = nil, cmd = nil) + end + + # + # Writes input to a command. If no command is supplied, the default + # command is used. + # + def write_cmd(buf, cmd = nil) + end + + # + # Closes a command that was executed. If no command is supplied, the + # default command is used. + # + def close_cmd(cmd = nil) + end + +end + +end +end +end diff --git a/lib/msf/core/session/provider/multi_command_shell.rb b/lib/msf/core/session/provider/multi_command_shell.rb new file mode 100644 index 0000000000..c3d8741642 --- /dev/null +++ b/lib/msf/core/session/provider/multi_command_shell.rb @@ -0,0 +1,64 @@ +module Msf +module Session +module Provider + +### +# +# MultiCommandShell +# ----------------- +# +# This interface is to be implemented by a session that is capable of +# providing multiple command shell interfaces simultaneously. Inherently, +# MultiCommandShell classes must also provide a mechanism by which they can +# implement the SingleCommandShell interface. +# +# +### +module MultiCommandShell + + include SingleCommandShell + + # + # Initializes the default command shell as expected from + # SingleCommandShell + # + def init_shell() + raise NotImplementedError + end + + # + # Opens a new command shell context and returns the handle + # + def open_shell() + raise NotImplementedError + end + + # + # Reads data from a command shell. If shell is nil, the default + # command shell from init_shell is used. + # + def read_shell(length = nil, shell = nil) + raise NotImplementedError + end + + # + # Writes data to a command shell. If shell is nil, the default + # command shell from init_shell is used. + # + def write_shell(buf, shell = nil) + raise NotImplementedError + end + + # + # Closes the provided command shell or the default one if none is + # given. + # + def close_shell(shell = nil) + raise NotImplementedError + end + +end + +end +end +end diff --git a/lib/msf/core/session/provider/single_command_execution.rb b/lib/msf/core/session/provider/single_command_execution.rb new file mode 100644 index 0000000000..cc50b939cd --- /dev/null +++ b/lib/msf/core/session/provider/single_command_execution.rb @@ -0,0 +1,44 @@ +module Msf +module Session +module Provider + +### +# +# SingleCommandExecution +# ---------------------- +# +# Executes a single command and optionally allows for reading/writing I/O +# to the new process. +# +### +module SingleCommandExecution + + # + # Initializes the executed command for reading/writing. + # + def init_cmd(command, arguments = nil, opts = nil) + end + + # + # Reads output from the command + # + def read_cmd(length = nil) + end + + # + # Writes input to the command + # + def write_cmd(buf) + end + + # + # Closes the command that was executed + # + def close_cmd() + end + +end + +end +end +end diff --git a/lib/msf/core/session/provider/single_command_shell.rb b/lib/msf/core/session/provider/single_command_shell.rb new file mode 100644 index 0000000000..cad4774256 --- /dev/null +++ b/lib/msf/core/session/provider/single_command_shell.rb @@ -0,0 +1,48 @@ +module Msf +module Session +module Provider + +### +# +# SingleCommandShell +# ------------------ +# +# This interface is to be implemented by a session that is only capable of +# providing an interface to a single command shell. +# +### +module SingleCommandShell + + # + # Initializes the command shell + # + def init_shell() + raise NotImplementedError + end + + # + # Reads data from the command shell + # + def read_shell(length = nil) + raise NotImplementedError + end + + # + # Writes data to the command shell + # + def write_shell(buf) + raise NotImplementedError + end + + # + # Closes the command shell + # + def close_shell() + raise NotImplementedError + end + +end + +end +end +end diff --git a/lib/msf/ui/console/command_dispatcher/exploit.rb b/lib/msf/ui/console/command_dispatcher/exploit.rb index 068f04eb80..d05f6fcdd8 100644 --- a/lib/msf/ui/console/command_dispatcher/exploit.rb +++ b/lib/msf/ui/console/command_dispatcher/exploit.rb @@ -110,8 +110,8 @@ class Exploit # interacted with, start interacting with it if (bg == false and session.interactive?) # Set the session's input and output handles - session.local_input = driver.input - session.local_output = driver.output + session.linput = driver.input + session.loutput = driver.output # Interact session.interact() diff --git a/lib/rex.rb b/lib/rex.rb index d8585e2fd3..f203e0d7fb 100644 --- a/lib/rex.rb +++ b/lib/rex.rb @@ -8,6 +8,7 @@ require 'rex/read_write_lock' require 'rex/transformer' require 'rex/text' require 'rex/string_utils' +require 'rex/sync/event' # Encoding require 'rex/encoder/xor' @@ -27,6 +28,7 @@ require 'rex/io/stream_server' require 'rex/socket' require 'rex/socket/parameters' require 'rex/socket/tcp' +require 'rex/socket/tcp_server' require 'rex/socket/comm/local' # Parsers diff --git a/lib/rex/io/stream.rb b/lib/rex/io/stream.rb index eeea8993b5..0b21174d92 100644 --- a/lib/rex/io/stream.rb +++ b/lib/rex/io/stream.rb @@ -21,42 +21,36 @@ module Stream # Set the stream to blocking or non-blocking # def blocking=(tf) - super end # # Check to see if the stream is blocking or non-blocking # def blocking - super end # # Writes data to the stream. # def write(buf, opts = {}) - super end # # Reads data from the stream. # def read(length = nil, opts = {}) - super end # # Shuts down the stream for reading, writing, or both. # def shutdown(how = SW_BOTH) - super end # # Closes the stream and allows for resource cleanup # def close - super end # @@ -64,14 +58,19 @@ module Stream # true if data is available for reading, otherwise false is returned. # def has_read_data?(timeout = nil) - super end # # Returns the file descriptor that can be polled via select, if any. # def poll_fd - super + end + + # + # Wrapper for poll_fd + # + def fd + poll_fd end ## @@ -141,7 +140,7 @@ module Stream end # No data in the first place? bust. - if (!has_read_data?(timeout)) + if (has_read_data?(timeout) == false) return nil end @@ -149,10 +148,15 @@ module Stream lps = 0 # Keep looping until there is no more data to be gotten.. - while (has_read_data?(ltimeout)) + while (has_read_data?(ltimeout) == true) temp = read(def_block_size) - break if (temp == nil or temp.empty?) + # If we read zero bytes and we had data, then we've hit EOF + if (temp and temp.length == 0) + raise EOFError + end + + break if (temp == nil or temp.empty? == true) buf += temp lps += 1 diff --git a/lib/rex/socket/comm/local.rb b/lib/rex/socket/comm/local.rb index 8506b9df80..efe0b7b166 100644 --- a/lib/rex/socket/comm/local.rb +++ b/lib/rex/socket/comm/local.rb @@ -41,6 +41,8 @@ class Rex::Socket::Comm::Local sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, 1) end + sock.setsockopt(Socket::SOL_SOCKET, Socket::SO_REUSEADDR, true) + sock.bind(Rex::Socket.to_sockaddr(param.localhost, param.localport)) rescue Errno::EADDRINUSE raise Rex::AddressInUse.new(param.localhost, param.localport), caller diff --git a/lib/rex/socket/tcp.rb b/lib/rex/socket/tcp.rb index 5b572209c9..b4cb93208f 100644 --- a/lib/rex/socket/tcp.rb +++ b/lib/rex/socket/tcp.rb @@ -43,24 +43,31 @@ class Rex::Socket::Tcp < Rex::Socket return sock.syswrite(buf) end + # + # Raises EOFError if it reaches end-of-file + # def read(length = nil, opts = {}) length = 16384 unless length - begin - return sock.sysread(length) - rescue EOFError - return nil - end + return sock.sysread(length) end def shutdown(how = SHUT_RDWR) return (sock.shutdown(how) == 0) end - def has_read_data?(timeout = nil) + def has_read_data?(timeout = 0) timeout = timeout.to_i if (timeout) - + return (select([ poll_fd ], nil, nil, timeout) != nil) end + def close + self.sock.close if (self.sock) + end + + def poll_fd + return self.sock + end + end diff --git a/lib/rex/socket/tcp_server.rb b/lib/rex/socket/tcp_server.rb index ba4b549529..7cd0094d4b 100644 --- a/lib/rex/socket/tcp_server.rb +++ b/lib/rex/socket/tcp_server.rb @@ -21,6 +21,13 @@ class Rex::Socket::TcpServer < Rex::Socket # ## + # + # Creates the server using the supplied hash + # + def self.create(hash) + self.create_param(Rex::Socket::Parameters.from_hash(hash)) + end + # # Wrapper around the base class' creation method that automatically sets # the parameter's protocol to TCP and sets the server flag to true @@ -48,7 +55,7 @@ class Rex::Socket::TcpServer < Rex::Socket # Accepts a child connection # def accept(opts = {}) - return Rex::Socket::Tcp.new(sock.accept[0]) + Rex::Socket::Tcp.new(self.sock.accept[0]) end # diff --git a/lib/rex/sync/event.rb b/lib/rex/sync/event.rb new file mode 100644 index 0000000000..9b94a0470b --- /dev/null +++ b/lib/rex/sync/event.rb @@ -0,0 +1,83 @@ +require 'thread' + +module Rex +module Sync + +### +# +# Event +# ----- +# +# This class wraps the logical ConditionVariable class to make it an easier to +# work with interface that is similar to Windows' synchronization events. +# +### +class Event + + Infinite = -1 + + def initialize(state = false, auto_reset = true, param = nil) + self.state = state + self.auto_reset = auto_reset + self.param = param + self.mutex = Mutex.new + self.cond = ConditionVariable.new + end + + # + # Sets the event and wakes up anyone who was waiting. + # + def set(param = nil) + self.param = param + + self.mutex.synchronize { + # If this event does not automatically reset its state, + # set the state to true + if (auto_reset == false) + self.state = true + end + + self.cond.broadcast + } + end + + # + # Resets the signaled state to false. + # + def reset + self.param = nil + self.state = false + end + + # + # Alias notify with set + # + alias notify set + + # + # Waits for the event to become signaled. Timeout is measured in + # seconds. Raises TimeoutError if the condition does not become signaled. + # + def wait(t = Infinite) + callcc { |ctx| + self.mutex.synchronize { + ctx.call if (self.state == true) + + timeout(t) { + self.cond.wait(self.mutex) + } + } + } + + return self.param + end + +protected + + attr_accessor :state, :auto_reset + attr_accessor :param, :mutex, :cond + +end + +end +end diff --git a/lib/rex/ui/text/input.rb b/lib/rex/ui/text/input.rb index a5ce87bb20..cbc5432816 100644 --- a/lib/rex/ui/text/input.rb +++ b/lib/rex/ui/text/input.rb @@ -37,6 +37,14 @@ class Input return eof end + # + # Returns a pollable file descriptor that is associated with this + # input medium. + # + def fd + raise NotImplementedError + end + # # Indicates whether or not this input medium is intrinsicly a # shell provider. This would indicate whether or not it diff --git a/lib/rex/ui/text/input/readline.rb b/lib/rex/ui/text/input/readline.rb index e37754092f..3f1b49a6ba 100644 --- a/lib/rex/ui/text/input/readline.rb +++ b/lib/rex/ui/text/input/readline.rb @@ -25,7 +25,17 @@ begin end end + # + # Regular old gets + # def gets + self.fd.gets + end + + # + # Prompt-based getline + # + def pgets if ((line = readline(prompt, true))) HISTORY.pop if (line.empty?) return line + "\n" @@ -35,6 +45,13 @@ begin end end + # + # Returns the $stdin handle. + # + def fd + $stdin + end + # # Indicates that this input medium as a shell builtin, no need # to extend. diff --git a/lib/rex/ui/text/input/stdio.rb b/lib/rex/ui/text/input/stdio.rb index 0b53966502..e97cc10116 100644 --- a/lib/rex/ui/text/input/stdio.rb +++ b/lib/rex/ui/text/input/stdio.rb @@ -20,6 +20,10 @@ class Input::Stdio < Rex::Ui::Text::Input def eof? return $stdin.eof? end + + def fd + return $stdin + end end end diff --git a/lib/rex/ui/text/shell.rb b/lib/rex/ui/text/shell.rb index 0206e9c12e..82887a8882 100644 --- a/lib/rex/ui/text/shell.rb +++ b/lib/rex/ui/text/shell.rb @@ -27,9 +27,9 @@ module Shell module InputShell attr_accessor :prompt, :output - def gets + def pgets output.print(prompt) - super + gets end end @@ -75,7 +75,7 @@ module Shell def run stop_flag = false - while ((line = input.gets)) + while ((line = input.pgets)) run_single(line) break if (input.eof? or self.stop_flag) @@ -175,6 +175,7 @@ module Shell end attr_accessor :disable_output + attr_reader :input, :output protected @@ -194,7 +195,8 @@ protected end - attr_accessor :input, :output, :stop_flag, :init_prompt + attr_writer :input, :output + attr_accessor :stop_flag, :init_prompt attr_accessor :prompt_char, :tab_complete_proc end diff --git a/modules/exploits/test/multi/aggressive.rb b/modules/exploits/test/multi/aggressive.rb index 1c188a73cf..f6d4665bc9 100644 --- a/modules/exploits/test/multi/aggressive.rb +++ b/modules/exploits/test/multi/aggressive.rb @@ -38,6 +38,11 @@ class Exploits::Test::Multi::Aggressive < Msf::Exploit::Remote def exploit connect + #puts "raw:" + #puts Rex::Text.to_c(payload.raw) + #puts "encoded:" + #puts Rex::Text.to_c(payload.encoded) + sock.put(payload.encoded) handler diff --git a/modules/payloads/singles/linux/x86/shell_reverse_tcp.rb b/modules/payloads/singles/linux/x86/shell_reverse_tcp.rb index a4ef5c03db..da91d6a321 100644 --- a/modules/payloads/singles/linux/x86/shell_reverse_tcp.rb +++ b/modules/payloads/singles/linux/x86/shell_reverse_tcp.rb @@ -1,5 +1,6 @@ require 'msf/core' require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/command_shell' module Msf module Payloads @@ -20,6 +21,7 @@ module Shell 'Platform' => 'linux', 'Arch' => ARCH_X86, 'Handler' => Msf::Handler::ReverseTcp, + 'Session' => Msf::Sessions::CommandShell, 'Payload' => { 'Offsets' =>