Skip to content

Commit

Permalink
Land #16135, Add setg sessiontlvlogging command to log TLV packets
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed Mar 4, 2022
2 parents 02143c5 + a2fadf0 commit 1253878
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 6 deletions.
5 changes: 4 additions & 1 deletion lib/msf/base/sessions/meterpreter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ def initialize(rstream, opts={})
opts[:ssl_cert] = opts[:datastore]['HandlerSSLCert']
end

if opts[:datastore] and opts[:datastore]['SessionTlvLogging']
opts[:tlv_log] = opts[:datastore]['SessionTlvLogging']
end

# Don't pass the datastore into the init_meterpreter method
opts.delete(:datastore)

Expand Down Expand Up @@ -736,4 +740,3 @@ def find_internet_connected_address

end
end

12 changes: 12 additions & 0 deletions lib/msf/ui/console/command_dispatcher/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1914,6 +1914,18 @@ def cmd_set(*args)
print_warning("Changing the SSL option's value may require changing RPORT!")
end

if name.casecmp?('SessionTlvLogging')
# Check if we need to append the default filename if user provided an output directory
if datastore[name].start_with?('file:')
pathname = ::Pathname.new(datastore[name].split('file:').last)
if ::File.directory?(pathname)
datastore[name] = ::File.join(datastore[name], 'sessiontlvlogging.txt')
end
end

framework.sessions.each { |_index, session| session.initialize_tlv_logging(datastore[name]) }
end

print_line("#{name} => #{datastore[name]}")
end

Expand Down
1 change: 1 addition & 0 deletions lib/msf/ui/console/command_dispatcher/modules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,7 @@ def show_global_options
[ 'LogLevel', framework.datastore['LogLevel'] || "0", 'Verbosity of logs (default 0, max 3)' ],
[ 'MinimumRank', framework.datastore['MinimumRank'] || "0", 'The minimum rank of exploits that will run without explicit confirmation' ],
[ 'SessionLogging', framework.datastore['SessionLogging'] || "false", 'Log all input and output for sessions' ],
[ 'SessionTlvLogging', framework.datastore['SessionTlvLogging'] || "false", 'Log all incoming and outgoing TLV packets' ],
[ 'TimestampOutput', framework.datastore['TimestampOutput'] || "false", 'Prefix all console output with a timestamp' ],
[ 'Prompt', framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt.to_s.gsub(/%.../,"") , "The prompt string" ],
[ 'PromptChar', framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar.to_s.gsub(/%.../,""), "The prompt character" ],
Expand Down
51 changes: 51 additions & 0 deletions lib/msf/ui/console/driver.rb
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ def on_variable_set(glob, var, val)
case var.downcase
when 'sessionlogging'
handle_session_logging(val) if glob
when 'sessiontlvlogging'
handle_session_tlv_logging(val) if glob
when 'consolelogging'
handle_console_logging(val) if glob
when 'loglevel'
Expand All @@ -412,6 +414,8 @@ def on_variable_unset(glob, var)
case var.downcase
when 'sessionlogging'
handle_session_logging('0') if glob
when 'sessiontlvlogging'
handle_session_tlv_logging('false') if glob
when 'consolelogging'
handle_console_logging('0') if glob
when 'loglevel'
Expand Down Expand Up @@ -601,6 +605,53 @@ def handle_ssh_ident(val)
$VERBOSE = verbose
end

def handle_session_tlv_logging(val)
if val
if val.casecmp?('console') || val.casecmp?('true') || val.casecmp?('false')
return true
elsif val.start_with?('file:') && !val.split('file:').empty?
pathname = ::Pathname.new(val.split('file:').last)

# Check if we want to write the log to file
if ::File.file?(pathname)
if ::File.writable?(pathname)
return true
else
print_status "No write permissions for log output file: #{pathname}"
return false
end
# Check if we want to write the log file to a directory
elsif ::File.directory?(pathname)
if ::File.writable?(pathname)
return true
else
print_status "No write permissions for log output directory: #{pathname}"
return false
end
# Check if the subdirectory exists
elsif ::File.directory?(pathname.dirname)
if ::File.writable?(pathname.dirname)
return true
else
print_status "No write permissions for log output directory: #{pathname.dirname}"
return false
end
else
# Else the directory doesn't exist. Check if we can create it.
begin
::FileUtils.mkdir_p(pathname.dirname)
return true
rescue ::StandardError => e
print_status "Error when trying to create directory #{pathname.dirname}: #{e.message}"
return false
end
end
end
end

false
end

# Require the appropriate readline library based on the user's preference.
#
# @return [void]
Expand Down
5 changes: 5 additions & 0 deletions lib/msf/ui/console/module_option_tab_completion.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ def tab_complete_option_names(mod, str, words)
PromptChar
PromptTimeFormat
MeterpreterPrompt
SessionTlvLogging
]
if !mod
return res
Expand Down Expand Up @@ -122,6 +123,10 @@ def tab_complete_option_names(mod, str, words)
# Provide tab completion for option values
#
def tab_complete_option_values(mod, str, words, opt:)
if words.last.casecmp?('SessionTlvLogging')
return %w[console true false file:<file>]
end

