@@ -32,10 +32,10 @@ def initialize(info={})
32
32
) )
33
33
end
34
34
35
- def find_user_names ( )
36
- # This function scrapes usernames, sids and homepaths from the
37
- # registry so that we'll know what user accounts are on the system
38
- # and where we can find those users registry hives.
35
+ # Scrapes usernames, sids and homepaths from the registry so that we'll know
36
+ # what user accounts are on the system and where we can find those users
37
+ # registry hives.
38
+ def find_user_names
39
39
user_names = [ ]
40
40
user_homedir_paths = [ ]
41
41
user_sids = [ ]
@@ -65,169 +65,174 @@ def find_user_names()
65
65
return user_names , user_homedir_paths , user_sids
66
66
end
67
67
68
+ # This function builds full registry muicache paths so that we can
69
+ # later enumerate the muicahe registry key contents.
68
70
def enum_muicache_paths ( sys_sids , mui_path )
69
- # This function builds full registry muicache paths so that we can
70
- # later enumerate the muicahe registry key contents.
71
71
user_mui_paths = [ ]
72
72
hive = "HKU\\ "
73
+
73
74
sys_sids . each do |sid |
74
75
full_path = hive + sid + mui_path
75
76
user_mui_paths << full_path
76
77
end
77
- return user_mui_paths
78
+
79
+ user_mui_paths
78
80
end
79
81
80
- def enumerate_muicache ( muicache_reg_keys , sys_users , sys_paths , muicache , hive_file , table )
81
- # This is the main enumeration function that calls other main
82
- # functions depending if we can access the registry directly or if
83
- # we need to download the hive and process it locally.
82
+ # This is the main enumeration function that calls other main
83
+ # functions depending if we can access the registry directly or if
84
+ # we need to download the hive and process it locally.
85
+ def enumerate_muicache ( muicache_reg_keys , sys_users , sys_paths , muicache , hive_file )
86
+ results = [ ]
87
+
84
88
loot_path = Msf ::Config ::loot_directory
85
89
all_user_entries = sys_users . zip ( muicache_reg_keys , sys_paths )
90
+
86
91
all_user_entries . each do |user , reg_key , sys_path |
87
92
local_hive_copy = ::File . join ( loot_path , "#{ sysinfo [ 'Computer' ] } _#{ user } _HIVE_#{ ::Time . now . utc . strftime ( '%Y%m%d.%M%S' ) } " )
88
93
subkeys = registry_enumvals ( reg_key )
89
- unless subkeys . blank?
94
+ if subkeys . blank?
95
+ # If the registry_enumvals returns us nothing then we'll know
96
+ # that the user is most likely not logged in and we'll need to
97
+ # download and process users hive locally.
98
+ print_error ( "User #{ user } : Can't access registry (maybe the user is not logged in atm?). Trying NTUSER.DAT/USRCLASS.DAT.." )
99
+ results = process_hive ( sys_path , user , local_hive_copy , muicache , hive_file ) || [ ]
100
+ else
90
101
# If the registry_enumvals returns us content we'll know that we
91
102
# can access the registry directly and thus continue to process
92
103
# the content collected from there.
93
104
print_status ( "User #{ user } : Enumerating registry.." )
94
105
subkeys . each do |key |
95
106
if key [ 0 ] != "@" and key != "LangID" and not key . nil?
96
- check_file_exists ( key , user , table )
107
+ result = check_file_exists ( key , user )
108
+ results << result unless result . nil?
97
109
end
98
110
end
99
- else
100
- # If the registry_enumvals returns us nothing then we'll know
101
- # that the user is most likely not logged in and we'll need to
102
- # download and process users hive locally.
103
- print_error ( "User #{ user } : Can't access registry (maybe the user is not logged in atm?). Trying NTUSER.DAT/USRCLASS.DAT.." )
104
- process_hive ( sys_path , user , local_hive_copy , table , muicache , hive_file )
105
111
end
106
112
end
107
- return table
113
+
114
+ results
108
115
end
109
116
110
- def check_file_exists ( key , user , table )
111
- # This function will check if it can find the program executable
112
- # from the path it found from the registry. Permissions might affect
113
- # if it detects the executable but it should be otherwise fairly
114
- # reliable.
117
+ # This function will check if it can find the program executable
118
+ # from the path it found from the registry. Permissions might affect
119
+ # if it detects the executable but it should be otherwise fairly
120
+ # reliable.
121
+ def check_file_exists ( key , user )
115
122
program_path = expand_path ( key )
116
123
if file_exist? ( key )
117
- table << [ user , program_path , "File found" ]
124
+ return [ user , program_path , "File found" ]
118
125
else
119
- table << [ user , program_path , "File not found" ]
126
+ return [ user , program_path , "File not found" ]
120
127
end
121
128
end
122
129
123
- def process_hive ( sys_path , user , local_hive_copy , table , muicache , hive_file )
124
- # This function will check if the filepath contains a registry hive
125
- # and if it does it'll proceed to call the function responsible of
126
- # downloading the hive. After successfull download it'll continue to
127
- # call the hive_parser function which will extract the contents of
128
- # the MUICache registry key.
130
+ # This function will check if the filepath contains a registry hive
131
+ # and if it does it'll proceed to call the function responsible of
132
+ # downloading the hive. After successfull download it'll continue to
133
+ # call the hive_parser function which will extract the contents of
134
+ # the MUICache registry key.
135
+ def process_hive ( sys_path , user , local_hive_copy , muicache , hive_file )
129
136
user_home_path = expand_path ( sys_path )
130
137
hive_path = user_home_path + hive_file
131
- ntuser_status = client . fs . file . exists? ( hive_path )
132
- if ntuser_status == true
133
- print_status ( "Downloading #{ user } 's NTUSER.DAT/USRCLASS.DAT file.." )
134
- hive_status = hive_download_status ( local_hive_copy , hive_path )
135
- if hive_status == true
136
- hive_parser ( local_hive_copy , muicache , user , table )
137
- else
138
- print_error ( "All registry hive download attempts failed. Unable to continue." )
139
- return nil
140
- end
141
- else
138
+ ntuser_status = file_exist? ( hive_path )
139
+
140
+ unless ntuser_status == true
142
141
print_error ( "Couldn't locate/download #{ user } 's registry hive. Can't proceed." )
143
142
return nil
144
143
end
144
+
145
+ print_status ( "Downloading #{ user } 's NTUSER.DAT/USRCLASS.DAT file.." )
146
+ hive_status = hive_download_status ( local_hive_copy , hive_path )
147
+
148
+ unless hive_status == true
149
+ print_error ( "All registry hive download attempts failed. Unable to continue." )
150
+ return nil
151
+ end
152
+
153
+ hive_parser ( local_hive_copy , muicache , user )
145
154
end
146
155
156
+ # This function downloads registry hives and checks for integrity
157
+ # after the transfer has completed so that we don't end up
158
+ # processing broken registry hive.
147
159
def hive_download_status ( local_hive_copy , hive_path )
148
- # This function downloads registry hives and checks for integrity
149
- # after the transfer has completed so that we don't end up
150
- # processing broken registry hive.
151
160
hive_status = false
161
+
152
162
3 . times do
153
- remote_hive_hash_raw = client . fs . file . md5 ( hive_path )
154
- unless remote_hive_hash_raw . blank?
155
- remote_hive_hash = remote_hive_hash_raw . unpack ( 'H*' )
156
- session . fs . file . download_file ( local_hive_copy , hive_path )
157
- local_hive_hash = file_local_digestmd5 ( local_hive_copy )
158
- if local_hive_hash == remote_hive_hash [ 0 ]
159
- print_good ( "Hive downloaded successfully." )
160
- hive_status = true
161
- break
162
- else
163
- print_error ( "Hive download corrupted, trying again (max 3 times).." )
164
- File . delete ( local_hive_copy ) # Downloaded corrupt hive gets deleted before new attempt is made
165
- hive_status = false
166
- end
163
+ remote_hive_hash_raw = file_remote_digestmd5 ( hive_path )
164
+ if remote_hive_hash_raw . blank?
165
+ next
167
166
end
167
+
168
+ remote_hive_hash = remote_hive_hash_raw . unpack ( 'H*' )
169
+ session . fs . file . download_file ( local_hive_copy , hive_path )
170
+ local_hive_hash = file_local_digestmd5 ( local_hive_copy )
171
+ if local_hive_hash == remote_hive_hash [ 0 ]
172
+ print_good ( "Hive downloaded successfully." )
173
+ hive_status = true
174
+ break
175
+ else
176
+ print_error ( "Hive download corrupted, trying again (max 3 times).." )
177
+ File . delete ( local_hive_copy ) # Downloaded corrupt hive gets deleted before new attempt is made
178
+ hive_status = false
179
+ end
180
+
168
181
end
169
- return hive_status
182
+
183
+ hive_status
170
184
end
171
185
172
- def hive_parser ( local_hive_copy , muicache , user , table )
173
- # This function is responsible for parsing the downloaded hive and
174
- # extracting the contents of the MUICache registry key.
175
- print_status ( "Phase 3: Parsing registry content.." )
186
+ # This function is responsible for parsing the downloaded hive and
187
+ # extracting the contents of the MUICache registry key.
188
+ def hive_parser ( local_hive_copy , muicache , user )
189
+ results = [ ]
190
+ print_status ( "Parsing registry content.." )
176
191
err_msg = "Error parsing hive. Can't continue."
177
192
hive = Rex ::Registry ::Hive . new ( local_hive_copy )
178
193
if hive . nil?
179
194
print_error ( err_msg )
180
195
return nil
181
- else
182
- muicache_key = hive . relative_query ( muicache )
183
- if muicache_key . nil?
184
- print_error ( err_msg )
185
- return nil
186
- else
187
- muicache_key_value_list = muicache_key . value_list
188
- if muicache_key_value_list . nil?
189
- print_error ( err_msg )
190
- return nil
191
- else
192
- muicache_key_values = muicache_key_value_list . values
193
- if muicache_key_values . nil?
194
- print_error ( err_msg )
195
- return nil
196
- else
197
- muicache_key_values . each do |value |
198
- key = value . name
199
- if key [ 0 ] != "@" and key != "LangID" and not key . nil?
200
- check_file_exists ( key , user , table )
201
- end
202
- end
203
- end
204
- end
196
+ end
197
+
198
+ muicache_key = hive . relative_query ( muicache )
199
+ if muicache_key . nil?
200
+ print_error ( err_msg )
201
+ return nil
202
+ end
203
+
204
+ muicache_key_value_list = muicache_key . value_list
205
+ if muicache_key_value_list . nil?
206
+ print_error ( err_msg )
207
+ return nil
208
+ end
209
+
210
+ muicache_key_values = muicache_key_value_list . values
211
+ if muicache_key_values . nil?
212
+ print_error ( err_msg )
213
+ return nil
214
+ end
215
+
216
+ muicache_key_values . each do |value |
217
+ key = value . name
218
+ if key [ 0 ] != "@" and key != "LangID" and not key . nil?
219
+ result = check_file_exists ( key , user )
220
+ results << result unless result . nil?
205
221
end
206
222
end
223
+
207
224
File . delete ( local_hive_copy ) # Downloaded hive gets deleted after processing
208
- return table
209
- end
210
225
211
- def print_user_names ( sys_users )
212
- # This prints usernames pulled from the paths found from the
213
- # registry.
214
- user_list = [ ]
215
- sys_users . each do |user |
216
- user_list << user
217
- end
218
- users = user_list . join ( ", " )
219
- print_good ( "Found users: #{ users } " )
226
+ results
220
227
end
221
228
229
+ # Information about the MUICache registry key was collected from:
230
+ #
231
+ # - Windows Forensic Analysis Toolkit / 2012 / Harlan Carvey
232
+ # - Windows Registry Forensics / 2011 / Harlan Carvey
233
+ # - http://forensicartifacts.com/2010/08/registry-muicache/
234
+ # - http://www.irongeek.com/i.php?page=security/windows-forensics-registry-and-file-system-spots
222
235
def run
223
-
224
- # Information about the MUICache registry key was collected from:
225
- #
226
- # - Windows Forensic Analysis Toolkit / 2012 / Harlan Carvey
227
- # - Windows Registry Forensics / 2011 / Harlan Carvey
228
- # - http://forensicartifacts.com/2010/08/registry-muicache/
229
- # - http://www.irongeek.com/i.php?page=security/windows-forensics-registry-and-file-system-spots
230
-
231
236
print_status ( "Starting to enumerate MuiCache registry keys.." )
232
237
sys_info = sysinfo [ 'OS' ]
233
238
@@ -254,24 +259,27 @@ def run
254
259
"File status" ,
255
260
] )
256
261
257
- print_status ( "Phase 1: Searching usernames .." )
258
- sys_users , sys_paths , sys_sids = find_user_names ( )
262
+ print_status ( "Phase 1: Searching user names .." )
263
+ sys_users , sys_paths , sys_sids = find_user_names
259
264
260
265
if sys_users . blank?
261
266
print_error ( "Was not able to find any user accounts. Unable to continue." )
262
267
return nil
263
268
else
264
- print_user_names ( sys_users )
269
+ print_good ( "Users found: #{ sys_users . join ( ", " ) } " )
265
270
end
266
271
267
272
print_status ( "Phase 2: Searching registry hives.." )
268
273
muicache_reg_keys = enum_muicache_paths ( sys_sids , muicache )
269
- results = enumerate_muicache ( muicache_reg_keys , sys_users , sys_paths , muicache , hive_file , table ) . to_s
274
+ results = enumerate_muicache ( muicache_reg_keys , sys_users , sys_paths , muicache , hive_file )
275
+
276
+ results . each { |r | table << r }
270
277
271
- print_status ( "Phase 4 : Processing results.." )
272
- loot = store_loot ( "muicache_info" , "text/plain" , session , results , nil , "MUICache Information" )
273
- print_line ( "\n " + results + "\n " )
278
+ print_status ( "Phase 3 : Processing results.." )
279
+ loot = store_loot ( "muicache_info" , "text/plain" , session , table . to_s , nil , "MUICache Information" )
280
+ print_line ( "\n " + table . to_s + "\n " )
274
281
print_status ( "Results stored in: #{ loot } " )
275
282
print_status ( "Execution finished." )
276
283
end
284
+
277
285
end
0 commit comments