CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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