res = []
# With no module, we have nothing to complete
if !mod
Expand Down
5 changes: 4 additions & 1 deletion lib/rex/post/meterpreter/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ def cleanup_meterpreter
end

shutdown_passive_dispatcher

shutdown_tlv_logging
end

#
Expand Down Expand Up @@ -165,6 +167,8 @@ def init_meterpreter(sock,opts={})
end
end

initialize_tlv_logging(opts[:tlv_log]) unless opts[:tlv_log].nil?

# Protocol specific dispatch mixins go here, this may be neader with explicit Client classes
opts[:dispatch_ext].each {|dx| self.extend(dx)} if opts[:dispatch_ext]
initialize_passive_dispatcher if opts[:passive_dispatcher]
Expand Down Expand Up @@ -502,4 +506,3 @@ def unicode_filter_decode(str)
end

end; end; end

76 changes: 72 additions & 4 deletions lib/rex/post/meterpreter/packet_dispatcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require 'rex/post/meterpreter/command_mapper'
require 'rex/post/meterpreter/packet_response_waiter'
require 'rex/exceptions'
require 'pathname'

module Rex
module Post
Expand Down Expand Up @@ -130,8 +131,7 @@ def send_packet(packet, opts={})
tlv_enc_key = opts[:tlv_enc_key]
end

# Uncomment this line if you want to see outbound packets in the console.
# STDERR.puts("\n\e[1;31mSEND\e[0m: #{packet.inspect}\n")
log_packet(packet, :send)

bytes = 0
raw = packet.to_r(session_guid, tlv_enc_key)
Expand Down Expand Up @@ -579,8 +579,7 @@ def decrypt_inbound_packet(packet)
def dispatch_inbound_packet(packet)
handled = false

# Uncomment this line if you want to see inbound packets in the console
# STDERR.puts("\n\e[1;32mRECV\e[0m: #{packet.inspect}\n")
log_packet(packet, :recv)

# Update our last reply time
self.last_checkin = ::Time.now
Expand Down Expand Up @@ -634,11 +633,80 @@ def deregister_inbound_handler(handler)
@inbound_handlers.delete(handler)
end

def initialize_tlv_logging(opt)
self.tlv_logging_error_occured = false
self.tlv_log_file = nil
self.tlv_log_file_path = nil
self.tlv_log_output = :none

if opt.casecmp?('console') || opt.casecmp?('true')
self.tlv_log_output = :console
elsif opt.start_with?('file:')
self.tlv_log_output = :file
self.tlv_log_file_path = opt.split('file:').last
end
end

protected

attr_accessor :receiver_thread # :nodoc:
attr_accessor :dispatcher_thread # :nodoc:
attr_accessor :waiters # :nodoc:

attr_accessor :tlv_log_output # :nodoc:
attr_accessor :tlv_log_file # :nodoc:
attr_accessor :tlv_log_file_path # :nodoc:
attr_accessor :tlv_logging_error_occured # :nodoc:

def shutdown_tlv_logging
self.tlv_log_output = :none
self.tlv_log_file.close unless self.tlv_log_file.nil?
self.tlv_log_file = nil
self.tlv_log_file_path = nil
end

def log_packet(packet, packet_type)
# if we previously failed to log, return
return if self.tlv_logging_error_occured || self.tlv_log_output == :none

if self.tlv_log_output == :console
log_packet_to_console(packet, packet_type)
elsif self.tlv_log_output == :file
log_packet_to_file(packet, packet_type)
end
end

def log_packet_to_console(packet, packet_type)
if packet_type == :send
print "\n%redSEND%clr: #{packet.inspect}\n"
elsif packet_type == :recv
print "\n%bluRECV%clr: #{packet.inspect}\n"
end
end

def log_packet_to_file(packet, packet_type)
pathname = ::Pathname.new(self.tlv_log_file_path.split('file:').last)

begin
if self.tlv_log_file.nil? || self.tlv_log_file.path != pathname.to_s
self.tlv_log_file.close unless self.tlv_log_file.nil?

self.tlv_log_file = ::File.open(pathname, 'a+')
end

if packet_type == :recv
self.tlv_log_file.puts("\nRECV: #{packet.inspect}\n")
elsif packet_type == :send
self.tlv_log_file.puts("\nSEND: #{packet.inspect}\n")
end
rescue ::StandardError => e
self.tlv_logging_error_occured = true
print_error "Failed writing to TLV Log File: #{pathname} with error: #{e.message}. Turning off logging for this session: #{self.inspect}..."
elog(e)
shutdown_tlv_logging
return
end
end
end

module HttpPacketDispatcher
Expand Down

0 comments on commit 1253878

Please sign in to comment.