Files
metasploit-gs/modules/post/windows/gather/enum_prefetch.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

229 lines
7.9 KiB
Ruby
Raw Normal View History

2013-07-14 15:30:54 +03:00
##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
2013-07-14 15:30:54 +03:00
##
2013-08-21 21:46:44 +03:00
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Post
2013-08-24 19:14:48 +03:00
include Msf::Post::File
2013-08-21 21:46:44 +03:00
include Msf::Post::Windows::Priv
2013-08-24 19:14:48 +03:00
include Msf::Post::Windows::Registry
2013-08-21 21:46:44 +03:00
2021-09-10 12:53:39 +01:00
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Windows Gather Prefetch File Information',
'Description' => %q{
This module gathers prefetch file information from WinXP, Win2k3 and Win7 systems
and current values of related registry keys. From each prefetch file we'll collect
filetime (converted to utc) of the last execution, file path hash, run count, filename
and the execution path.
},
'License' => MSF_LICENSE,
'Author' => ['TJ Glad <tjglad[at]cmail.nu>'],
'Platform' => ['win'],
2021-10-06 13:43:31 +01:00
'SessionType' => ['meterpreter'],
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
stdapi_fs_search
stdapi_sys_config_getenv
]
}
}
2021-09-10 12:53:39 +01:00
)
)
2013-08-21 21:46:44 +03:00
end
2023-02-08 13:47:34 +00:00
def print_prefetch_key_value
2013-08-21 21:46:44 +03:00
# Checks if Prefetch registry key exists and what value it has.
2023-02-08 13:47:34 +00:00
prefetch_key_value = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters', 'EnablePrefetcher')
2013-08-24 19:14:48 +03:00
if prefetch_key_value == 0
2023-02-08 13:47:34 +00:00
print_error('EnablePrefetcher Value: (0) = Disabled (Non-Default).')
2013-08-24 19:14:48 +03:00
elsif prefetch_key_value == 1
2023-02-08 13:47:34 +00:00
print_good('EnablePrefetcher Value: (1) = Application launch prefetching enabled (Non-Default).')
2013-08-24 19:14:48 +03:00
elsif prefetch_key_value == 2
2023-02-08 13:47:34 +00:00
print_good('EnablePrefetcher Value: (2) = Boot prefetching enabled (Non-Default, excl. Win2k3).')
2013-08-24 19:14:48 +03:00
elsif prefetch_key_value == 3
2023-02-08 13:47:34 +00:00
print_good('EnablePrefetcher Value: (3) = Applaunch and boot enabled (Default Value, excl. Win2k3).')
2013-08-21 21:46:44 +03:00
else
2023-02-08 13:47:34 +00:00
print_error('No value or unknown value. Results might vary.')
2013-08-21 21:46:44 +03:00
end
end
2013-08-28 09:25:49 -05:00
def print_timezone_key_values(key_value)
# Looks for timezone information from registry.
2023-02-08 13:47:34 +00:00
timezone = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', key_value)
tz_bias = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', 'Bias')
if timezone.nil? || tz_bias.nil?
2013-08-21 21:46:44 +03:00
print_line("Couldn't find key/value for timezone from registry.")
else
2023-02-08 13:47:34 +00:00
print_good('Remote: Timezone is %s.' % timezone)
2013-08-28 09:25:49 -05:00
if tz_bias < 0xfff
2023-02-08 13:47:34 +00:00
print_good('Remote: Localtime bias to UTC: -%s minutes.' % tz_bias)
2013-08-28 09:25:49 -05:00
else
offset = 0xffffffff
bias = offset - tz_bias
2023-02-08 13:47:34 +00:00
print_good('Remote: Localtime bias to UTC: +%s minutes.' % bias)
2013-08-21 21:46:44 +03:00
end
end
2013-08-28 09:25:49 -05:00
end
2013-08-21 21:46:44 +03:00
def gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)
# Collects the desired information from each prefetch file found
# from the system.
2013-08-24 19:14:48 +03:00
prefetch_file = read_file(filename)
2013-12-16 21:33:07 +02:00
if prefetch_file.blank?
2013-08-24 19:14:48 +03:00
print_error("Couldn't read file: #{filename}")
2013-08-28 09:29:49 -05:00
return nil
2013-08-21 21:46:44 +03:00
else
# First we extract the saved filename
2013-12-16 21:33:07 +02:00
pf_filename = prefetch_file[name_offset, 60]
2013-08-24 19:14:48 +03:00
idx = pf_filename.index("\x00\x00")
name = Rex::Text.to_ascii(pf_filename.slice(0..idx))
# Then we get the runcount
2013-12-16 21:33:07 +02:00
run_count = prefetch_file[runcount_offset, 4].unpack('v')[0]
# Then the filepath hash
2013-12-16 21:33:07 +02:00
path_hash = prefetch_file[hash_offset, 4].unpack('h*')[0].upcase.reverse
# Last we get the latest execution time
2013-12-16 21:33:07 +02:00
filetime_a = prefetch_file[filetime_offset, 16].unpack('q*')
filetime = filetime_a[0] + filetime_a[1]
last_exec = Time.at((filetime - 116444736000000000) / 10000000).utc.to_s
# This is for reading file paths of the executable from
# the prefetch file. We'll use this to find out from where the
# file was executed.
2013-12-08 18:17:22 +02:00
# First we'll use specific offsets for finding out the location
# and length of the filepath so that we can find it.
filepath = []
2013-12-16 21:33:07 +02:00
fpath_offset = prefetch_file[0x64, 2].unpack('v').first
fpath_length = prefetch_file[0x68, 2].unpack('v').first
filepath_data = prefetch_file[fpath_offset, fpath_length]
# This part will extract the filepath so that we can find and
# compare its contents to the filename we found previously. This
2013-12-08 18:17:22 +02:00
# allows us to find the filepath (if it can be found inside the
# prefetch file) used to execute the program
# referenced in the prefetch-file.
2013-12-16 21:33:07 +02:00
unless filepath_data.blank?
fpath_data_array = filepath_data.split("\\\x00D\x00E\x00V\x00I\x00C\x00E")
fpath_data_array.each do |path|
2023-02-08 13:47:34 +00:00
next if path.blank?
fpath_name = path.split('\\').last.gsub(/\0/, '')
if fpath_name == name
filepath << path
end
end
2013-08-21 21:46:44 +03:00
end
end
2013-12-16 21:33:07 +02:00
if filepath.blank?
2023-02-08 13:47:34 +00:00
filepath << '*** Filepath not found ***'
2013-12-16 21:33:07 +02:00
end
return [last_exec, path_hash, run_count, name, filepath[0]]
2013-08-21 21:46:44 +03:00
end
def run
2023-02-08 13:47:34 +00:00
print_status('Prefetch Gathering started.')
2013-08-21 21:46:44 +03:00
# Check to see what Windows Version is running.
# Needed for offsets.
# Tested on WinXP, Win2k3 and Win7 systems.
# http://www.forensicswiki.org/wiki/Prefetch
# http://www.forensicswiki.org/wiki/Windows_Prefetch_File_Format
2013-07-14 17:24:27 +03:00
2013-08-16 02:46:16 +03:00
error_msg = "You don't have enough privileges. Try getsystem."
2013-07-14 17:24:27 +03:00
version = get_version_info
if version.xp_or_2003?
2023-02-08 13:47:34 +00:00
if !is_admin?
2013-08-21 21:46:44 +03:00
print_error(error_msg)
return nil
end
2013-08-21 21:46:44 +03:00
# Offsets for WinXP & Win2k3
print_good("Detected #{version.product_name} (max 128 entries)")
2013-08-21 21:46:44 +03:00
name_offset = 0x10
hash_offset = 0x4C
runcount_offset = 0x90
filetime_offset = 0x78
2013-08-21 21:46:44 +03:00
# Registry key for timezone
2023-02-08 13:47:34 +00:00
key_value = 'StandardName'
2013-08-21 21:46:44 +03:00
elsif version.win7_or_2008r2? && !version.windows_server?
2023-02-08 13:47:34 +00:00
if !is_admin?
2013-08-21 21:46:44 +03:00
print_error(error_msg)
return nil
end
2013-08-21 21:46:44 +03:00
# Offsets for Win7
print_good("Detected #{version.product_name} (max 128 entries)")
2013-08-21 21:46:44 +03:00
name_offset = 0x10
hash_offset = 0x4C
runcount_offset = 0x98
filetime_offset = 0x78
2013-08-21 21:46:44 +03:00
# Registry key for timezone
2023-02-08 13:47:34 +00:00
key_value = 'TimeZoneKeyName'
2013-08-21 21:46:44 +03:00
else
2023-02-08 13:47:34 +00:00
print_error('No offsets for the target Windows version. Currently works only on WinXP, Win2k3 and Win7.')
2013-08-21 21:46:44 +03:00
return nil
end
2016-08-10 13:30:09 -05:00
table = Rex::Text::Table.new(
2023-02-08 13:47:34 +00:00
'Header' => 'Prefetch Information',
2021-09-10 12:53:39 +01:00
'Indent' => 1,
2013-08-21 21:46:44 +03:00
'Columns' =>
[
2023-02-08 13:47:34 +00:00
'Last execution (filetime)',
'Run Count',
'Hash',
'Filename',
'Filepath'
2021-09-10 12:53:39 +01:00
]
)
print_prefetch_key_value
2013-08-28 09:25:49 -05:00
print_timezone_key_values(key_value)
2023-02-08 13:47:34 +00:00
print_good('Current UTC Time: %s' % Time.now.utc)
sys_root = session.sys.config.getenv('SYSTEMROOT')
2023-02-08 13:47:34 +00:00
full_path = sys_root + '\\Prefetch\\'
file_type = '*.pf'
print_status('Gathering information from remote system. This will take awhile..')
2013-08-21 21:46:44 +03:00
# Goes through the files in Prefetch directory, creates file paths for the
2013-08-24 19:14:48 +03:00
# gather_pf_info function that enumerates all the pf info
2013-08-21 21:46:44 +03:00
2013-08-28 09:27:18 -05:00
getfile_prefetch_filenames = client.fs.file.search(full_path, file_type)
2023-02-08 13:47:34 +00:00
if getfile_prefetch_filenames.empty? || getfile_prefetch_filenames.nil?
2013-08-24 19:14:48 +03:00
print_error("Could not find/access any .pf files. Can't continue. (Might be temporary error..)")
2013-08-21 21:46:44 +03:00
return nil
else
getfile_prefetch_filenames.each do |file|
2023-02-08 13:47:34 +00:00
if file.empty? || file.nil?
2013-08-28 09:27:18 -05:00
next
2013-08-21 21:46:44 +03:00
else
2013-08-28 09:55:21 -05:00
filename = ::File.join(file['path'], file['name'])
pf_entry = gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)
2023-02-08 13:47:34 +00:00
if !pf_entry.nil?
2013-08-28 09:29:49 -05:00
table << pf_entry
end
2013-08-21 21:46:44 +03:00
end
end
end
# Stores and prints out results
results = table.to_s
2023-02-08 13:47:34 +00:00
loot = store_loot('prefetch_info', 'text/plain', session, results, nil, 'Prefetch Information')
2013-08-21 21:46:44 +03:00
print_line("\n" + results + "\n")
2023-02-08 13:47:34 +00:00
print_status('Finished gathering information from prefetch files.')
2013-08-21 21:46:44 +03:00
print_status("Results stored in: #{loot}")
end
2013-07-14 15:30:54 +03:00
end