metasploit-framework/modules/post/windows/gather/enum_prefetch.rb

187 lines
6.8 KiB
Ruby

##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
require 'msf/core/post/windows/registry'
class Metasploit3 < Msf::Post
include Msf::Post::File
include Msf::Post::Windows::Priv
include Msf::Post::Windows::Registry
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.
Run count, hash and filename information is collected from each prefetch file while
Last Modified and Create times are file MACE values.
},
'License' => MSF_LICENSE,
'Author' => ['TJ Glad <fraktaali[at]gmail.com>'],
'Platform' => ['win'],
'SessionType' => ['meterpreter']
))
end
def print_prefetch_key_value()
# Checks if Prefetch registry key exists and what value it has.
prefetch_key_value = registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters", "EnablePrefetcher")
if prefetch_key_value == 0
print_error("EnablePrefetcher Value: (0) = Disabled (Non-Default).")
elsif prefetch_key_value == 1
print_good("EnablePrefetcher Value: (1) = Application launch prefetching enabled (Non-Default).")
elsif prefetch_key_value == 2
print_good("EnablePrefetcher Value: (2) = Boot prefetching enabled (Non-Default, excl. Win2k3).")
elsif prefetch_key_value == 3
print_good("EnablePrefetcher Value: (3) = Applaunch and boot enabled (Default Value, excl. Win2k3).")
else
print_error("No value or unknown value. Results might vary.")
end
end
def print_timezone_key_values(key_value)
# Looks for timezone from registry
timezone = registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", key_value)
tz_bias = registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", "Bias")
if timezone.nil? or tz_bias.nil?
print_line("Couldn't find key/value for timezone from registry.")
else
print_good("Remote: Timezone is %s." % timezone)
if tz_bias < 0xfff
print_good("Remote: Localtime bias to UTC: -%s minutes." % tz_bias)
else
offset = 0xffffffff
bias = offset - tz_bias
print_good("Remote: Localtime bias to UTC: +%s minutes." % bias)
end
end
end
def gather_pf_info(name_offset, hash_offset, runcount_offset, filename)
# We'll load the file and parse information from the offsets
prefetch_file = read_file(filename)
if prefetch_file.empty? or prefetch_file.nil?
print_error("Couldn't read file: #{filename}")
return nil
else
# First we'll get the filename
pf_filename = prefetch_file[name_offset..name_offset+60]
idx = pf_filename.index("\x00\x00")
name = Rex::Text.to_ascii(pf_filename.slice(0..idx))
# Next we'll get the run count
run_count = prefetch_file[runcount_offset..runcount_offset+4].unpack('L*')[0].to_s
# Then file path hash
path_hash = prefetch_file[hash_offset..hash_offset+4].unpack('h8')[0].reverse.upcase.to_s
# Last is mace value for timestamps
mtimes = client.priv.fs.get_file_mace(filename)
if mtimes.nil? or mtimes.empty?
last_modified = "Error reading value"
created = "Error reading value"
else
last_modified = mtimes['Modified'].utc.to_s
created = mtimes['Created'].utc.to_s
end
return [last_modified, created, run_count, path_hash, name]
end
end
def run
print_status("Prefetch Gathering started.")
# 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
sysnfo = client.sys.config.sysinfo['OS']
error_msg = "You don't have enough privileges. Try getsystem."
if sysnfo =~/(Windows XP|2003|.NET)/
# For some reason we need system privileges to read file
# mace time on XP/2003 while we can do the same only
# as admin on Win7.
if not is_system?
print_error(error_msg)
return nil
end
# Offsets for WinXP & Win2k3
print_good("Detected #{sysnfo} (max 128 entries)")
name_offset = 0x10
hash_offset = 0x4C
runcount_offset = 0x90
# Registry key for timezone
key_value = "StandardName"
elsif sysnfo =~/(Windows 7)/
if not is_admin?
print_error(error_msg)
return nil
end
# Offsets for Win7
print_good("Detected #{sysnfo} (max 128 entries)")
name_offset = 0x10
hash_offset = 0x4C
runcount_offset = 0x98
# Registry key for timezone
key_value = "TimeZoneKeyName"
else
print_error("No offsets for the target Windows version. Currently works only on WinXP, Win2k3 and Win7.")
return nil
end
table = Rex::Ui::Text::Table.new(
'Header' => "Prefetch Information",
'Indent' => 1,
'Columns' =>
[
"Modified (mace)",
"Created (mace)",
"Run Count",
"Hash",
"Filename"
])
print_prefetch_key_value
print_timezone_key_values(key_value)
print_good("Current UTC Time: %s" % Time.now.utc)
sys_root = expand_path("%SYSTEMROOT%")
full_path = sys_root + "\\Prefetch\\"
file_type = "*.pf"
print_status("Gathering information from remote system. This will take awhile..")
# Goes through the files in Prefetch directory, creates file paths for the
# gather_pf_info function that enumerates all the pf info
getfile_prefetch_filenames = client.fs.file.search(full_path, file_type)
if getfile_prefetch_filenames.empty? or getfile_prefetch_filenames.nil?
print_error("Could not find/access any .pf files. Can't continue. (Might be temporary error..)")
return nil
else
getfile_prefetch_filenames.each do |file|
if file.empty? or file.nil?
next
else
filename = ::File.join(file['path'], file['name'])
pf_entry = gather_pf_info(name_offset, hash_offset, runcount_offset, filename)
if not pf_entry.nil?
table << pf_entry
end
end
end
end
# Stores and prints out results
results = table.to_s
loot = store_loot("prefetch_info", "text/plain", session, results, nil, "Prefetch Information")
print_line("\n" + results + "\n")
print_status("Finished gathering information from prefetch files.")
print_status("Results stored in: #{loot}")
end
end