Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/enum_prefetch.rb
21545 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Post
7
include Msf::Post::File
8
include Msf::Post::Windows::Priv
9
include Msf::Post::Windows::Registry
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'Windows Gather Prefetch File Information',
16
'Description' => %q{
17
This module gathers prefetch file information from WinXP, Win2k3 and Win7 systems
18
and current values of related registry keys. From each prefetch file we'll collect
19
filetime (converted to utc) of the last execution, file path hash, run count, filename
20
and the execution path.
21
},
22
'License' => MSF_LICENSE,
23
'Author' => ['TJ Glad <tjglad[at]cmail.nu>'],
24
'Platform' => ['win'],
25
'SessionType' => ['meterpreter'],
26
'Notes' => {
27
'Stability' => [CRASH_SAFE],
28
'SideEffects' => [],
29
'Reliability' => []
30
},
31
'Compat' => {
32
'Meterpreter' => {
33
'Commands' => %w[
34
stdapi_fs_search
35
stdapi_sys_config_getenv
36
]
37
}
38
}
39
)
40
)
41
end
42
43
def print_prefetch_key_value
44
# Checks if Prefetch registry key exists and what value it has.
45
prefetch_key_value = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\Session Manager\\Memory Management\\PrefetchParameters', 'EnablePrefetcher')
46
if prefetch_key_value == 0
47
print_error('EnablePrefetcher Value: (0) = Disabled (Non-Default).')
48
elsif prefetch_key_value == 1
49
print_good('EnablePrefetcher Value: (1) = Application launch prefetching enabled (Non-Default).')
50
elsif prefetch_key_value == 2
51
print_good('EnablePrefetcher Value: (2) = Boot prefetching enabled (Non-Default, excl. Win2k3).')
52
elsif prefetch_key_value == 3
53
print_good('EnablePrefetcher Value: (3) = Applaunch and boot enabled (Default Value, excl. Win2k3).')
54
else
55
print_error('No value or unknown value. Results might vary.')
56
end
57
end
58
59
def print_timezone_key_values(key_value)
60
# Looks for timezone information from registry.
61
timezone = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', key_value)
62
tz_bias = registry_getvaldata('HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation', 'Bias')
63
if timezone.nil? || tz_bias.nil?
64
print_line("Couldn't find key/value for timezone from registry.")
65
else
66
print_good('Remote: Timezone is %s.' % timezone)
67
if tz_bias < 0xfff
68
print_good('Remote: Localtime bias to UTC: -%s minutes.' % tz_bias)
69
else
70
offset = 0xffffffff
71
bias = offset - tz_bias
72
print_good('Remote: Localtime bias to UTC: +%s minutes.' % bias)
73
end
74
end
75
end
76
77
def gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)
78
# Collects the desired information from each prefetch file found
79
# from the system.
80
81
prefetch_file = read_file(filename)
82
if prefetch_file.blank?
83
print_error("Couldn't read file: #{filename}")
84
return nil
85
else
86
# First we extract the saved filename
87
pf_filename = prefetch_file[name_offset, 60]
88
idx = pf_filename.index("\x00\x00")
89
name = Rex::Text.to_ascii(pf_filename.slice(0..idx))
90
91
# Then we get the runcount
92
run_count = prefetch_file[runcount_offset, 4].unpack('v')[0]
93
94
# Then the filepath hash
95
path_hash = prefetch_file[hash_offset, 4].unpack('h*')[0].upcase.reverse
96
97
# Last we get the latest execution time
98
filetime_a = prefetch_file[filetime_offset, 16].unpack('q*')
99
filetime = filetime_a[0] + filetime_a[1]
100
last_exec = Time.at((filetime - 116444736000000000) / 10000000).utc.to_s
101
102
# This is for reading file paths of the executable from
103
# the prefetch file. We'll use this to find out from where the
104
# file was executed.
105
106
# First we'll use specific offsets for finding out the location
107
# and length of the filepath so that we can find it.
108
filepath = []
109
fpath_offset = prefetch_file[0x64, 2].unpack('v').first
110
fpath_length = prefetch_file[0x68, 2].unpack('v').first
111
filepath_data = prefetch_file[fpath_offset, fpath_length]
112
113
# This part will extract the filepath so that we can find and
114
# compare its contents to the filename we found previously. This
115
# allows us to find the filepath (if it can be found inside the
116
# prefetch file) used to execute the program
117
# referenced in the prefetch-file.
118
unless filepath_data.blank?
119
fpath_data_array = filepath_data.split("\\\x00D\x00E\x00V\x00I\x00C\x00E")
120
fpath_data_array.each do |path|
121
next if path.blank?
122
123
fpath_name = path.split('\\').last.gsub(/\0/, '')
124
if fpath_name == name
125
filepath << path
126
end
127
end
128
end
129
end
130
if filepath.blank?
131
filepath << '*** Filepath not found ***'
132
end
133
134
return [last_exec, path_hash, run_count, name, filepath[0]]
135
end
136
137
def run
138
print_status('Prefetch Gathering started.')
139
140
# Check to see what Windows Version is running.
141
# Needed for offsets.
142
# Tested on WinXP, Win2k3 and Win7 systems.
143
# http://www.forensicswiki.org/wiki/Prefetch
144
# http://www.forensicswiki.org/wiki/Windows_Prefetch_File_Format
145
146
error_msg = "You don't have enough privileges. Try getsystem."
147
148
version = get_version_info
149
if version.xp_or_2003?
150
151
if !is_admin?
152
print_error(error_msg)
153
return nil
154
end
155
156
# Offsets for WinXP & Win2k3
157
print_good("Detected #{version.product_name} (max 128 entries)")
158
name_offset = 0x10
159
hash_offset = 0x4C
160
runcount_offset = 0x90
161
filetime_offset = 0x78
162
# Registry key for timezone
163
key_value = 'StandardName'
164
165
elsif version.win7_or_2008r2? && !version.windows_server?
166
if !is_admin?
167
print_error(error_msg)
168
return nil
169
end
170
171
# Offsets for Win7
172
print_good("Detected #{version.product_name} (max 128 entries)")
173
name_offset = 0x10
174
hash_offset = 0x4C
175
runcount_offset = 0x98
176
filetime_offset = 0x78
177
# Registry key for timezone
178
key_value = 'TimeZoneKeyName'
179
else
180
print_error('No offsets for the target Windows version. Currently works only on WinXP, Win2k3 and Win7.')
181
return nil
182
end
183
184
table = Rex::Text::Table.new(
185
'Header' => 'Prefetch Information',
186
'Indent' => 1,
187
'Columns' =>
188
[
189
'Last execution (filetime)',
190
'Run Count',
191
'Hash',
192
'Filename',
193
'Filepath'
194
]
195
)
196
197
print_prefetch_key_value
198
print_timezone_key_values(key_value)
199
print_good('Current UTC Time: %s' % Time.now.utc)
200
sys_root = session.sys.config.getenv('SYSTEMROOT')
201
full_path = sys_root + '\\Prefetch\\'
202
file_type = '*.pf'
203
print_status('Gathering information from remote system. This will take awhile..')
204
205
# Goes through the files in Prefetch directory, creates file paths for the
206
# gather_pf_info function that enumerates all the pf info
207
208
getfile_prefetch_filenames = client.fs.file.search(full_path, file_type)
209
if getfile_prefetch_filenames.empty? || getfile_prefetch_filenames.nil?
210
print_error("Could not find/access any .pf files. Can't continue. (Might be temporary error..)")
211
return nil
212
else
213
getfile_prefetch_filenames.each do |file|
214
if file.empty? || file.nil?
215
next
216
else
217
filename = ::File.join(file['path'], file['name'])
218
pf_entry = gather_pf_info(name_offset, hash_offset, runcount_offset, filetime_offset, filename)
219
if !pf_entry.nil?
220
table << pf_entry
221
end
222
end
223
end
224
end
225
226
# Stores and prints out results
227
results = table.to_s
228
loot = store_loot('prefetch_info', 'text/plain', session, results, nil, 'Prefetch Information')
229
print_line("\n" + results + "\n")
230
print_status('Finished gathering information from prefetch files.')
231
print_status("Results stored in: #{loot}")
232
end
233
end
234
235