Module: Msf::Auxiliary::PasswordCracker

Includes:
Report
Defined in:
lib/msf/core/auxiliary/password_cracker.rb

Overview

This module provides methods for working with a Password Cracker

Instance Method Summary collapse

Methods included from Report

#active_db?, #create_cracked_credential, #create_credential, #create_credential_and_login, #create_credential_login, #db, #db_warning_given?, #get_client, #get_host, #inside_workspace_boundary?, #invalidate_login, #mytask, #myworkspace, #myworkspace_id, #report_auth_info, #report_client, #report_exploit, #report_host, #report_loot, #report_note, #report_service, #report_vuln, #report_web_form, #report_web_page, #report_web_site, #report_web_vuln, #store_cred, #store_local, #store_loot

Methods included from Metasploit::Framework::Require

optionally, optionally_active_record_railtie, optionally_include_metasploit_credential_creation, #optionally_include_metasploit_credential_creation, optionally_require_metasploit_db_gem_engines

Instance Method Details

#append_results(tbl, cracked_hashes) ⇒ String

This method appends a list of cracked hashes to the list used to generate the printed table

Parameters:

  • tbl (Array)

    Array of all results that have been cracked

  • cracked_hashes (Array)

    Array of results to add to the table

Returns:

  • (String)

    the table in string format for printing



207
208
209
210
211
212
213
214
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 207

def append_results(tbl, cracked_hashes)
  cracked_hashes.each do |row|
    next if tbl.rows.include? row

    tbl << row
  end
  tbl.to_s
end

#cracker_results_tableRex::Text::Table

This method returns a cracker results table

Returns:

  • (Rex::Text::Table)

    table for printing results



219
220
221
222
223
224
225
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 219

def cracker_results_table
  Rex::Text::Table.new(
    'Header' => 'Cracked Hashes',
    'Indent' => 1,
    'Columns' => ['DB ID', 'Hash Type', 'Username', 'Cracked Password', 'Method']
  )
end

#hash_job(jtr_type, cracker) ⇒ Hash

This method creates a job for the password cracker to do. A job is categorized by the hash type and will include the hash type (type), formatted_hashlist (hashes in the cracker's format), creds (db objects for each hash), and cred_ids_left_to_crack (array of db ids that aren't cracked yet)

Parameters:

  • jtr_type (String)

    hash type we're cracking such as md5, sha1

  • cracker (String)

    the password cracker to use such as 'john' or 'hashcat'

Returns:

  • (Hash)

    of the data needed to crack as described above



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 144

def hash_job(jtr_type, cracker)
  # create the base data
  job = { 'type' => jtr_type, 'formatted_hashlist' => [], 'creds' => [], 'cred_ids_left_to_crack' => [] }
  job['db_formats'] = Metasploit::Framework::PasswordCracker::JtR::Formatter.jtr_to_db(jtr_type)
  if jtr_type == 'dynamic_1034' # postgres
    creds = framework.db.creds(workspace: myworkspace, type: 'Metasploit::Credential::PostgresMD5')
  elsif ['lm', 'nt'].include? jtr_type
    creds = framework.db.creds(workspace: myworkspace, type: 'Metasploit::Credential::NTLMHash')
  else
    creds = framework.db.creds(workspace: myworkspace, type: 'Metasploit::Credential::NonreplayableHash')
  end
  creds.each do |core|
    jtr_format = core.private.jtr_format

    # Unfortunately NTLMHash always set JtR Format to 'nt,lm' so we have to do a special case here
    # to figure out which it is
    if jtr_format == 'nt,lm'
      jtr_format = core.private.data.start_with?('aad3b435b51404eeaad3b435b51404ee') ? 'nt' : 'lm'
    end

    next unless job['db_formats'].include? jtr_format
    # only add hashes which havne't been cracked
    next if password_cracked?(core.private.data)

    job['creds'] << core
    job['cred_ids_left_to_crack'] << core.id
    if cracker == 'john'
      job['formatted_hashlist'] << Metasploit::Framework::PasswordCracker::JtR::Formatter.hash_to_jtr(core)
    elsif cracker == 'hashcat'
      job['formatted_hashlist'] << Metasploit::Framework::PasswordCracker::Hashcat::Formatter.hash_to_hashcat(core)
    end
  end

  if job['creds'].length > 0
    return job
  end

  nil
end

#initialize(info = {}) ⇒ Object

Initializes an instance of an auxiliary module that calls out to John the Ripper (jtr)



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 23

def initialize(info = {})
  super

  register_options(
    [
      OptPath.new('CONFIG', [false, 'The path to a John config file to use instead of the default']),
      OptPath.new('CUSTOM_WORDLIST', [false, 'The path to an optional custom wordlist']),
      OptInt.new('ITERATION_TIMEOUT', [false, 'The max-run-time for each iteration of cracking']),
      OptPath.new('CRACKER_PATH', [false, 'The absolute path to the cracker executable']),
      OptInt.new('FORK', [false, 'Forks for John the Ripper to use', 1]),
      OptBool.new('KORELOGIC', [false, 'Apply the KoreLogic rules to John the Ripper Wordlist Mode(slower)', false]),
      OptBool.new('MUTATE', [false, 'Apply common mutations to the Wordlist (SLOW)', false]),
      OptPath.new('POT', [false, 'The path to a John POT file to use instead of the default']),
      OptBool.new('USE_CREDS', [false, 'Use existing credential data saved in the database', true]),
      OptBool.new('USE_DB_INFO', [false, 'Use looted database schema info to seed the wordlist', true]),
      OptBool.new('USE_DEFAULT_WORDLIST', [false, 'Use the default metasploit wordlist', true]),
      OptBool.new('USE_HOSTNAMES', [false, 'Seed the wordlist with hostnames from the workspace', true]),
      OptBool.new('USE_ROOT_WORDS', [false, 'Use the Common Root Words Wordlist', true])
    ], Msf::Auxiliary::PasswordCracker
  )

  register_advanced_options(
    [
      OptBool.new('DeleteTempFiles', [false, 'Delete temporary wordlist and hash files', true]),
      OptBool.new('OptimizeKernel', [false, 'Utilize Optimized Kernels in Hashcat', true]),
      OptBool.new('ShowCommand', [false, 'Print the cracker command being used', true]),
    ], Msf::Auxiliary::PasswordCracker
  )
