Files
metasploit-gs/modules/auxiliary/analyze/crack_databases.rb
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

265 lines
10 KiB
Ruby
Raw Normal View History

2019-04-04 20:50:52 -04:00
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::PasswordCracker
2019-10-08 20:31:23 -04:00
include Msf::Exploit::Deprecated
moved_from 'auxiliary/analyze/jtr_mssql_fast'
moved_from 'auxiliary/analyze/jtr_mysql_fast'
moved_from 'auxiliary/analyze/jtr_oracle_fast'
moved_from 'auxiliary/analyze/jtr_postgres_fast'
2019-04-04 20:50:52 -04:00
def initialize
super(
2023-02-05 17:17:30 -05:00
'Name' => 'Password Cracker: Databases',
'Description' => %(
2019-04-04 20:50:52 -04:00
This module uses John the Ripper or Hashcat to identify weak passwords that have been
acquired from the mssql_hashdump, mysql_hashdump, postgres_hashdump, or oracle_hashdump modules.
Passwords that have been successfully cracked are then saved as proper credentials.
Due to the complexity of some of the hash types, they can be very slow. Setting the
ITERATION_TIMEOUT is highly recommended.
2023-02-18 13:09:45 -05:00
MSSQL is 131, 132, and 1731 in hashcat.
MYSQL is 200, and 300 in hashcat.
ORACLE is 112, and 12300 in hashcat.
POSTGRES is 12 in hashcat.
2023-02-05 17:17:30 -05:00
),
'Author' => [
'theLightCosine',
'hdm',
'h00die' # hashcat integration
],
'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)
'Actions' => [
['john', { 'Description' => 'Use John the Ripper' }],
['hashcat', { 'Description' => 'Use Hashcat' }],
],
2019-04-04 20:50:52 -04:00
'DefaultAction' => 'john',
)
register_options(
[
2023-02-05 17:17:30 -05:00
OptBool.new('MSSQL', [false, 'Include MSSQL hashes', true]),
OptBool.new('MYSQL', [false, 'Include MySQL hashes', true]),
OptBool.new('ORACLE', [false, 'Include Oracle hashes', true]),
OptBool.new('POSTGRES', [false, 'Include Postgres hashes', true]),
OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),
OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])
2019-04-04 20:50:52 -04:00
]
)
end
def show_command(cracker_instance)
2019-07-15 19:57:39 -04:00
return unless datastore['ShowCommand']
2023-02-05 17:17:30 -05:00
2019-07-15 19:57:39 -04:00
if action.name == 'john'
cmd = cracker_instance.john_crack_command
elsif action.name == 'hashcat'
cmd = cracker_instance.hashcat_crack_command
2019-04-04 20:50:52 -04:00
end
2019-07-15 19:57:39 -04:00
print_status(" Cracking Command: #{cmd.join(' ')}")
2019-04-04 20:50:52 -04:00
end
def run
2023-02-18 13:09:45 -05:00
def check_results(passwords, results, hash_type, method)
2019-04-04 20:50:52 -04:00
passwords.each do |password_line|
password_line.chomp!
next if password_line.blank?
2023-02-05 17:17:30 -05:00
fields = password_line.split(':')
2023-02-18 13:09:45 -05:00
cred = { 'hash_type' => hash_type, 'method' => method }
2023-02-05 17:17:30 -05:00
2023-02-18 13:09:45 -05:00
if action.name == 'john'
next unless fields.count >= 3
2023-02-05 17:17:30 -05:00
2023-02-18 13:09:45 -05:00
cred['username'] = fields.shift
cred['core_id'] = fields.pop
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
2019-04-04 20:50:52 -04:00
elsif action.name == 'hashcat'
next unless fields.count >= 2
2023-02-05 17:17:30 -05:00
2019-04-04 20:50:52 -04:00
case hash_type
when 'dynamic_1034'
2023-02-18 13:09:45 -05:00
# for postgres we get 4 fields, id:hash:un:pass.
cred['core_id'] = fields.shift
cred['hash'] = fields.shift
cred['username'] = fields.shift
cred['password'] = fields.join(':')
2019-04-04 20:50:52 -04:00
when 'oracle11', 'raw-sha1,oracle'
2023-02-18 13:09:45 -05:00
cred['core_id'] = fields.shift
cred['hash'] = "#{fields.shift}#{fields.shift}" # we pull the first two fields, hash and salt
cred['password'] = fields.join(':')
2019-04-04 20:50:52 -04:00
else
2023-02-18 13:09:45 -05:00
cred['core_id'] = fields.shift
cred['hash'] = fields.shift
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
2019-04-04 20:50:52 -04:00
end
2023-02-18 13:09:45 -05:00
next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines
# we don't have the username since we overloaded it with the core_id (since its a better fit for us)
# so we can now just go grab the username from the DB
cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username
2019-04-04 20:50:52 -04:00
end
2023-02-18 13:09:45 -05:00
results = process_cracker_results(results, cred)
2019-04-04 20:50:52 -04:00
end
results
end
2023-02-18 18:12:12 -05:00
tbl = tbl = cracker_results_table
2019-04-04 20:50:52 -04:00
# array of hashes in jtr_format in the db, converted to an OR combined regex
2023-02-18 13:09:45 -05:00
hash_types_to_crack = []
2019-04-04 20:50:52 -04:00
if datastore['MSSQL']
2023-02-18 13:09:45 -05:00
hash_types_to_crack << 'mssql'
hash_types_to_crack << 'mssql05'
hash_types_to_crack << 'mssql12'
2019-04-04 20:50:52 -04:00
end
if datastore['MYSQL']
2023-02-18 13:09:45 -05:00
hash_types_to_crack << 'mysql'
hash_types_to_crack << 'mysql-sha1'
2019-04-04 20:50:52 -04:00
end
if datastore['ORACLE']
# dynamic_1506 is oracle 11/12's H field, MD5.
# hashcat requires a format we dont have all the data for
# in the current dumper, so this is disabled in module and lib
if action.name == 'john'
2023-02-18 13:09:45 -05:00
hash_types_to_crack << 'oracle'
hash_types_to_crack << 'dynamic_1506'
2019-04-04 20:50:52 -04:00
end
2023-02-18 13:09:45 -05:00
hash_types_to_crack << 'oracle11'
hash_types_to_crack << 'oracle12c'
2019-04-04 20:50:52 -04:00
end
if datastore['POSTGRES']
2023-02-18 13:09:45 -05:00
hash_types_to_crack << 'dynamic_1034'
2019-04-04 20:50:52 -04:00
end
2023-02-18 13:09:45 -05:00
jobs_to_do = []
# build our job list
hash_types_to_crack.each do |hash_type|
job = hash_job(hash_type, action.name)
if job.nil?
print_status("No #{hash_type} found to crack")
else
jobs_to_do << job
end
end
# bail early of no jobs to do
if jobs_to_do.empty?
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
return
end
2019-04-04 20:50:52 -04:00
# array of arrays for cracked passwords.
# Inner array format: db_id, hash_type, username, password, method_of_crack
results = []
cracker = new_password_cracker(action.name)
2019-04-04 20:50:52 -04:00
# generate our wordlist and close the file handle.
wordlist = wordlist_file
unless wordlist
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
return
end
wordlist.close
print_status "Wordlist file written out to #{wordlist.path}"
2023-02-18 13:09:45 -05:00
cleanup_files = [wordlist.path]
jobs_to_do.each do |job|
format = job['type']
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
hash_file.puts job['formatted_hashlist']
hash_file.close
cracker.hash_path = hash_file.path
cleanup_files << hash_file.path
2019-04-04 20:50:52 -04:00
# dupe our original cracker so we can safely change options between each run
cracker_instance = cracker.dup
cracker_instance.format = format
2023-02-18 13:09:45 -05:00
2019-04-04 20:50:52 -04:00
if action.name == 'john'
cracker_instance.fork = datastore['FORK']
end
# first check if anything has already been cracked so we don't report it incorrectly
print_status "Checking #{format} hashes already cracked..."
2023-02-18 13:09:45 -05:00
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
vprint_good(append_results(tbl, results)) unless results.empty?
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
next if job['cred_ids_left_to_crack'].empty?
2019-04-04 20:50:52 -04:00
if action.name == 'john'
2019-05-31 17:13:34 -04:00
print_status "Cracking #{format} hashes in single mode..."
cracker_instance.mode_single(wordlist.path)
show_command cracker_instance
cracker_instance.crack do |line|
vprint_status line.chomp
end
2023-02-18 13:09:45 -05:00
results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')
vprint_good(append_results(tbl, results)) unless results.empty?
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
next if job['cred_ids_left_to_crack'].empty?
2019-05-31 17:13:34 -04:00
2023-02-18 13:09:45 -05:00
print_status "Cracking #{format} hashes in normal mode..."
2019-05-31 17:13:34 -04:00
cracker_instance.mode_normal
2019-04-04 20:50:52 -04:00
show_command cracker_instance
cracker_instance.crack do |line|
vprint_status line.chomp
end
2023-02-18 13:09:45 -05:00
results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')
vprint_good(append_results(tbl, results)) unless results.empty?
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
next if job['cred_ids_left_to_crack'].empty?
2019-04-04 20:50:52 -04:00
end
2019-05-31 17:13:34 -04:00
if datastore['INCREMENTAL']
print_status "Cracking #{format} hashes in incremental mode..."
cracker_instance.mode_incremental
2019-04-04 20:50:52 -04:00
show_command cracker_instance
cracker_instance.crack do |line|
vprint_status line.chomp
end
2023-02-18 13:09:45 -05:00
results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')
vprint_good(append_results(tbl, results)) unless results.empty?
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
next if job['cred_ids_left_to_crack'].empty?
2019-04-04 20:50:52 -04:00
end
2019-05-31 17:13:34 -04:00
if datastore['WORDLIST']
print_status "Cracking #{format} hashes in wordlist mode..."
cracker_instance.mode_wordlist(wordlist.path)
# Turn on KoreLogic rules if the user asked for it
if action.name == 'john' && datastore['KORELOGIC']
cracker_instance.rules = 'KoreLogicRules'
2023-02-05 17:17:30 -05:00
print_status 'Applying KoreLogic ruleset...'
2019-05-31 17:13:34 -04:00
end
show_command cracker_instance
cracker_instance.crack do |line|
vprint_status line.chomp
end
2023-02-18 13:09:45 -05:00
results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')
vprint_good(append_results(tbl, results)) unless results.empty?
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
next if job['cred_ids_left_to_crack'].empty?
2019-04-04 20:50:52 -04:00
end
2023-02-05 17:17:30 -05:00
# give a final print of results
2023-02-18 13:09:45 -05:00
print_good(append_results(tbl, results))
2019-04-04 20:50:52 -04:00
end
if datastore['DeleteTempFiles']
cleanup_files.each do |f|
File.delete(f)
end
end
end
end