Path: blob/master/modules/auxiliary/analyze/crack_linux.rb
19758 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_linux'910def initialize11super(12'Name' => 'Password Cracker: Linux',13'Description' => %{14This module uses John the Ripper or Hashcat to identify weak passwords that have been15acquired from unshadowed passwd files from Unix/Linux systems. The module will only crack16MD5, BSDi and DES implementations by default. However, it can also crack17Blowfish and SHA(256/512), but it is much slower.18MD5 is format 500 in hashcat.19DES is format 1500 in hashcat.20BSDI is format 12400 in hashcat.21BLOWFISH is format 3200 in hashcat.22SHA256 is format 7400 in hashcat.23SHA512 is format 1800 in hashcat.24},25'Author' => [26'theLightCosine',27'hdm',28'h00die' # hashcat integration29],30'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)31'Actions' => [32['john', { 'Description' => 'Use John the Ripper' }],33['hashcat', { 'Description' => 'Use Hashcat' }],34],35'DefaultAction' => 'john',36'Notes' => {37'Stability' => [CRASH_SAFE],38'SideEffects' => [],39'Reliability' => []40}41)4243register_options(44[45OptBool.new('MD5', [false, 'Include MD5 hashes', true]),46OptBool.new('DES', [false, 'Indlude DES hashes', true]),47OptBool.new('BSDI', [false, 'Include BSDI hashes', true]),48OptBool.new('BLOWFISH', [false, 'Include BLOWFISH hashes (Very Slow)', false]),49OptBool.new('SHA256', [false, 'Include SHA256 hashes (Very Slow)', false]),50OptBool.new('SHA512', [false, 'Include SHA512 hashes (Very Slow)', false]),51OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),52OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])53]54)55end5657def show_command(cracker_instance)58return unless datastore['ShowCommand']5960if action.name == 'john'61cmd = cracker_instance.john_crack_command62elsif action.name == 'hashcat'63cmd = cracker_instance.hashcat_crack_command64end65print_status(" Cracking Command: #{cmd.join(' ')}")66end6768def check_results(passwords, results, hash_type, method)69passwords.each do |password_line|70password_line.chomp!71next if password_line.blank?7273fields = password_line.split(':')74cred = { 'hash_type' => hash_type, 'method' => method }7576if action.name == 'john'77next unless fields.count >= 3 # If we don't have an expected minimum number of fields, this is probably not a hash line7879cred['username'] = fields.shift80cred['core_id'] = fields.pop814.times { fields.pop } # Get rid of extra :82cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it83elsif action.name == 'hashcat'84next unless fields.count >= 2 # If we don't have an expected minimum number of fields, this is probably not a hash line8586cred['core_id'] = fields.shift87cred['hash'] = fields.shift88cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it89next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines9091# we don't have the username since we overloaded it with the core_id (since its a better fit for us)92# so we can now just go grab the username from the DB93cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username94end95results = process_cracker_results(results, cred)96end9798results99end100101def run102tbl = tbl = cracker_results_table103104# array of hashes in jtr_format in the db, converted to an OR combined regex105hash_types_to_crack = []106hash_types_to_crack << 'md5crypt' if datastore['MD5']107hash_types_to_crack << 'descrypt' if datastore['DES']108hash_types_to_crack << 'bsdicrypt' if datastore['BSDI']109hash_types_to_crack << 'bcrypt' if datastore['BLOWFISH']110hash_types_to_crack << 'sha256crypt' if datastore['SHA256']111hash_types_to_crack << 'sha512crypt' if datastore['SHA512']112113jobs_to_do = []114115# build our job list116hash_types_to_crack.each do |hash_type|117job = hash_job(hash_type, action.name)118if job.nil?119print_status("No #{hash_type} found to crack")120else121jobs_to_do << job122end123end124125# bail early of no jobs to do126if jobs_to_do.empty?127print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")128return129end130131# array of arrays for cracked passwords.132# Inner array format: db_id, hash_type, username, password, method_of_crack133results = []134135cracker = new_password_cracker(action.name)136137# generate our wordlist and close the file handle.138wordlist = wordlist_file139unless wordlist140print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')141return142end143144wordlist.close145print_status "Wordlist file written out to #{wordlist.path}"146147cleanup_files = [wordlist.path]148jobs_to_do.each do |job|149format = job['type']150hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")151hash_file.puts job['formatted_hashlist']152hash_file.close153cracker.hash_path = hash_file.path154cleanup_files << hash_file.path155156# dupe our original cracker so we can safely change options between each run157cracker_instance = cracker.dup158cracker_instance.format = format159160if action.name == 'john'161cracker_instance.fork = datastore['FORK']162end163164# first check if anything has already been cracked so we don't report it incorrectly165print_status "Checking #{format} hashes already cracked..."166results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')167vprint_good(append_results(tbl, results)) unless results.empty?168job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list169next if job['cred_ids_left_to_crack'].empty?170171if action.name == 'john'172print_status "Cracking #{format} hashes in single mode..."173cracker_instance.mode_single(wordlist.path)174show_command cracker_instance175cracker_instance.crack do |line|176vprint_status line.chomp177end178results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')179vprint_good(append_results(tbl, results)) unless results.empty?180job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list181next if job['cred_ids_left_to_crack'].empty?182183print_status "Cracking #{format} hashes in normal mode..."184cracker_instance.mode_normal185show_command cracker_instance186cracker_instance.crack do |line|187vprint_status line.chomp188end189results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')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?206end207208next unless datastore['WORDLIST']209210print_status "Cracking #{format} hashes in wordlist mode..."211cracker_instance.mode_wordlist(wordlist.path)212# Turn on KoreLogic rules if the user asked for it213if action.name == 'john' && datastore['KORELOGIC']214cracker_instance.rules = 'KoreLogicRules'215print_status 'Applying KoreLogic ruleset...'216end217show_command cracker_instance218cracker_instance.crack do |line|219vprint_status line.chomp220end221222results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')223vprint_good(append_results(tbl, results)) unless results.empty?224job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list225next if job['cred_ids_left_to_crack'].empty?226end227228# give a final print of results229print_good(append_results(tbl, results))230231if datastore['DeleteTempFiles']232cleanup_files.each do |f|233File.delete(f)234end235end236end237end238239240