Skip to content

Commit cbbd7bf

Browse files
committed
Refacotor code
1 parent cdabb71 commit cbbd7bf

File tree

1 file changed

+124
-116
lines changed

1 file changed

+124
-116
lines changed

modules/post/windows/gather/enum_muicache.rb

Lines changed: 124 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,10 @@ def initialize(info={})
3232
))
3333
end
3434

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
3939
user_names = []
4040
user_homedir_paths = []
4141
user_sids = []
@@ -65,169 +65,174 @@ def find_user_names()
6565
return user_names, user_homedir_paths, user_sids
6666
end
6767

68+
# This function builds full registry muicache paths so that we can
69+
# later enumerate the muicahe registry key contents.
6870
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.
7171
user_mui_paths = []
7272
hive = "HKU\\"
73+
7374
sys_sids.each do |sid|
7475
full_path = hive + sid + mui_path
7576
user_mui_paths << full_path
7677
end
77-
return user_mui_paths
78+
79+
user_mui_paths
7880
end
7981

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+
8488
loot_path = Msf::Config::loot_directory
8589
all_user_entries = sys_users.zip(muicache_reg_keys, sys_paths)
90+
8691
all_user_entries.each do |user, reg_key, sys_path|
8792
local_hive_copy = ::File.join(loot_path, "#{sysinfo['Computer']}_#{user}_HIVE_#{::Time.now.utc.strftime('%Y%m%d.%M%S')}")
8893
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
90101
# If the registry_enumvals returns us content we'll know that we
91102
# can access the registry directly and thus continue to process
92103
# the content collected from there.
93104
print_status("User #{user}: Enumerating registry..")
94105
subkeys.each do |key|
95106
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?
97109
end
98110
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)
105111
end
106112
end
107-
return table
113+
114+
results
108115
end
109116

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)
115122
program_path = expand_path(key)
116123
if file_exist?(key)
117-
table << [user, program_path, "File found"]
124+
return [user, program_path, "File found"]
118125
else
119-
table << [user, program_path, "File not found"]
126+
return [user, program_path, "File not found"]
120127
end
121128
end
122129

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)
129136
user_home_path = expand_path(sys_path)
130137
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
142141
print_error("Couldn't locate/download #{user}'s registry hive. Can't proceed.")
143142
return nil
144143
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)
145154
end
146155

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.
147159
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.
151160
hive_status = false
161+
152162
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
167166
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+
168181
end
169-
return hive_status
182+
183+
hive_status
170184
end
171185

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..")
176191
err_msg = "Error parsing hive. Can't continue."
177192
hive = Rex::Registry::Hive.new(local_hive_copy)
178193
if hive.nil?
179194
print_error(err_msg)
180195
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?
205221
end
206222
end
223+
207224
File.delete(local_hive_copy) # Downloaded hive gets deleted after processing
208-
return table
209-
end
210225

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
220227
end
221228

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
222235
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-
231236
print_status("Starting to enumerate MuiCache registry keys..")
232237
sys_info = sysinfo['OS']
233238

@@ -254,24 +259,27 @@ def run
254259
"File status",
255260
])
256261

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
259264

260265
if sys_users.blank?
261266
print_error("Was not able to find any user accounts. Unable to continue.")
262267
return nil
263268
else
264-
print_user_names(sys_users)
269+
print_good("Users found: #{sys_users.join(", ")}")
265270
end
266271

267272
print_status("Phase 2: Searching registry hives..")
268273
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 }
270277

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")
274281
print_status("Results stored in: #{loot}")
275282
print_status("Execution finished.")
276283
end
284+
277285
end

0 commit comments

Comments
 (0)