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_windows.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_windows_fast'910def initialize11super(12'Name' => 'Password Cracker: Windows',13'Description' => %(14This module uses John the Ripper or Hashcat to identify weak passwords that have been15acquired from Windows systems.16LANMAN is format 3000 in hashcat.17NTLM is format 1000 in hashcat.18MSCASH is format 1100 in hashcat.19MSCASH2 is format 2100 in hashcat.20NetNTLM is format 5500 in hashcat.21NetNTLMv2 is format 5600 in hashcat.22),23'Author' => [24'theLightCosine',25'hdm',26'h00die' # hashcat integration27],28'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)29'Actions' => [30['john', { 'Description' => 'Use John the Ripper' }],31['hashcat', { 'Description' => 'Use Hashcat' }],32],33'DefaultAction' => 'john',34)3536register_options(37[38OptBool.new('NTLM', [false, 'Crack NTLM hashes', true]),39OptBool.new('LANMAN', [false, 'Crack LANMAN hashes', true]),40OptBool.new('MSCASH', [false, 'Crack M$ CASH hashes (1 and 2)', true]),41OptBool.new('NETNTLM', [false, 'Crack NetNTLM', true]),42OptBool.new('NETNTLMV2', [false, 'Crack NetNTLMv2', true]),43OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),44OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true]),45OptBool.new('NORMAL', [false, 'Run in normal mode (John the Ripper only)', true])46]47)48end4950def half_lm_regex51# ^\?{7} is ??????? which is JTR format, so password would be ???????D52# ^[notfound] is hashcat format, so password would be [notfound]D53/^[?{7}|\[notfound\]]/54end5556def show_command(cracker_instance)57return unless datastore['ShowCommand']5859if action.name == 'john'60cmd = cracker_instance.john_crack_command61elsif action.name == 'hashcat'62cmd = cracker_instance.hashcat_crack_command63end64print_status(" Cracking Command: #{cmd.join(' ')}")65end6667def run68# we have to overload the process_cracker_results from password_cracker.rb since LANMAN69# is a special case where we may need to do some combining70def process_cracker_results(results, cred)71return results if cred['core_id'].nil? # make sure we have good data7273# make sure we dont add the same one again74if results.select { |r| r.first == cred['core_id'] }.empty?75results << [cred['core_id'], cred['hash_type'], cred['username'], cred['password'], cred['method']]76end7778# however, a special case for LANMAN where it may come back as ???????D (jtr) or [notfound]D (hashcat)79# we want to overwrite the one that was there *if* we have something better.80results.map! do |r|81if r.first == cred['core_id'] &&82r[3] =~ half_lm_regex83[cred['core_id'], cred['hash_type'], cred['username'], cred['password'], cred['method']]84else85r86end87end8889create_cracked_credential(username: cred['username'], password: cred['password'], core_id: cred['core_id'])90results91end9293def check_results(passwords, results, hash_type, method)94passwords.each do |password_line|95password_line.chomp!96next if password_line.blank?9798fields = password_line.split(':')99cred = { 'hash_type' => hash_type, 'method' => method }100if action.name == 'john'101# If we don't have an expected minimum number of fields, this is probably not a hash line102next unless fields.count > 2103104cred['username'] = fields.shift105cred['core_id'] = fields.pop106case hash_type107when 'mscash', 'mscash2', 'netntlm', 'netntlmv2'108cred['password'] = fields.shift109when 'lm', 'nt'110# If we don't have an expected minimum number of fields, this is probably not a NTLM hash111next unless fields.count >= 61121132.times { fields.pop } # Get rid of extra :114nt_hash = fields.pop115lm_hash = fields.pop116id = fields.pop117password = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it118if hash_type == 'lm' && password.blank?119if nt_hash == Metasploit::Credential::NTLMHash::BLANK_NT_HASH120password = ''121else122next123end124end125126# password can be nil if the hash is broken (i.e., the NT and127# LM sides don't actually match) or if john was only able to128# crack one half of the LM hash. In the latter case, we'll129# have a line like:130# username:???????WORD:...:...:::131cred['password'] = john_lm_upper_to_ntlm(password, nt_hash)132end133next if cred['password'].nil?134elsif action.name == 'hashcat'135next unless fields.count >= 2136137cred['core_id'] = fields.shift138139if ['netntlm', 'netntlmv2'].include? hash_type140# we could grab the username here, but no need since we grab it later based on core_id, which is safer1416.times { fields.shift } # Get rid of a bunch of extra fields142else143cred['hash'] = fields.shift144end145146fields.pop if hash_type == 'mscash' # Get rid of username147148cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it149next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines150151# we don't have the username since we overloaded it with the core_id (since its a better fit for us)152# so we can now just go grab the username from the DB153cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username154end155results = process_cracker_results(results, cred)156end157results158end159160tbl = cracker_results_table161162# array of hashes in jtr_format in the db, converted to an OR combined regex163hash_types_to_crack = []164hash_types_to_crack << 'lm' if datastore['LANMAN']165hash_types_to_crack << 'nt' if datastore['NTLM']166hash_types_to_crack << 'mscash' if datastore['MSCASH']167hash_types_to_crack << 'mscash2' if datastore['MSCASH']168hash_types_to_crack << 'netntlm' if datastore['NETNTLM']169hash_types_to_crack << 'netntlmv2' if datastore['NETNTLMV2']170171jobs_to_do = []172173# build our job list174hash_types_to_crack.each do |hash_type|175job = hash_job(hash_type, action.name)176if job.nil?177print_status("No #{hash_type} found to crack")178else179jobs_to_do << job180end181end182183# bail early of no jobs to do184if jobs_to_do.empty?185print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")186return187end188189# array of arrays for cracked passwords.190# Inner array format: db_id, hash_type, username, password, method_of_crack191results = []192193cracker = new_password_cracker(action.name)194195# generate our wordlist and close the file handle.196wordlist = wordlist_file197unless wordlist198print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')199return200end201202wordlist.close203print_status "Wordlist file written out to #{wordlist.path}"204205cleanup_files = [wordlist.path]206207jobs_to_do.each do |job|208format = job['type']209hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")210hash_file.puts job['formatted_hashlist']211hash_file.close212cracker.hash_path = hash_file.path213cleanup_files << hash_file.path214# dupe our original cracker so we can safely change options between each run215cracker_instance = cracker.dup216cracker_instance.format = format217if action.name == 'john'218cracker_instance.fork = datastore['FORK']219end220221# first check if anything has already been cracked so we don't report it incorrectly222print_status "Checking #{format} hashes already cracked..."223results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')224vprint_good(append_results(tbl, results)) unless results.empty?225job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list226next if job['cred_ids_left_to_crack'].empty?227228if action.name == 'john'229print_status "Cracking #{format} hashes in single mode..."230cracker_instance.mode_single(wordlist.path)231show_command cracker_instance232cracker_instance.crack do |line|233vprint_status line.chomp234end235results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')236vprint_good(append_results(tbl, results)) unless results.empty?237job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list238next if job['cred_ids_left_to_crack'].empty?239240if datastore['NORMAL']241print_status "Cracking #{format} hashes in normal mode..."242cracker_instance.mode_normal243show_command cracker_instance244cracker_instance.crack do |line|245vprint_status line.chomp246end247results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')248vprint_good(append_results(tbl, results)) unless results.empty?249job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list250next if job['cred_ids_left_to_crack'].empty?251end252end253254if datastore['INCREMENTAL']255print_status "Cracking #{format} hashes in incremental mode..."256cracker_instance.mode_incremental257show_command cracker_instance258cracker_instance.crack do |line|259vprint_status line.chomp260end261results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')262vprint_good(append_results(tbl, results)) unless results.empty?263job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list264next if job['cred_ids_left_to_crack'].empty?265end266267if datastore['WORDLIST']268print_status "Cracking #{format} hashes in wordlist mode..."269cracker_instance.mode_wordlist(wordlist.path)270# Turn on KoreLogic rules if the user asked for it271if action.name == 'john' && datastore['KORELOGIC']272cracker_instance.rules = 'KoreLogicRules'273print_status 'Applying KoreLogic ruleset...'274end275show_command cracker_instance276cracker_instance.crack do |line|277vprint_status line.chomp278end279280results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')281282vprint_good(append_results(tbl, results)) unless results.empty?283job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list284next if job['cred_ids_left_to_crack'].empty?285end286287# give a final print of results288print_good(append_results(tbl, results))289end290if datastore['DeleteTempFiles']291cleanup_files.each do |f|292File.delete(f)293end294end295end296end297298299