Path: blob/master/modules/auxiliary/analyze/crack_databases.rb
19664 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_mssql_fast'9moved_from 'auxiliary/analyze/jtr_mysql_fast'10moved_from 'auxiliary/analyze/jtr_oracle_fast'11moved_from 'auxiliary/analyze/jtr_postgres_fast'1213def initialize14super(15'Name' => 'Password Cracker: Databases',16'Description' => %(17This module uses John the Ripper or Hashcat to identify weak passwords that have been18acquired from the mssql_hashdump, mysql_hashdump, postgres_hashdump, or oracle_hashdump modules.19Passwords that have been successfully cracked are then saved as proper credentials.20Due to the complexity of some of the hash types, they can be very slow. Setting the21ITERATION_TIMEOUT is highly recommended.22MSSQL is 131, 132, and 1731 in hashcat.23MYSQL is 200, and 300 in hashcat.24ORACLE is 112, and 12300 in hashcat.25POSTGRES is 12 in hashcat.26),27'Author' => [28'theLightCosine',29'hdm',30'h00die' # hashcat integration31],32'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)33'Actions' => [34['john', { 'Description' => 'Use John the Ripper' }],35['hashcat', { 'Description' => 'Use Hashcat' }],36],37'DefaultAction' => 'john',38'Notes' => {39'Stability' => [CRASH_SAFE],40'SideEffects' => [],41'Reliability' => []42}43)4445register_options(46[47OptBool.new('MSSQL', [false, 'Include MSSQL hashes', true]),48OptBool.new('MYSQL', [false, 'Include MySQL hashes', true]),49OptBool.new('ORACLE', [false, 'Include Oracle hashes', true]),50OptBool.new('POSTGRES', [false, 'Include Postgres hashes', true]),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 >= 37879cred['username'] = fields.shift80cred['core_id'] = fields.pop81cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it82elsif action.name == 'hashcat'83next unless fields.count >= 28485cred['core_id'] = fields.shift86case hash_type87when 'dynamic_1034'88# for postgres we get 4 fields, id:hash:un:pass.89cred['hash'] = fields.shift90cred['username'] = fields.shift91when 'oracle11', 'raw-sha1,oracle'92cred['hash'] = "#{fields.shift}#{fields.shift}" # we pull the first two fields, hash and salt93else94cred['hash'] = fields.shift95end96cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it9798next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines99100# we don't have the username since we overloaded it with the core_id (since its a better fit for us)101# so we can now just go grab the username from the DB102cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username103end104results = process_cracker_results(results, cred)105end106107results108end109110def run111tbl = tbl = cracker_results_table112113# array of hashes in jtr_format in the db, converted to an OR combined regex114hash_types_to_crack = []115116if datastore['MSSQL']117hash_types_to_crack << 'mssql'118hash_types_to_crack << 'mssql05'119hash_types_to_crack << 'mssql12'120end121if datastore['MYSQL']122hash_types_to_crack << 'mysql'123hash_types_to_crack << 'mysql-sha1'124end125if datastore['ORACLE']126# dynamic_1506 is oracle 11/12's H field, MD5.127128# hashcat requires a format we dont have all the data for129# in the current dumper, so this is disabled in module and lib130if action.name == 'john'131hash_types_to_crack << 'oracle'132hash_types_to_crack << 'dynamic_1506'133end134hash_types_to_crack << 'oracle11'135hash_types_to_crack << 'oracle12c'136end137if datastore['POSTGRES']138hash_types_to_crack << 'dynamic_1034'139end140141jobs_to_do = []142143# build our job list144hash_types_to_crack.each do |hash_type|145job = hash_job(hash_type, action.name)146if job.nil?147print_status("No #{hash_type} found to crack")148else149jobs_to_do << job150end151end152153# bail early of no jobs to do154if jobs_to_do.empty?155print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")156return157end158159# array of arrays for cracked passwords.160# Inner array format: db_id, hash_type, username, password, method_of_crack161results = []162163cracker = new_password_cracker(action.name)164165# generate our wordlist and close the file handle.166wordlist = wordlist_file167unless wordlist168print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')169return170end171172wordlist.close173print_status "Wordlist file written out to #{wordlist.path}"174175cleanup_files = [wordlist.path]176177jobs_to_do.each do |job|178format = job['type']179hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")180hash_file.puts job['formatted_hashlist']181hash_file.close182cracker.hash_path = hash_file.path183cleanup_files << hash_file.path184185# dupe our original cracker so we can safely change options between each run186cracker_instance = cracker.dup187cracker_instance.format = format188189if action.name == 'john'190cracker_instance.fork = datastore['FORK']191end192193# first check if anything has already been cracked so we don't report it incorrectly194print_status "Checking #{format} hashes already cracked..."195results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')196vprint_good(append_results(tbl, results)) unless results.empty?197job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list198next if job['cred_ids_left_to_crack'].empty?199200if action.name == 'john'201print_status "Cracking #{format} hashes in single mode..."202cracker_instance.mode_single(wordlist.path)203show_command cracker_instance204cracker_instance.crack do |line|205vprint_status line.chomp206end207results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')208vprint_good(append_results(tbl, results)) unless results.empty?209job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list210next if job['cred_ids_left_to_crack'].empty?211212print_status "Cracking #{format} hashes in normal mode..."213cracker_instance.mode_normal214show_command cracker_instance215cracker_instance.crack do |line|216vprint_status line.chomp217end218results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')219vprint_good(append_results(tbl, results)) unless results.empty?220job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list221next if job['cred_ids_left_to_crack'].empty?222end223224if datastore['INCREMENTAL']225print_status "Cracking #{format} hashes in incremental mode..."226cracker_instance.mode_incremental227show_command cracker_instance228cracker_instance.crack do |line|229vprint_status line.chomp230end231results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')232vprint_good(append_results(tbl, results)) unless results.empty?233job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list234next if job['cred_ids_left_to_crack'].empty?235end236237if datastore['WORDLIST']238print_status "Cracking #{format} hashes in wordlist mode..."239cracker_instance.mode_wordlist(wordlist.path)240# Turn on KoreLogic rules if the user asked for it241if action.name == 'john' && datastore['KORELOGIC']242cracker_instance.rules = 'KoreLogicRules'243print_status 'Applying KoreLogic ruleset...'244end245show_command cracker_instance246cracker_instance.crack do |line|247vprint_status line.chomp248end249250results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')251vprint_good(append_results(tbl, results)) unless results.empty?252job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list253next if job['cred_ids_left_to_crack'].empty?254end255256# give a final print of results257print_good(append_results(tbl, results))258end259if datastore['DeleteTempFiles']260cleanup_files.each do |f|261File.delete(f)262end263end264end265end266267268