Skip to content

Commit 1042dbe

Browse files
author
jvazquez-r7
committed
Land rapid7#2108, @jiuweigui's post module to get info from prefetch files
2 parents 26531db + 0fbe411 commit 1042dbe

File tree

1 file changed

+186
-0
lines changed

1 file changed

+186
-0
lines changed
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# web site for more information on licensing and terms of use.
5+
# http://metasploit.com/
6+
##
7+
8+
require 'msf/core'
9+
require 'rex'
10+
require 'msf/core/post/windows/registry'
11+
class Metasploit3 < Msf::Post
12+
13+
include Msf::Post::File
14+
include Msf::Post::Windows::Priv
15+
include Msf::Post::Windows::Registry
16+
17+
def initialize(info={})
18+
super(update_info(info,
19+
'Name' => 'Windows Gather Prefetch File Information',
20+
'Description' => %q{
21+
This module gathers prefetch file information from WinXP, Win2k3 and Win7 systems.
22+
Run count, hash and filename information is collected from each prefetch file while
23+
Last Modified and Create times are file MACE values.
24+
},
25+
'License' => MSF_LICENSE,
26+
'Author' => ['TJ Glad <fraktaali[at]gmail.com>'],
27+
'Platform' => ['win'],
28+
'SessionType' => ['meterpreter']
29+
))
30+
end
31+
32+
def print_prefetch_key_value()
33+
# Checks if Prefetch registry key exists and what value it has.
34+
prefetch_key_value = registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters", "EnablePrefetcher")
35+
if prefetch_key_value == 0
36+
print_error("EnablePrefetcher Value: (0) = Disabled (Non-Default).")
37+
elsif prefetch_key_value == 1
38+
print_good("EnablePrefetcher Value: (1) = Application launch prefetching enabled (Non-Default).")
39+
elsif prefetch_key_value == 2
40+
print_good("EnablePrefetcher Value: (2) = Boot prefetching enabled (Non-Default, excl. Win2k3).")
41+
elsif prefetch_key_value == 3
42+
print_good("EnablePrefetcher Value: (3) = Applaunch and boot enabled (Default Value, excl. Win2k3).")
43+
else
44+
print_error("No value or unknown value. Results might vary.")
45+
end
46+
end
47+
48+
def print_timezone_key_values(key_value)
49+
# Looks for timezone from registry
50+
timezone = registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", key_value)
51+
tz_bias = registry_getvaldata("HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation", "Bias")
52+
if timezone.nil? or tz_bias.nil?
53+
print_line("Couldn't find key/value for timezone from registry.")
54+
else
55+
print_good("Remote: Timezone is %s." % timezone)
56+
if tz_bias < 0xfff
57+
print_good("Remote: Localtime bias to UTC: -%s minutes." % tz_bias)
58+
else
59+
offset = 0xffffffff
60+
bias = offset - tz_bias
61+
print_good("Remote: Localtime bias to UTC: +%s minutes." % bias)
62+
end
63+
end
64+
end
65+
66+
def gather_pf_info(name_offset, hash_offset, runcount_offset, filename)
67+
# We'll load the file and parse information from the offsets
68+
prefetch_file = read_file(filename)
69+
if prefetch_file.empty? or prefetch_file.nil?
70+
print_error("Couldn't read file: #{filename}")
71+
return nil
72+
else
73+
# First we'll get the filename
74+
pf_filename = prefetch_file[name_offset..name_offset+60]
75+
idx = pf_filename.index("\x00\x00")
76+
name = Rex::Text.to_ascii(pf_filename.slice(0..idx))
77+
# Next we'll get the run count
78+
run_count = prefetch_file[runcount_offset..runcount_offset+4].unpack('L*')[0].to_s
79+
# Then file path hash
80+
path_hash = prefetch_file[hash_offset..hash_offset+4].unpack('h8')[0].reverse.upcase.to_s
81+
# Last is mace value for timestamps
82+
mtimes = client.priv.fs.get_file_mace(filename)
83+
if mtimes.nil? or mtimes.empty?
84+
last_modified = "Error reading value"
85+
created = "Error reading value"
86+
else
87+
last_modified = mtimes['Modified'].utc.to_s
88+
created = mtimes['Created'].utc.to_s
89+
end
90+
return [last_modified, created, run_count, path_hash, name]
91+
end
92+
end
93+
94+
def run
95+
print_status("Prefetch Gathering started.")
96+
# Check to see what Windows Version is running.
97+
# Needed for offsets.
98+
# Tested on WinXP, Win2k3 and Win7 systems.
99+
# http://www.forensicswiki.org/wiki/Prefetch
100+
# http://www.forensicswiki.org/wiki/Windows_Prefetch_File_Format
101+
102+
sysnfo = client.sys.config.sysinfo['OS']
103+
error_msg = "You don't have enough privileges. Try getsystem."
104+
105+
if sysnfo =~/(Windows XP|2003|.NET)/
106+
# For some reason we need system privileges to read file
107+
# mace time on XP/2003 while we can do the same only
108+
# as admin on Win7.
109+
if not is_system?
110+
print_error(error_msg)
111+
return nil
112+
end
113+
# Offsets for WinXP & Win2k3
114+
print_good("Detected #{sysnfo} (max 128 entries)")
115+
name_offset = 0x10
116+
hash_offset = 0x4C
117+
runcount_offset = 0x90
118+
# Registry key for timezone
119+
key_value = "StandardName"
120+
121+
elsif sysnfo =~/(Windows 7)/
122+
if not is_admin?
123+
print_error(error_msg)
124+
return nil
125+
end
126+
# Offsets for Win7
127+
print_good("Detected #{sysnfo} (max 128 entries)")
128+
name_offset = 0x10
129+
hash_offset = 0x4C
130+
runcount_offset = 0x98
131+
# Registry key for timezone
132+
key_value = "TimeZoneKeyName"
133+
134+
else
135+
print_error("No offsets for the target Windows version. Currently works only on WinXP, Win2k3 and Win7.")
136+
return nil
137+
end
138+
139+
table = Rex::Ui::Text::Table.new(
140+
'Header' => "Prefetch Information",
141+
'Indent' => 1,
142+
'Columns' =>
143+
[
144+
"Modified (mace)",
145+
"Created (mace)",
146+
"Run Count",
147+
"Hash",
148+
"Filename"
149+
])
150+
print_prefetch_key_value
151+
print_timezone_key_values(key_value)
152+
print_good("Current UTC Time: %s" % Time.now.utc)
153+
sys_root = expand_path("%SYSTEMROOT%")
154+
full_path = sys_root + "\\Prefetch\\"
155+
file_type = "*.pf"
156+
print_status("Gathering information from remote system. This will take awhile..")
157+
158+
# Goes through the files in Prefetch directory, creates file paths for the
159+
# gather_pf_info function that enumerates all the pf info
160+
161+
getfile_prefetch_filenames = client.fs.file.search(full_path, file_type)
162+
if getfile_prefetch_filenames.empty? or getfile_prefetch_filenames.nil?
163+
print_error("Could not find/access any .pf files. Can't continue. (Might be temporary error..)")
164+
return nil
165+
else
166+
getfile_prefetch_filenames.each do |file|
167+
if file.empty? or file.nil?
168+
next
169+
else
170+
filename = ::File.join(file['path'], file['name'])
171+
pf_entry = gather_pf_info(name_offset, hash_offset, runcount_offset, filename)
172+
if not pf_entry.nil?
173+
table << pf_entry
174+
end
175+
end
176+
end
177+
end
178+
179+
# Stores and prints out results
180+
results = table.to_s
181+
loot = store_loot("prefetch_info", "text/plain", session, results, nil, "Prefetch Information")
182+
print_line("\n" + results + "\n")
183+
print_status("Finished gathering information from prefetch files.")
184+
print_status("Results stored in: #{loot}")
185+
end
186+
end

0 commit comments

Comments
 (0)