Path: blob/master/modules/auxiliary/analyze/crack_aix.rb
21537 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['auto', { 'Description' => 'Auto-selection of cracker' }]28],29'DefaultAction' => 'auto',30'Notes' => {31'Stability' => [CRASH_SAFE],32'SideEffects' => [],33'Reliability' => []34}35)3637register_options(38[39OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),40OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])41]42)43end4445def show_command(cracker_instance)46return unless datastore['ShowCommand']4748if @cracker_type == 'john'49cmd = cracker_instance.john_crack_command50elsif @cracker_type == 'hashcat'51cmd = cracker_instance.hashcat_crack_command52end53print_status(" Cracking Command: #{cmd.join(' ')}")54end5556def check_results(passwords, results, hash_type, method)57passwords.each do |password_line|58password_line.chomp!59next if password_line.blank?6061fields = password_line.split(':')62# If we don't have an expected minimum number of fields, this is probably not a hash line63next unless fields.count >= 36465cred = { 'hash_type' => hash_type, 'method' => method }66if @cracker_type == 'john'67cred['username'] = fields.shift68cred['core_id'] = fields.pop694.times { fields.pop } # Get rid of extra :70cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it71elsif @cracker_type == 'hashcat'72cred['core_id'] = fields.shift73cred['hash'] = fields.shift74cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it75next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines7677# we don't have the username since we overloaded it with the core_id (since its a better fit for us)78# so we can now just go grab the username from the DB79cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username80end81results = process_cracker_results(results, cred)82end8384results85end8687def run88tbl = cracker_results_table89cracker = new_password_cracker(action.name)90if action.name == 'auto'91@cracker_type = cracker.get_type92else93@cracker_type = action.name94end9596hash_types_to_crack = ['descrypt']97jobs_to_do = []9899# build our job list100hash_types_to_crack.each do |hash_type|101job = hash_job(hash_type, @cracker_type)102if job.nil?103print_status("No #{hash_type} found to crack")104else105jobs_to_do << job106end107end108109# bail early of no jobs to do110if jobs_to_do.empty?111print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")112return113end114115# array of arrays for cracked passwords.116# Inner array format: db_id, hash_type, username, password, method_of_crack117results = []118119# generate our wordlist and close the file handle. max length of DES is 8120wordlist = wordlist_file(8)121unless wordlist122print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')123return124end125126wordlist.close127print_status "Wordlist file written out to #{wordlist.path}"128129cleanup_files = [wordlist.path]130131jobs_to_do.each do |job|132format = job['type']133hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")134hash_file.puts job['formatted_hashlist']135hash_file.close136cracker.hash_path = hash_file.path137cleanup_files << hash_file.path138139# dupe our original cracker so we can safely change options between each run140cracker_instance = cracker.dup141cracker_instance.format = format142143if @cracker_type == 'john'144cracker_instance.fork = datastore['FORK']145end146147# first check if anything has already been cracked so we don't report it incorrectly148print_status "Checking #{format} hashes already cracked..."149results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')150vprint_good(append_results(tbl, results)) unless results.empty?151job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list152next if job['cred_ids_left_to_crack'].empty?153154if @cracker_type == 'john'155print_status "Cracking #{format} hashes in single mode..."156cracker_instance.mode_single(wordlist.path)157show_command cracker_instance158cracker_instance.crack do |line|159vprint_status(" #{line.chomp}")160end161results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')162vprint_good(append_results(tbl, results)) unless results.empty?163job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list164next if job['cred_ids_left_to_crack'].empty?165166print_status "Cracking #{format} hashes in normal mode..."167cracker_instance.mode_normal168show_command cracker_instance169cracker_instance.crack do |line|170vprint_status(" #{line.chomp}")171end172results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')173vprint_good(append_results(tbl, results)) unless results.empty?174job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list175next if job['cred_ids_left_to_crack'].empty?176end177178if datastore['INCREMENTAL']179print_status "Cracking #{format} hashes in incremental mode..."180cracker_instance.mode_incremental181show_command cracker_instance182cracker_instance.crack do |line|183vprint_status(" #{line.chomp}")184end185results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')186vprint_good(append_results(tbl, results)) unless results.empty?187job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list188next if job['cred_ids_left_to_crack'].empty?189end190191next unless datastore['WORDLIST']192193print_status "Cracking #{format} hashes in wordlist mode..."194cracker_instance.mode_wordlist(wordlist.path)195# Turn on KoreLogic rules if the user asked for it196if @cracker_type == 'john' && datastore['KORELOGIC']197cracker_instance.rules = 'KoreLogicRules'198print_status 'Applying KoreLogic ruleset...'199end200show_command cracker_instance201cracker_instance.crack do |line|202vprint_status(" #{line.chomp}")203end204205results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')206vprint_good(append_results(tbl, results)) unless results.empty?207job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list208next if job['cred_ids_left_to_crack'].empty?209end210211# give a final print of results212print_good(append_results(tbl, results))213214if datastore['DeleteTempFiles']215cleanup_files.each do |f|216File.delete(f)217end218end219end220end221222223