lib/msf/core/db_manager/session_event.rb
module Msf::DBManager::SessionEvent
DEFAULT_ORDER = :desc
DEFAULT_LIMIT = 100
DEFAULT_OFFSET = 0
# Retrieves session events that are stored in the database.
#
# @param opts [Hash] Hash containing query key-value pairs based on the session event model.
# @option opts :id [Integer] A specific session event ID. If specified, all other options are ignored.
#
# Additional query options:
# @option opts :order [Symbol|String] The session event created_at sort order.
# Valid values: :asc, :desc, 'asc' or 'desc'. Default: :desc
# @option opts :limit [Integer] The maximum number of session events that will be retrieved from the query.
# Default: 100
# @option opts :offset [Integer] The number of session events the query will begin reading from the start
# of the set. Default: 0
# @option opts :search_term [String] Search regular expression used to filter results.
# All fields are converted to strings and results are returned if the pattern is matched.
# @return [Array<Mdm::SessionEvent>|Mdm::SessionEvent::ActiveRecord_Relation] session events that are matched.
def session_events(opts)
::ApplicationRecord.connection_pool.with_connection {
# If we have the ID, there is no point in creating a complex query.
if opts[:id] && !opts[:id].to_s.empty?
return Array.wrap(Mdm::SessionEvent.find(opts[:id]))
end
opts = opts.clone() # protect the original caller's opts
# Passing workspace keys to the search will cause exceptions, so remove them if they were accidentally included.
opts.delete(:workspace)
order = opts.delete(:order)
order = order.nil? ? DEFAULT_ORDER : order.to_sym
limit = opts.delete(:limit) || DEFAULT_LIMIT
offset = opts.delete(:offset) || DEFAULT_OFFSET
search_term = opts.delete(:search_term)
results = Mdm::SessionEvent.where(opts).order(created_at: order).offset(offset).limit(limit)
if search_term && !search_term.empty?
re_search_term = /#{search_term}/mi
results = results.select { |event|
event.attribute_names.any? { |a| event[a.intern].to_s.match(re_search_term) }
}
end
results
}
end
#
# Record a session event in the database
#
# opts MUST contain one of:
# +:session+:: the Msf::Session, Mdm::Session or Hash representation of Mdm::Session we are reporting
# +:etype+:: event type, enum: command, output, upload, download, filedelete
#
# opts may contain
# +:output+:: the data for an output event
# +:command+:: the data for an command event
# +:remote_path+:: path to the associated file for upload, download, and filedelete events
# +:local_path+:: path to the associated file for upload, and download
#
def report_session_event(opts)
return if not active
raise ArgumentError.new("Missing required option :session") if opts[:session].nil?
raise ArgumentError.new("Expected an :etype") unless opts[:etype]
session = nil
::ApplicationRecord.connection_pool.with_connection {
if opts[:session].respond_to? :db_record
session = opts[:session].db_record
if session.nil?
# The session doesn't have a db_record which means
# a) the database wasn't connected at session registration time
# or
# b) something awful happened and the report_session call failed
#
# Either way, we can't do anything with this session as is, so
# log a warning and punt.
wlog("Warning: trying to report a session_event for a session with no db_record (#{opts[:session].sid})")
return
end
event_data = { :created_at => Time.now }
else
session = opts[:session]
event_data = { :created_at => opts[:created_at] }
end
session_id = nil
if session.is_a?(Mdm::Session)
session_id = session.id
elsif session.is_a?(Hash) && session.key?(:id)
session_id = session[:id]
end
event_data[:session_id] = session_id
[:remote_path, :local_path, :output, :command, :etype].each do |attr|
event_data[attr] = opts[attr] if opts[attr]
end
s = ::Mdm::SessionEvent.create(event_data)
}
end
end