diff --git a/lib/msf/base/sessions/meterpreter_options.rb b/lib/msf/base/sessions/meterpreter_options.rb index 2a7f149d64..b4b8dcedf5 100644 --- a/lib/msf/base/sessions/meterpreter_options.rb +++ b/lib/msf/base/sessions/meterpreter_options.rb @@ -75,6 +75,10 @@ module Msf 'MeterpreterDebugBuild', [false, 'Use a debug version of Meterpreter'] ), + OptMeterpreterDebugLogging.new( + 'MeterpreterDebugLogging', + [false, 'The Meterpreter debug logging configuration, see https://github.com/rapid7/metasploit-framework/wiki/Meterpreter-Debugging-Meterpreter-Sessions'] + ) ], self.class ) diff --git a/lib/msf/core/opt_meterpreter_debug_logging.rb b/lib/msf/core/opt_meterpreter_debug_logging.rb new file mode 100644 index 0000000000..e94c31d734 --- /dev/null +++ b/lib/msf/core/opt_meterpreter_debug_logging.rb @@ -0,0 +1,69 @@ +# -*- coding: binary -*- + +module Msf + ### + # + # Meterpreter Debug Logging option + # + ### + class OptMeterpreterDebugLogging < OptBase + def initialize(in_name, attrs = [], **kwargs) + super + end + + def type + 'meterpreterdebuglogging' + end + + def validate_on_assignment? + true + end + + def valid?(value, check_empty: true) + return false if !super(value, check_empty: check_empty) + + begin + _parse_result = self.class.parse_logging_options(value) + true + rescue ::ArgumentError + false + end + end + + ## + # + # Parses the given Meterpreter Debug Logging string + # + ## + def self.parse_logging_options(value) + result = {} + errors = [] + + return result if value.nil? + + value = value.strip + # Match 'rpath:./file', 'rpath:/file', and drive letters e.g. 'rpath:C:/file' + rpath_regex = %r{^rpath:((\.?/\p{ASCII}+)|(\p{ASCII}:/\p{ASCII}+))}i + + # Check if we log to rpath + if value.match?(rpath_regex) + # https://docs.microsoft.com/en-us/windows/win32/fileio/maximum-file-path-limitation?tabs=cmd + max_length = 260 + rpath_value = value.split('rpath:').last + if rpath_value.length <= max_length + result[:rpath] = rpath_value + else + errors << "Rpath is too long. Max length: #{max_length}" + end + else + errors << 'Value is not a valid rpath string' + end + + if errors.any? + raise ::ArgumentError, "Failed to validate MeterpreterDebugLogging option: #{value} with error/errors: #{errors.join('. ')}" + end + + result + end + end +end diff --git a/lib/msf/core/payload/python/meterpreter_loader.rb b/lib/msf/core/payload/python/meterpreter_loader.rb index dbc5f079df..d301b0b1bb 100644 --- a/lib/msf/core/payload/python/meterpreter_loader.rb +++ b/lib/msf/core/payload/python/meterpreter_loader.rb @@ -34,9 +34,10 @@ module Payload::Python::MeterpreterLoader default: true ), OptBool.new( - 'PythonMeterpreterDebug', - 'Enable debugging for the Python meterpreter' - ), + 'MeterpreterDebugBuild', + 'Enable debugging for the Python meterpreter', + aliases: ['PythonMeterpreterDebug'] + ) ] + Msf::Opt::http_header_options ) @@ -70,12 +71,16 @@ module Payload::Python::MeterpreterLoader txt.gsub('\\', '\\' * 8).gsub('\'', %q(\\\\\\\')) } + if ds['MeterpreterDebugBuild'] + met.sub!(%q|DEBUGGING = False|, %q|DEBUGGING = True|) + + logging_options = Msf::OptMeterpreterDebugLogging.parse_logging_options(ds['MeterpreterDebugLogging']) + met.sub!(%q|DEBUGGING_LOG_FILE_PATH = None|, %Q|DEBUGGING_LOG_FILE_PATH = "#{logging_options[:rpath]}"|) if logging_options[:rpath] + end + unless ds['MeterpreterTryToFork'] met.sub!('TRY_TO_FORK = True', 'TRY_TO_FORK = False') end - if ds['PythonMeterpreterDebug'] - met.sub!('DEBUGGING = False', 'DEBUGGING = True') - end met.sub!("# PATCH-SETUP-ENCRYPTION #", python_encryptor_loader) @@ -357,7 +362,6 @@ class AESCBC(object): return _b2s(pt) ? end - end end diff --git a/spec/lib/msf/core/opt_meterpreter_debug_logging_spec.rb b/spec/lib/msf/core/opt_meterpreter_debug_logging_spec.rb new file mode 100644 index 0000000000..3d05320991 --- /dev/null +++ b/spec/lib/msf/core/opt_meterpreter_debug_logging_spec.rb @@ -0,0 +1,20 @@ +# -*- coding:binary -*- + +require 'spec_helper' + +RSpec.describe Msf::OptMeterpreterDebugLogging do + valid_values = [ + { value: 'rpath:C:/log.txt', normalized: 'rpath:C:/log.txt' }, + { value: 'rpath:/tmp/log.txt', normalized: 'rpath:/tmp/log.txt' }, + { value: 'rpath:./log.log', normalized: 'rpath:./log.log' }, + { value: ' rpath:./log.log ', normalized: ' rpath:./log.log ' } + ] + invalid_values = [ + { value: 'rpath', normalized: 'rpath' }, + { value: 'C:', normalized: 'C:' }, + { value: 'C', normalized: 'C' }, + { value: 'rpath:C', normalized: 'rpath:C' } + ] + + it_behaves_like 'an option', valid_values, invalid_values, 'meterpreterdebuglogging' +end