Path: blob/master/modules/auxiliary/analyze/apply_pot.rb
19612 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::PasswordCracker78def initialize9super(10'Name' => 'Apply Pot File To Hashes',11'Description' => %(12This module uses a John the Ripper or Hashcat .pot file to crack any password13hashes in the creds database instantly. JtR's --show functionality is used to14help combine all the passwords into an easy to use format.15),16'Author' => ['h00die'],17'License' => MSF_LICENSE,18'Actions' => [19['john', { 'Description' => 'Use John the Ripper' }],20# ['hashcat', 'Description' => 'Use Hashcat'], # removed for simplicity21],22'DefaultAction' => 'john',23'Notes' => {24'Stability' => [CRASH_SAFE],25'SideEffects' => [],26'Reliability' => []27}28)29deregister_options('ITERATION_TIMEOUT')30deregister_options('CUSTOM_WORDLIST')31deregister_options('KORELOGIC')32deregister_options('MUTATE')33deregister_options('USE_CREDS')34deregister_options('USE_DB_INFO')35deregister_options('USE_DEFAULT_WORDLIST')36deregister_options('USE_ROOT_WORDS')37deregister_options('USE_HOSTNAMES')38end3940# Not all hash formats include an 'id' field, which corresponds which db entry41# an item is to its hash. This can be problematic, especially when a username42# is used as a salt. Due to all the variations, we make a small HashLookup43# class to handle all the fields for easier lookup later.44class HashLookup45attr_accessor :db_hash, :jtr_hash, :username, :id4647def initialize(db_hash, jtr_hash, username, id)48@db_hash = db_hash49@jtr_hash = jtr_hash50@username = username51@id = id52end53end5455def show_run_command(cracker_instance)56return unless datastore['ShowCommand']5758cmd = cracker_instance.show_command59print_status(" Cracking Command: #{cmd.join(' ')}")60end6162def run63cracker = new_password_cracker(action.name)6465lookups = []6667# create one massive hash file with all the hashes68hashlist = Rex::Quickfile.new('hashes_tmp')69framework.db.creds(workspace: myworkspace).each do |core|70next if core.private.type == 'Metasploit::Credential::Password'7172jtr_hash = Metasploit::Framework::PasswordCracker::JtR::Formatter.hash_to_jtr(core)73hashlist.puts jtr_hash74lookups << HashLookup.new(core.private.data, jtr_hash, core.public, core.id)75end76hashlist.close77cracker.hash_path = hashlist.path78print_status "Hashes Written out to #{hashlist.path}"79cleanup_files = [cracker.hash_path]8081# cycle through all hash types we dump asking jtr to show us82# cracked passwords. The advantage to this vs just comparing83# john.pot to the hashes directly is we use jtr to recombine84# lanman, and other assorted nuances85[86'bcrypt', 'bsdicrypt', 'descrypt', 'lm',87'mscash', 'mscash2', 'netntlm', 'netntlmv2',88'md5crypt', 'mysql', 'mysql-sha1', 'mssql', 'mssql05', 'mssql12',89'oracle', 'oracle11', 'oracle12c', 'dynamic_1506', # oracles90'dynamic_1034', # postgres91# 'android-sha1', 'android-samsung-sha1', 'android-md5', # mobile is done with hashcat, so skip these92'PBKDF2-HMAC-SHA1', 'phpass', 'mediawiki', 'pbkdf2-sha256', # webapps93'xsha', 'xsha512', 'PBKDF2-HMAC-SHA512', # osx94'nt', # nt needs to be 2nd to last because it can hit on android hashes95'crypt' # crypt NEEDS TO BE LAST so it doesn't accidentally read in other compatible hashes96].each do |format|97print_status("Checking #{format} hashes against pot file")98cracker.format = format99show_run_command(cracker)100cracker.each_cracked_password.each do |password_line|101password_line.chomp!102next if password_line.blank? || password_line.nil?103104fields = password_line.split(':')105core_id = nil106case format107when 'descrypt'108next unless fields.count >= 3109110username = fields.shift111core_id = fields.pop1124.times { fields.pop } # Get rid of extra :113when 'netntlm', 'netntlmv2'114next unless fields.count >= 7115116username = fields.shift117core_id = fields.pop1189.times { fields.pop }119when 'md5crypt', 'bsdicrypt', 'crypt', 'bcrypt', 'xsha', 'xsha512'120next unless fields.count >= 7121122username = fields.shift123core_id = fields.pop1244.times { fields.pop }125when 'PBKDF2-HMAC-SHA512'126next unless fields.count >= 2127128username = fields.shift129core_id = fields.pop130when 'mssql', 'mssql05', 'mssql12', 'mysql', 'mysql-sha1',131'oracle', 'dynamic_1506', 'oracle11', 'oracle12c',132'PBKDF2-HMAC-SHA1', 'phpass', 'mediawiki', 'pbkdf2-sha256',133'mscash', 'mscash2'134next unless fields.count >= 3135136username = fields.shift137core_id = fields.pop138when 'dynamic_1034' # postgres139next unless fields.count >= 2140141username = fields.shift142fields.join(':')143# unfortunately to match up all the fields we need to pull the hash144# field as well, and it is only available in the pot file.145pot = cracker.pot || cracker.john_pot_file146147File.open(pot, 'rb').each do |line|148next unless line.start_with?('$dynamic_1034$') # postgres format149150lookups.each do |l|151pot_hash = line.split(':')[0]152raw_pot_hash = pot_hash.split('$')[2]153next unless l.username.to_s == username &&154l.jtr_hash == "#{username}:$dynamic_1034$#{raw_pot_hash}" &&155l.db_hash == raw_pot_hash156157core_id = l.id158break159end160end161when 'lm', 'nt'162next unless fields.count >= 7163164username = fields.shift165core_id = fields.pop1662.times { fields.pop }167# get the NT and LM hashes168nt_hash = fields.pop169fields.pop170core_id = fields.pop171password = fields.join(':')172if format == 'lm'173if password.blank?174if nt_hash == Metasploit::Credential::NTLMHash::BLANK_NT_HASH175password = ''176else177next178end179end180password = john_lm_upper_to_ntlm(password, nt_hash)181next if password.nil?182end183fields = password.split(':') # for consistency on the following join out of the case184end185next if core_id.nil?186187password = fields.join(':')188print_good "#{username}:#{password}"189# android hashes will also crack here, but the output fields are in a different order190# check if core_id is an int or not, for android hashes it wont convert191core_id_int = begin192Integer(core_id)193rescue StandardError194nil195end196next if core_id_int.nil?197198create_cracked_credential(username: username, password: password, core_id: core_id)199end200end201if datastore['DeleteTempFiles']202cleanup_files.each do |f|203File.delete(f)204end205end206end207end208209210