Path: blob/master/modules/auxiliary/analyze/crack_mobile.rb
19567 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' => 'Password Cracker: Mobile',11'Description' => %{12This module uses Hashcat to identify weak passwords that have been13acquired from Android systems. These utilize MD5 or SHA1 hashing.14Android (Samsung) SHA1 is format 5800 in Hashcat. Android15(non-Samsung) SHA1 is format 110 in Hashcat. Android MD5 is format 10.16JTR does not support Android hashes at the time of writing.17},18'Author' => [19'h00die'20],21'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)22'Actions' => [23['hashcat', { 'Description' => 'Use Hashcat' }],24],25'DefaultAction' => 'hashcat',26'Notes' => {27'Stability' => [CRASH_SAFE],28'SideEffects' => [],29'Reliability' => []30}31)3233register_options(34[35OptBool.new('SAMSUNG', [false, 'Include Samsung SHA1 hashes', true]),36OptBool.new('SHA1', [false, 'Include Android-SHA1 hashes', true]),37OptBool.new('MD5', [false, 'Include Android-MD5 hashes', true]),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' # leaving this code here figuring jtr will eventually come around, but its an unused code block48cmd = 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(':')61cred = { 'hash_type' => hash_type, 'method' => method }62# If we don't have an expected minimum number of fields, this is probably not a hash line63if action.name == 'john'64next unless fields.count >= 36566cred['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'71next unless fields.count >= 27273cred['core_id'] = fields.shift74cred['hash'] = "#{fields.shift}:#{fields.shift}" # grab hash and salt75cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with : in them76next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines7778# we don't have the username since we overloaded it with the core_id (since its a better fit for us)79# so we can now just go grab the username from the DB80cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username81end82results = process_cracker_results(results, cred)83end84results85end8687def run88tbl = Rex::Text::Table.new(89'Header' => 'Cracked Hashes',90'Indent' => 1,91'Columns' => ['DB ID', 'Hash Type', 'Username', 'Cracked Password', 'Method']92)9394# array of hashes in jtr_format in the db, converted to an OR combined regex95hash_types_to_crack = []96hash_types_to_crack << 'android-sha1' if datastore['SHA1']97hash_types_to_crack << 'android-samsung-sha1' if datastore['SAMSUNG']98hash_types_to_crack << 'android-md5' if datastore['MD5']99100jobs_to_do = []101102# build our job list103hash_types_to_crack.each do |hash_type|104job = hash_job(hash_type, action.name)105if job.nil?106print_status("No #{hash_type} found to crack")107else108jobs_to_do << job109end110end111112# bail early of no jobs to do113if jobs_to_do.empty?114print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")115return116end117118# array of arrays for cracked passwords.119# Inner array format: db_id, hash_type, username, password, method_of_crack120results = []121122cracker = new_password_cracker(action.name)123124# generate our wordlist and close the file handle. max length of DES is 8125wordlist = wordlist_file(8)126unless wordlist127print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')128return129end130131wordlist.close132print_status "Wordlist file written out to #{wordlist.path}"133134cleanup_files = [wordlist.path]135136jobs_to_do.each do |job|137format = job['type']138hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")139hash_file.puts job['formatted_hashlist']140hash_file.close141cracker.hash_path = hash_file.path142cleanup_files << hash_file.path143# dupe our original cracker so we can safely change options between each run144cracker_instance = cracker.dup145cracker_instance.format = format146147if action.name == 'john'148cracker_instance.fork = datastore['FORK']149end150151# first check if anything has already been cracked so we don't report it incorrectly152print_status "Checking #{format} hashes already cracked..."153results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')154vprint_good(append_results(tbl, results)) unless results.empty?155job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list156next if job['cred_ids_left_to_crack'].empty?157158if action.name == 'john'159print_status "Cracking #{format} hashes in single mode..."160cracker_instance.mode_single(wordlist.path)161show_command cracker_instance162cracker_instance.crack do |line|163vprint_status line.chomp164end165results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')166vprint_good(append_results(tbl, results)) unless results.empty?167job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list168next if job['cred_ids_left_to_crack'].empty?169170print_status "Cracking #{format} hashes in normal mode..."171cracker_instance.mode_normal172show_command cracker_instance173cracker_instance.crack do |line|174vprint_status line.chomp175end176results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')177vprint_good(append_results(tbl, results)) unless results.empty?178job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list179next if job['cred_ids_left_to_crack'].empty?180end181182if action.name == 'hashcat'183print_status "Cracking #{format} hashes in pin mode..."184cracker_instance.mode_pin185show_command cracker_instance186cracker_instance.crack do |line|187vprint_status line.chomp188end189results = check_results(cracker_instance.each_cracked_password, results, format, 'Pin')190vprint_good(append_results(tbl, results)) unless results.empty?191job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list192next if job['cred_ids_left_to_crack'].empty?193end194195if datastore['INCREMENTAL']196print_status "Cracking #{format} hashes in incremental mode..."197cracker_instance.mode_incremental198show_command cracker_instance199cracker_instance.crack do |line|200vprint_status line.chomp201end202results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')203vprint_good(append_results(tbl, results)) unless results.empty?204job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list205next if job['cred_ids_left_to_crack'].empty?206end207208if datastore['WORDLIST']209print_status "Cracking #{format} hashes in wordlist mode..."210cracker_instance.mode_wordlist(wordlist.path)211# Turn on KoreLogic rules if the user asked for it212if action.name == 'john' && datastore['KORELOGIC']213cracker_instance.rules = 'KoreLogicRules'214print_status 'Applying KoreLogic ruleset...'215end216show_command cracker_instance217cracker_instance.crack do |line|218vprint_status line.chomp219end220221results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')222vprint_good(append_results(tbl, results)) unless results.empty?223job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list224next if job['cred_ids_left_to_crack'].empty?225end226227# give a final print of results228print_good(append_results(tbl, results))229end230231if datastore['DeleteTempFiles']232cleanup_files.each do |f|233File.delete(f)234end235end236end237end238239240