Path: blob/master/modules/auxiliary/analyze/crack_aix.rb
19852 views
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Auxiliary6include Msf::Auxiliary::PasswordCracker7include Msf::Exploit::Deprecated8moved_from 'auxiliary/analyze/jtr_aix'910def initialize11super(12'Name' => 'Password Cracker: AIX',13'Description' => %(14This module uses John the Ripper or Hashcat to identify weak passwords that have been15acquired from passwd files on AIX systems. These utilize DES hashing.16DES is format 1500 in Hashcat.17),18'Author' => [19'theLightCosine',20'hdm',21'h00die' # hashcat integration22],23'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)24'Actions' => [25['john', { 'Description' => 'Use John the Ripper' }],26['hashcat', { 'Description' => 'Use Hashcat' }],27],28'DefaultAction' => 'john',29'Notes' => {30'Stability' => [CRASH_SAFE],31'SideEffects' => [],32'Reliability' => []33}34)3536register_options(37[38OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),39OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])40]41)42end4344def show_command(cracker_instance)45return unless datastore['ShowCommand']4647if action.name == 'john'48cmd = cracker_instance.john_crack_command49elsif action.name == 'hashcat'50cmd = cracker_instance.hashcat_crack_command51end52print_status(" Cracking Command: #{cmd.join(' ')}")53end5455def check_results(passwords, results, hash_type, method)56passwords.each do |password_line|57password_line.chomp!58next if password_line.blank?5960fields = password_line.split(':')61# If we don't have an expected minimum number of fields, this is probably not a hash line62next unless fields.count >= 36364cred = { 'hash_type' => hash_type, 'method' => method }65if action.name == 'john'66cred['username'] = fields.shift67cred['core_id'] = fields.pop684.times { fields.pop } # Get rid of extra :69cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it70elsif action.name == 'hashcat'71cred['core_id'] = fields.shift72cred['hash'] = fields.shift73cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it74next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines7576# we don't have the username since we overloaded it with the core_id (since its a better fit for us)77# so we can now just go grab the username from the DB78cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username79end80results = process_cracker_results(results, cred)81end8283results84end8586def run87tbl = tbl = cracker_results_table8889hash_types_to_crack = ['descrypt']90jobs_to_do = []9192# build our job list93hash_types_to_crack.each do |hash_type|94job = hash_job(hash_type, action.name)95if job.nil?96print_status("No #{hash_type} found to crack")97else98jobs_to_do << job99end100end101102# bail early of no jobs to do103if jobs_to_do.empty?104print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")105return106end107108# array of arrays for cracked passwords.109# Inner array format: db_id, hash_type, username, password, method_of_crack110results = []111112cracker = new_password_cracker(action.name)113114# generate our wordlist and close the file handle. max length of DES is 8115wordlist = wordlist_file(8)116unless wordlist117print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')118return119end120121wordlist.close122print_status "Wordlist file written out to #{wordlist.path}"123124cleanup_files = [wordlist.path]125126jobs_to_do.each do |job|127format = job['type']128hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")129hash_file.puts job['formatted_hashlist']130hash_file.close131cracker.hash_path = hash_file.path132cleanup_files << hash_file.path133134# dupe our original cracker so we can safely change options between each run135cracker_instance = cracker.dup136cracker_instance.format = format137138if action.name == 'john'139cracker_instance.fork = datastore['FORK']140end141142# first check if anything has already been cracked so we don't report it incorrectly143print_status "Checking #{format} hashes already cracked..."144results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')145vprint_good(append_results(tbl, results)) unless results.empty?146job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list147next if job['cred_ids_left_to_crack'].empty?148149if action.name == 'john'150print_status "Cracking #{format} hashes in single mode..."151cracker_instance.mode_single(wordlist.path)152show_command cracker_instance153cracker_instance.crack do |line|154vprint_status(" #{line.chomp}")155end156results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')157vprint_good(append_results(tbl, results)) unless results.empty?158job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list159next if job['cred_ids_left_to_crack'].empty?160161print_status "Cracking #{format} hashes in normal mode..."162cracker_instance.mode_normal163show_command cracker_instance164cracker_instance.crack do |line|165vprint_status(" #{line.chomp}")166end167results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')168vprint_good(append_results(tbl, results)) unless results.empty?169job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list170next if job['cred_ids_left_to_crack'].empty?171end172173if datastore['INCREMENTAL']174print_status "Cracking #{format} hashes in incremental mode..."175cracker_instance.mode_incremental176show_command cracker_instance177cracker_instance.crack do |line|178vprint_status(" #{line.chomp}")179end180results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')181vprint_good(append_results(tbl, results)) unless results.empty?182job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list183next if job['cred_ids_left_to_crack'].empty?184end185186next unless datastore['WORDLIST']187188print_status "Cracking #{format} hashes in wordlist mode..."189cracker_instance.mode_wordlist(wordlist.path)190# Turn on KoreLogic rules if the user asked for it191if action.name == 'john' && datastore['KORELOGIC']192cracker_instance.rules = 'KoreLogicRules'193print_status 'Applying KoreLogic ruleset...'194end195show_command cracker_instance196cracker_instance.crack do |line|197vprint_status(" #{line.chomp}")198end199200results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')201vprint_good(append_results(tbl, results)) unless results.empty?202job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list203next if job['cred_ids_left_to_crack'].empty?204end205206# give a final print of results207print_good(append_results(tbl, results))208209if datastore['DeleteTempFiles']210cleanup_files.each do |f|211File.delete(f)212end213end214end215end216217218