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_webapps.rb
Views: 11779
##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: Webapps',11'Description' => %(12This module uses John the Ripper or Hashcat to identify weak passwords that have been13acquired from various web applications.14Atlassian uses PBKDF2-HMAC-SHA1 which is 12001 in hashcat.15PHPass uses phpass which is 400 in hashcat.16Mediawiki is MD5 based and is 3711 in hashcat.17Apache Superset, some Flask and Werkzeug apps is pbkdf2-sha256 and is 10900 in hashcat18),19'Author' => [20'h00die'21],22'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)23'Actions' => [24['john', { 'Description' => 'Use John the Ripper' }],25['hashcat', { 'Description' => 'Use Hashcat' }],26],27'DefaultAction' => 'john',28)2930register_options(31[32OptBool.new('ATLASSIAN',[false, 'Include Atlassian hashes', true]),33OptBool.new('MEDIAWIKI',[false, 'Include MediaWiki hashes', true]),34OptBool.new('PHPASS',[false, 'Include Wordpress/PHPass, Joomla, phpBB3 hashes', true]),35OptBool.new('PBKDF2',[false, 'Apache Superset, some Flask and Werkzeug apps hashes', true]),36OptBool.new('INCREMENTAL',[false, 'Run in incremental mode', true]),37OptBool.new('WORDLIST',[false, 'Run in wordlist mode', true])38]39)40end4142def show_command(cracker_instance)43return unless datastore['ShowCommand']4445if action.name == 'john'46cmd = cracker_instance.john_crack_command47elsif action.name == 'hashcat'48cmd = cracker_instance.hashcat_crack_command49end50print_status(" Cracking Command: #{cmd.join(' ')}")51end5253def run54def check_results(passwords, results, hash_type, method)55passwords.each do |password_line|56password_line.chomp!57next if password_line.blank?5859fields = password_line.split(':')60cred = { 'hash_type' => hash_type, 'method' => method }61# If we don't have an expected minimum number of fields, this is probably not a hash line62if action.name == 'john'63next unless fields.count >= 36465cred['username'] = fields.shift66cred['core_id'] = fields.pop67cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it68elsif action.name == 'hashcat'69next unless fields.count >= 27071cred['core_id'] = fields.shift72cred['hash'] = fields.shift73cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it74next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines7576# we don't have the username since we overloaded it with the core_id (since its a better fit for us)77# so we can now just go grab the username from the DB78cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username79end80results = process_cracker_results(results, cred)81end82results83end8485tbl = tbl = cracker_results_table8687hash_types_to_crack = []88hash_types_to_crack << 'PBKDF2-HMAC-SHA1' if datastore['ATLASSIAN']89hash_types_to_crack << 'phpass' if datastore['PHPASS']90hash_types_to_crack << 'mediawiki' if datastore['MEDIAWIKI']91hash_types_to_crack << 'pbkdf2-sha256' if datastore['PBKDF2']92jobs_to_do = []9394# build our job list95hash_types_to_crack.each do |hash_type|96job = hash_job(hash_type, action.name)97if job.nil?98print_status("No #{hash_type} found to crack")99else100jobs_to_do << job101end102end103104# bail early of no jobs to do105if jobs_to_do.empty?106print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")107return108end109110# array of arrays for cracked passwords.111# Inner array format: db_id, hash_type, username, password, method_of_crack112results = []113114cracker = new_password_cracker(action.name)115116# generate our wordlist and close the file handle.117wordlist = wordlist_file118unless wordlist119print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')120return121end122123wordlist.close124print_status "Wordlist file written out to #{wordlist.path}"125126cleanup_files = [wordlist.path]127128jobs_to_do.each do |job|129format = job['type']130hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")131hash_file.puts job['formatted_hashlist']132hash_file.close133cracker.hash_path = hash_file.path134cleanup_files << hash_file.path135# dupe our original cracker so we can safely change options between each run136cracker_instance = cracker.dup137cracker_instance.format = format138if action.name == 'john'139cracker_instance.fork = datastore['FORK']140end141142# first check if anything has already been cracked so we don't report it incorrectly143print_status "Checking #{format} hashes already cracked..."144results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')145vprint_good(append_results(tbl, results)) unless results.empty?146job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list147next if job['cred_ids_left_to_crack'].empty?148149if action.name == 'john'150print_status "Cracking #{format} hashes in single mode..."151cracker_instance.mode_single(wordlist.path)152show_command cracker_instance153cracker_instance.crack do |line|154vprint_status line.chomp155end156results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')157vprint_good(append_results(tbl, results)) unless results.empty?158job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list159next if job['cred_ids_left_to_crack'].empty?160161print_status "Cracking #{format} hashes in normal mode..."162cracker_instance.mode_normal163show_command cracker_instance164cracker_instance.crack do |line|165vprint_status line.chomp166end167results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')168vprint_good(append_results(tbl, results)) unless results.empty?169job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list170next if job['cred_ids_left_to_crack'].empty?171end172173if datastore['INCREMENTAL']174print_status "Cracking #{format} hashes in incremental mode..."175cracker_instance.mode_incremental176show_command cracker_instance177cracker_instance.crack do |line|178vprint_status line.chomp179end180results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')181vprint_good(append_results(tbl, results)) unless results.empty?182job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list183next if job['cred_ids_left_to_crack'].empty?184end185186if datastore['WORDLIST']187print_status "Cracking #{format} hashes in wordlist mode..."188cracker_instance.mode_wordlist(wordlist.path)189# Turn on KoreLogic rules if the user asked for it190if action.name == 'john' && datastore['KORELOGIC']191cracker_instance.rules = 'KoreLogicRules'192print_status 'Applying KoreLogic ruleset...'193end194show_command cracker_instance195cracker_instance.crack do |line|196vprint_status line.chomp197end198199results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')200vprint_good(append_results(tbl, results)) unless results.empty?201job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list202next if job['cred_ids_left_to_crack'].empty?203end204205# give a final print of results206print_good(append_results(tbl, results))207end208if datastore['DeleteTempFiles']209cleanup_files.each do |f|210File.delete(f)211end212end213end214end215216217