From db40a555479577a44e50383d43b0216175db5a88 Mon Sep 17 00:00:00 2001 From: Matt Miller Date: Sun, 2 Oct 2005 03:21:26 +0000 Subject: [PATCH] session logging support git-svn-id: file:///home/svn/incoming/trunk@2938 4d416f70-5f16-0410-b530-b9f4589650da --- documentation/plan.txt | 4 +- lib/msf/base/logging.rb | 49 ++++++++++++++++ lib/msf/base/sessions/meterpreter.rb | 2 + lib/msf/core/session.rb | 32 +++++++++++ lib/msf/ui/console/command_dispatcher/core.rb | 4 +- lib/msf/ui/console/driver.rb | 52 +++++++++++++++-- lib/msf/ui/console/framework_event_manager.rb | 9 +++ lib/rex/logging/log_dispatcher.rb | 2 +- lib/rex/ui/text/output/buffer.rb | 2 + lib/rex/ui/text/output/stdio.rb | 2 + lib/rex/ui/text/shell.rb | 56 +++++++++++++++++-- 11 files changed, 198 insertions(+), 16 deletions(-) diff --git a/documentation/plan.txt b/documentation/plan.txt index c62072c947..8b95e52663 100644 --- a/documentation/plan.txt +++ b/documentation/plan.txt @@ -26,8 +26,8 @@ X - meta information X - stager/stage calling conventions X - stack requirements X - make payload prepend target specific - - sessions - - logging session activity +X - sessions +X - logging session activity - handler sharing - exploits using the same payload/handler can share (ref count) - modules needing ports (above other modules) diff --git a/lib/msf/base/logging.rb b/lib/msf/base/logging.rb index 4ced09d4de..82a7cc9f91 100644 --- a/lib/msf/base/logging.rb +++ b/lib/msf/base/logging.rb @@ -27,12 +27,61 @@ class Logging [ 'rex', 'core', + 'base', ].each { |src| register_log_source(src, f) } end end + # + # Enables a log source. + # + def self.enable_log_source(src) + f = Rex::Logging::Sinks::Flatfile.new( + Msf::Config.log_directory + File::SEPARATOR + "#{src}.log") + + register_log_source(src, f) + end + + # + # Stops logging for a given log source. + # + def self.disable_log_source(src) + deregister_log_source(src) + end + + # + # Sets whether or not session logging is to be enabled. + # + def self.enable_session_logging(tf) + @session_logging = tf + end + + # + # Returns whether or not session logging is enabled. + # + def self.session_logging_enabled? + @session_logging || false + end + + # + # Starts logging for a given session. + # + def self.start_session_log(session) + f = Rex::Logging::Sinks::Flatfile.new( + Msf::Config.session_log_directory + File::SEPARATOR + "#{session.log_file_name}.log") + + register_log_source(session.log_source, f) + end + + # + # Stops logging for a given session. + # + def self.stop_session_log(session) + deregister_log_source(session.log_source) + end + end end diff --git a/lib/msf/base/sessions/meterpreter.rb b/lib/msf/base/sessions/meterpreter.rb index c24f851501..4f95e808f0 100644 --- a/lib/msf/base/sessions/meterpreter.rb +++ b/lib/msf/base/sessions/meterpreter.rb @@ -66,12 +66,14 @@ class Meterpreter < Rex::Post::Meterpreter::Client # def init_ui(input, output) console.init_ui(input, output) + console.set_log_source(log_source) end # # Resets the console's I/O handles # def reset_ui + console.unset_log_source console.reset_ui end diff --git a/lib/msf/core/session.rb b/lib/msf/core/session.rb index f08b9cce18..a18fbda56e 100644 --- a/lib/msf/core/session.rb +++ b/lib/msf/core/session.rb @@ -104,6 +104,38 @@ module Session "#{(tunnel_local || '??').to_s} -> #{(tunnel_peer || '??').to_s}" end + ## + # + # Logging + # + ## + + # + # Returns the suggested name of the log file for this session. + # + def log_file_name + dt = Time.now + + dstr = sprintf("%.4d%.2d%.2d", dt.year, dt.mon, dt.mday) + + ("#{dstr}_" + tunnel_to_s + "_#{name.to_s}_#{(type || 'unknown').downcase}").gsub(/\s/, '_').gsub(/->/, 'to') + end + + # + # Returns the log source that should be used for this session. + # + def log_source + "session_#{name.to_s}" + end + + def log_from_remote(buf) + rlog(buf, log_source) + end + + def log_from_local(buf) + rlog(buf, log_source) + end + ## # # Core interface diff --git a/lib/msf/ui/console/command_dispatcher/core.rb b/lib/msf/ui/console/command_dispatcher/core.rb index 18b0c0d4cf..868292388f 100644 --- a/lib/msf/ui/console/command_dispatcher/core.rb +++ b/lib/msf/ui/console/command_dispatcher/core.rb @@ -354,7 +354,7 @@ class Core value = args[1] # If the driver indicates that the value is not valid, bust out. - if (driver.on_variable_set(name, value) == false) + if (driver.on_variable_set(global, name, value) == false) print_error("The value specified for #{name} is not valid.") return true end @@ -446,7 +446,7 @@ class Core end while ((val = args.shift)) - if (driver.on_variable_unset(val) == false) + if (driver.on_variable_unset(global, val) == false) print_error("The variable #{val} cannot be unset at this time.") next end diff --git a/lib/msf/ui/console/driver.rb b/lib/msf/ui/console/driver.rb index 46f5b61369..edcf552475 100644 --- a/lib/msf/ui/console/driver.rb +++ b/lib/msf/ui/console/driver.rb @@ -21,6 +21,7 @@ module Console ### class Driver < Msf::Ui::Driver + ConfigCore = "framework/core" ConfigGroup = "framework/ui/console" # @@ -47,11 +48,17 @@ class Driver < Msf::Ui::Driver # Register event handlers register_event_handlers - # Process things before we actually display the prompt and get rocking - on_startup + # Temporarily disable output + self.disable_output = true # Load console-specific configuration load_config + + # Re-enable output + self.disable_output = false + + # Process things before we actually display the prompt and get rocking + on_startup # Process the resource script process_rc_file @@ -71,12 +78,21 @@ class Driver < Msf::Ui::Driver # If we have configuration, process it if (conf.group?(ConfigGroup)) conf[ConfigGroup].each_pair { |k, v| - case k - when "ActiveModule" + case k.downcase + when "activemodule" run_single("use #{v}") end } end + + if (conf.group?(ConfigCore)) + conf[ConfigCore].each_pair { |k, v| + case k.downcase + when "sessionlogging" + handle_session_logging(v) + end + } + end end # @@ -125,12 +141,14 @@ class Driver < Msf::Ui::Driver # some other kind of task. If this routine returns false it will indicate # that the variable is not being set to a valid value. # - def on_variable_set(var, val) + def on_variable_set(glob, var, val) case var.downcase when "payload" if (framework.modules.valid?(val) == false) return false end + when "sessionlogging" + handle_session_logging(val) if (glob) end end @@ -138,7 +156,11 @@ class Driver < Msf::Ui::Driver # Called when a variable is unset. If this routine returns false it is an # indication that the variable should not be allowed to be unset. # - def on_variable_unset(var) + def on_variable_unset(glob, var) + case var.downcase + when "sessionlogging" + handle_session_logging('0') if (glob) + end end attr_reader :framework @@ -147,7 +169,25 @@ class Driver < Msf::Ui::Driver protected attr_writer :framework + + ## + # + # Handlers for various global configuration values + # + ## + # + # SessionLogging + # + def handle_session_logging(val) + if (val =~ /^(yes|y|true|t|1)/i) + Msf::Logging.enable_session_logging(true) + print_line("Session logging will be enabled for future sessions.") + else + Msf::Logging.enable_session_logging(false) + print_line("Session logging will be disabled for future sessions.") + end + end end end diff --git a/lib/msf/ui/console/framework_event_manager.rb b/lib/msf/ui/console/framework_event_manager.rb index 806985b4d3..0ac6c19036 100644 --- a/lib/msf/ui/console/framework_event_manager.rb +++ b/lib/msf/ui/console/framework_event_manager.rb @@ -33,6 +33,12 @@ module FrameworkEventManager # def on_session_open(session) output.print_status("#{session.desc} session #{session.name} opened (#{session.tunnel_to_s})") + + if (Msf::Logging.session_logging_enabled? == true) + Msf::Logging.start_session_log(session) + + output.print_status("Started logging session interaction.") + end end # @@ -43,6 +49,9 @@ module FrameworkEventManager output.print_line end + # If logging had been enabled for this session, stop it now. + Msf::Logging::stop_session_log(session) + output.print_status("#{session.desc} session #{session.name} closed.") end diff --git a/lib/rex/logging/log_dispatcher.rb b/lib/rex/logging/log_dispatcher.rb index 06afc4c8b0..70094fe5b4 100644 --- a/lib/rex/logging/log_dispatcher.rb +++ b/lib/rex/logging/log_dispatcher.rb @@ -125,7 +125,7 @@ def register_log_source(src, sink) $dispatcher[src] = sink end -def deregister_log_source(src, sink) +def deregister_log_source(src) $dispatcher.delete(src) end diff --git a/lib/rex/ui/text/output/buffer.rb b/lib/rex/ui/text/output/buffer.rb index 4b7a08d812..b209dcfd96 100644 --- a/lib/rex/ui/text/output/buffer.rb +++ b/lib/rex/ui/text/output/buffer.rb @@ -20,6 +20,8 @@ class Output::Buffer < Rex::Ui::Text::Output def print(msg = '') self.buf += msg || '' + + msg end def reset diff --git a/lib/rex/ui/text/output/stdio.rb b/lib/rex/ui/text/output/stdio.rb index d46fedeb56..4f60c11644 100644 --- a/lib/rex/ui/text/output/stdio.rb +++ b/lib/rex/ui/text/output/stdio.rb @@ -17,6 +17,8 @@ class Output::Stdio < Rex::Ui::Text::Output def print(msg = '') $stdout.print(msg) $stdout.flush + + msg end end diff --git a/lib/rex/ui/text/shell.rb b/lib/rex/ui/text/shell.rb index 7aaa1a1345..22e6e41b07 100644 --- a/lib/rex/ui/text/shell.rb +++ b/lib/rex/ui/text/shell.rb @@ -28,7 +28,7 @@ module Shell attr_accessor :prompt, :output def pgets - output.print(prompt) + _print_prompt(prompt) gets end end @@ -79,6 +79,20 @@ module Shell init_ui end + # + # Sets the log source that should be used for logging input and output. + # + def set_log_source(log_source) + self.log_source = log_source + end + + # + # Unsets the log source so that logging becomes disabled. + # + def unset_log_source + set_log_source(nil) + end + # # Performs tab completion on the supplied string # @@ -93,6 +107,8 @@ module Shell stop_flag = false while ((line = input.pgets)) + log_output(input.prompt) + # If a block was passed in, pass the line to it. If it returns true, # break out of the shell loop. if (block) @@ -177,6 +193,13 @@ module Shell def colorize(*color) # This check is busted atm... #return (supports_color? == false) ? '' : Rex::Ui::Text::Color.ansi(color) + return do_colorize(*color) + end + + # + # Colorize regardless of terminal support + # + def do_colorize(*color) return Rex::Ui::Text::Color.ansi(*color) end @@ -186,25 +209,25 @@ module Shell def print_error(msg) # Errors are not subject to disabled output - output.print_error(msg) + log_output(output.print_error(msg)) end def print_status(msg) return if (disable_output == true) - output.print_status(msg) + log_output(output.print_status(msg)) end def print_line(msg) return if (disable_output == true) - output.print_line(msg) + log_output(output.print_line(msg)) end def print(msg) return if (disable_output == true) - output.print(msg) + log_output(output.print(msg)) end attr_accessor :disable_output @@ -216,6 +239,8 @@ protected # Parse a line into an array of arguments # def parse_line(line) + log_input(line) + line.gsub!(/(\r|\n)/, '') begin @@ -227,10 +252,31 @@ protected return [] end + # + # Print the prompt, but do not log it. + # + def _print_prompt(prompt) + output.print(prompt) + end + + # + # Writes the supplied input to the log source if one has been registered. + # + def log_input(buf) + rlog(do_colorize("red") + buf + do_colorize("clear"), log_source) if (log_source) + end + + # + # Writes the supplied output to the log source if one has been registered. + # + def log_output(buf) + rlog(do_colorize("blue") + buf + do_colorize("clear"), log_source) if (log_source) + end attr_writer :input, :output attr_accessor :stop_flag, :init_prompt attr_accessor :prompt_char, :tab_complete_proc + attr_accessor :log_source end