Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/auxiliary/analyze/crack_databases.rb
Views: 11780
##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)3940register_options(41[42OptBool.new('MSSQL', [false, 'Include MSSQL hashes', true]),43OptBool.new('MYSQL', [false, 'Include MySQL hashes', true]),44OptBool.new('ORACLE', [false, 'Include Oracle hashes', true]),45OptBool.new('POSTGRES', [false, 'Include Postgres hashes', true]),46OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),47OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])48]49)50end5152def show_command(cracker_instance)53return unless datastore['ShowCommand']5455if action.name == 'john'56cmd = cracker_instance.john_crack_command57elsif action.name == 'hashcat'58cmd = cracker_instance.hashcat_crack_command59end60print_status(" Cracking Command: #{cmd.join(' ')}")61end6263def run64def check_results(passwords, results, hash_type, method)65passwords.each do |password_line|66password_line.chomp!67next if password_line.blank?6869fields = password_line.split(':')70cred = { 'hash_type' => hash_type, 'method' => method }7172if action.name == 'john'73next unless fields.count >= 37475cred['username'] = fields.shift76cred['core_id'] = fields.pop77cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it78elsif action.name == 'hashcat'79next unless fields.count >= 28081case hash_type82when 'dynamic_1034'83# for postgres we get 4 fields, id:hash:un:pass.84cred['core_id'] = fields.shift85cred['hash'] = fields.shift86cred['username'] = fields.shift87cred['password'] = fields.join(':')88when 'oracle11', 'raw-sha1,oracle'89cred['core_id'] = fields.shift90cred['hash'] = "#{fields.shift}#{fields.shift}" # we pull the first two fields, hash and salt91cred['password'] = fields.join(':')92else93cred['core_id'] = fields.shift94cred['hash'] = fields.shift95cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it96end9798next 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)105end106results107end108109tbl = tbl = cracker_results_table110111# array of hashes in jtr_format in the db, converted to an OR combined regex112hash_types_to_crack = []113114if datastore['MSSQL']115hash_types_to_crack << 'mssql'116hash_types_to_crack << 'mssql05'117hash_types_to_crack << 'mssql12'118end119if datastore['MYSQL']120hash_types_to_crack << 'mysql'121hash_types_to_crack << 'mysql-sha1'122end123if datastore['ORACLE']124# dynamic_1506 is oracle 11/12's H field, MD5.125126# hashcat requires a format we dont have all the data for127# in the current dumper, so this is disabled in module and lib128if action.name == 'john'129hash_types_to_crack << 'oracle'130hash_types_to_crack << 'dynamic_1506'131end132hash_types_to_crack << 'oracle11'133hash_types_to_crack << 'oracle12c'134end135if datastore['POSTGRES']136hash_types_to_crack << 'dynamic_1034'137end138139jobs_to_do = []140141# build our job list142hash_types_to_crack.each do |hash_type|143job = hash_job(hash_type, action.name)144if job.nil?145print_status("No #{hash_type} found to crack")146else147jobs_to_do << job148end149end150151# bail early of no jobs to do152if jobs_to_do.empty?153print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")154return155end156157# array of arrays for cracked passwords.158# Inner array format: db_id, hash_type, username, password, method_of_crack159results = []160161cracker = new_password_cracker(action.name)162163# generate our wordlist and close the file handle.164wordlist = wordlist_file165unless wordlist166print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')167return168end169170wordlist.close171print_status "Wordlist file written out to #{wordlist.path}"172173cleanup_files = [wordlist.path]174175jobs_to_do.each do |job|176format = job['type']177hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")178hash_file.puts job['formatted_hashlist']179hash_file.close180cracker.hash_path = hash_file.path181cleanup_files << hash_file.path182183# dupe our original cracker so we can safely change options between each run184cracker_instance = cracker.dup185cracker_instance.format = format186187if action.name == 'john'188cracker_instance.fork = datastore['FORK']189end190191# first check if anything has already been cracked so we don't report it incorrectly192print_status "Checking #{format} hashes already cracked..."193results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')194vprint_good(append_results(tbl, results)) unless results.empty?195job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list196next if job['cred_ids_left_to_crack'].empty?197198if action.name == 'john'199print_status "Cracking #{format} hashes in single mode..."200cracker_instance.mode_single(wordlist.path)201show_command cracker_instance202cracker_instance.crack do |line|203vprint_status line.chomp204end205results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')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?209210print_status "Cracking #{format} hashes in normal mode..."211cracker_instance.mode_normal212show_command cracker_instance213cracker_instance.crack do |line|214vprint_status line.chomp215end216results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')217vprint_good(append_results(tbl, results)) unless results.empty?218job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list219next if job['cred_ids_left_to_crack'].empty?220end221222if datastore['INCREMENTAL']223print_status "Cracking #{format} hashes in incremental mode..."224cracker_instance.mode_incremental225show_command cracker_instance226cracker_instance.crack do |line|227vprint_status line.chomp228end229results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')230vprint_good(append_results(tbl, results)) unless results.empty?231job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list232next if job['cred_ids_left_to_crack'].empty?233end234235if datastore['WORDLIST']236print_status "Cracking #{format} hashes in wordlist mode..."237cracker_instance.mode_wordlist(wordlist.path)238# Turn on KoreLogic rules if the user asked for it239if action.name == 'john' && datastore['KORELOGIC']240cracker_instance.rules = 'KoreLogicRules'241print_status 'Applying KoreLogic ruleset...'242end243show_command cracker_instance244cracker_instance.crack do |line|245vprint_status line.chomp246end247248results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')249vprint_good(append_results(tbl, results)) unless results.empty?250job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list251next if job['cred_ids_left_to_crack'].empty?252end253254# give a final print of results255print_good(append_results(tbl, results))256end257if datastore['DeleteTempFiles']258cleanup_files.each do |f|259File.delete(f)260end261end262end263end264265266