end

#john_lm_upper_to_ntlm(pwd, hash) ⇒ String?

Parameters:

  • pwd (String)

    Password recovered from cracking an LM hash

  • hash (String)

    NTLM hash for this password

Returns:

  • (String)

    `pwd` converted to the correct case to match the given NTLM hash

  • (nil)

    if no case matches the NT hash. This can happen when `pwd` came from a john run that only cracked half of the LM hash



59
60
61
62
63
64
65
66
67
68
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 59

def john_lm_upper_to_ntlm(pwd, hash)
  pwd = pwd.upcase
  hash = hash.upcase
  Rex::Text.permute_case(pwd).each do |str|
    if hash == Rex::Proto::NTLM::Crypt.ntlm_hash(str).unpack('H*')[0].upcase
      return str
    end
  end
  nil
end

#new_password_cracker(cracking_application) ⇒ nilClass, Metasploit::Framework::PasswordCracker::Cracker

This method creates a new Metasploit::Framework::PasswordCracker::Cracker and populates some of the attributes based on the module datastore options.

Returns:



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 75

def new_password_cracker(cracking_application)
  fail_with(Msf::Module::Failure::BadConfig, 'Password cracking is not available without an active database connection.') unless framework.db.active
  cracker = Metasploit::Framework::PasswordCracker::Cracker.new(
    config: datastore['CONFIG'],
    cracker_path: datastore['CRACKER_PATH'],
    max_runtime: datastore['ITERATION_TIMEOUT'],
    pot: datastore['POT'],
    optimize: datastore['OptimizeKernel'],
    wordlist: datastore['CUSTOM_WORDLIST']
  )
  cracker.cracker = cracking_application
  begin
    cracker.binary_path
  rescue Metasploit::Framework::PasswordCracker::PasswordCrackerNotFoundError => e
    fail_with(Msf::Module::Failure::BadConfig, e.message)
  end
  # throw this to a local variable since it causes a shell out to pull the version
  cracker_version = cracker.cracker_version
  if cracker.cracker == 'john' && (cracker_version.nil? || !cracker_version.include?('jumbo'))
    fail_with(Msf::Module::Failure::BadConfig, 'John the Ripper JUMBO patch version required.  See https://github.com/magnumripper/JohnTheRipper')
  end
  print_good("#{cracker.cracker} Version Detected: #{cracker_version}")
  cracker
end

#password_cracked?(hash) ⇒ Boolean

This method determines if a given password hash already been cracked in the database

Parameters:

  • hash (String)

    password hash to check against the database

Returns:

  • (Boolean)

    if the password has been cracked in the db



126
127
128
129
130
131
132
133
134
135
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 126

def password_cracked?(hash)
  framework.db.creds({ pass: hash }).each do |test_cred|
    test_cred.public.cores.each do |core|
      if core.origin_type == 'Metasploit::Credential::Origin::CrackedPassword'
        return true
      end
    end
  end
  false
end

#process_cracker_results(results, cred) ⇒ Array

This method takes a results table, and a newly cracked cred, and adds the cred to the table if it isn't there already. It also creates the cracked credential in the database.

Parameters:

  • results (Hash)

    Hash of the newly cracked cred information, should have hash_type, method, username core_id, and password fields.

Returns:

  • (Array)

    Array of results for printing in a table



190
191
192
193
194
195
196
197
198
199
200
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 190

def process_cracker_results(results, cred)
  return results if cred['core_id'].nil? # make sure we have good data

  # make sure we dont add the same one again
  if results.select { |r| r.first == cred['core_id'] }.empty?
    results << [cred['core_id'], cred['hash_type'], cred['username'], cred['password'], cred['method']]
  end

  create_cracked_credential(username: cred['username'], password: cred['password'], core_id: cred['core_id'])
  results
end

#wordlist_file(max_len = 0) ⇒ nilClass, Rex::Quickfile

This method instantiates a Metasploit::Framework::JtR::Wordlist, writes the data out to a file and returns the Rex::Quickfile object.

Parameters:

  • max_len (Integer) (defaults to: 0)

    max length of a word in the wordlist, 0 default for ignored value

Returns:

  • (nilClass)

    if there is no active framework db connection

  • (Rex::Quickfile)

    if it successfully wrote the wordlist to a file



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/msf/core/auxiliary/password_cracker.rb', line 106

def wordlist_file(max_len = 0)
  return nil unless framework.db.active

  wordlist = Metasploit::Framework::PasswordCracker::Wordlist.new(
    custom_wordlist: datastore['CUSTOM_WORDLIST'],
    mutate: datastore['MUTATE'],
    use_creds: datastore['USE_CREDS'],
    use_db_info: datastore['USE_DB_INFO'],
    use_default_wordlist: datastore['USE_DEFAULT_WORDLIST'],
    use_hostnames: datastore['USE_HOSTNAMES'],
    use_common_root: datastore['USE_ROOT_WORDS'],
    workspace: myworkspace
  )
  wordlist.to_file(max_len)
end