Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add setg sessiontlvlogging command to log TLV packets #16135

Merged
merged 2 commits into from
Mar 4, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 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,15 @@ def cmd_set(*args)
print_warning("Changing the SSL option's value may require changing RPORT!")
end

# Correctly set the file output if user provides a directory for SessionTlvLogging
if name.casecmp?('SessionTlvLogging')
pathname = ::Pathname.new(datastore[name].split('file:').last)
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved

if ::File.directory?(pathname) && datastore[name].start_with?('file:')
datastore[name] = ::File.join(datastore[name], 'sessiontlvlogging.txt')
end
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
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved

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
3 changes: 2 additions & 1 deletion lib/rex/post/meterpreter/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ def init_meterpreter(sock,opts={})
self.url = opts[:url]
self.ssl = opts[:ssl]

self.tlv_logging_enabled = true
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved

self.pivot_session = opts[:pivot_session]
if self.pivot_session
self.expiration = self.pivot_session.expiration
Expand Down Expand Up @@ -502,4 +504,3 @@ def unicode_filter_decode(str)
end

end; end; end

53 changes: 49 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 @@ -88,6 +89,9 @@ def shutdown_passive_dispatcher
self.send_queue = []
self.recv_queue = []
self.waiters = []

self.tlv_log_file.close unless self.tlv_log_file.nil?
self.tlv_log_file = nil
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved
end

def on_passive_request(cli, req)
Expand Down Expand Up @@ -130,8 +134,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) if self.tlv_logging_enabled

bytes = 0
raw = packet.to_r(session_guid, tlv_enc_key)
Expand Down Expand Up @@ -579,8 +582,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) if self.tlv_logging_enabled

# Update our last reply time
self.last_checkin = ::Time.now
Expand Down Expand Up @@ -639,6 +641,49 @@ def deregister_inbound_handler(handler)
attr_accessor :receiver_thread # :nodoc:
attr_accessor :dispatcher_thread # :nodoc:
attr_accessor :waiters # :nodoc:
attr_accessor :tlv_log_file # :nodoc:
attr_accessor :tlv_logging_enabled # :nodoc:

def log_packet(packet, packet_type)
option = framework.datastore['SessionTlvLogging']
return if option.nil? || option.casecmp?('false')

if option.casecmp?('console') || option.casecmp?('true')
log_packet_to_console(packet, packet_type)
elsif option.start_with?('file:')
log_packet_to_file(packet, packet_type)
end
end

def log_packet_to_console(packet, packet_type)
if packet_type == :recv
print "\n%bluRECV%clr: #{packet.inspect}\n"
elsif packet_type == :send
print "\n%redSEND%clr: #{packet.inspect}\n"
end
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved
end

def log_packet_to_file(packet, packet_type)
path = framework.datastore['SessionTlvLogging'].split('file:').last
pathname = ::Pathname.new(path)
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved

begin
self.tlv_log_file ||= ::File.open(pathname, 'a+')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a blocker: If the user changed log file location, this would still log to the old location. I believe that's what handle_session_tlv_logging is meant to handle potentially


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_enabled = false
print_error "Failed writing to TLV Log File: #{pathname} with error: #{e.message}. Turning off logging for this session: #{self.inspect}..."
sjanusz-r7 marked this conversation as resolved.
Show resolved Hide resolved
elog(e)
self.tlv_log_file.close unless self.tlv_log_file.nil?
self.tlv_log_file = nil
return
end
end
end

module HttpPacketDispatcher
Expand Down