Class: Metasploit::Framework::PasswordCracker::Cracker

Inherits:
Object
  • Object
show all
Includes:
ActiveModel::Validations
Defined in:
lib/metasploit/framework/password_crackers/cracker.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes = {}) ⇒ Cracker

Returns a new instance of Cracker.

Parameters:

  • attributes (Hash{Symbol => String,nil}) (defaults to: {})

118
119
120
121
122
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 118

def initialize(attributes={})
  attributes.each do |attribute, value|
    public_send("#{attribute}=", value)
  end
end

Instance Attribute Details

#attackString

Returns The attack mode for hashcat to use (not applicable to John).

Returns:

  • (String)

    The attack mode for hashcat to use (not applicable to John)


13
14
15
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 13

def attack
  @attack
end

#configString

Returns The path to an optional config file for John to use.

Returns:

  • (String)

    The path to an optional config file for John to use


17
18
19
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 17

def config
  @config
end

#crackerString

Returns Which cracker to use. 'john' and 'hashcat' are valid.

Returns:

  • (String)

    Which cracker to use. 'john' and 'hashcat' are valid


21
22
23
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 21

def cracker
  @cracker
end

#cracker_pathString

This attribute allows the user to specify a cracker binary to use. If not supplied, the Cracker will search the PATH for a suitable john or hashcat binary and finally fall back to the pre-compiled john versions shipped with Metasploit.

Returns:

  • (String)

    The file path to an alternative cracker binary to use


29
30
31
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 29

def cracker_path
  @cracker_path
end

#forkString

If the cracker type is john, the amount of forks to specify

Returns:

  • (String)

    The hash format to try.


42
43
44
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 42

def fork
  @fork
end

#formatString

If the cracker type is john, this format will automatically be translated to the hashcat equivalent via jtr_format_to_hashcat_format

Returns:

  • (String)

    The hash format to try.


36
37
38
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 36

def format
  @format
end

#hash_pathString

Returns The path to the file containing the hashes.

Returns:

  • (String)

    The path to the file containing the hashes


46
47
48
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 46

def hash_path
  @hash_path
end

#increment_lengthArray

Returns The incremental min and max to use.

Returns:

  • (Array)

    The incremental min and max to use


54
55
56
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 54

def increment_length
  @increment_length
end

#incrementalString

Returns The incremental mode to use.

Returns:

  • (String)

    The incremental mode to use


50
51
52
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 50

def incremental
  @incremental
end

#maskObject

If the cracker type is hashcat, If set, the mask to use. Should consist of the character sets pre-defined by hashcat, such as ?d ?s ?l etc

@return [String] The mask to use

61
62
63
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 61

def mask
  @mask
end

#max_lengthInteger

Returns An optional maximum length of password to attempt cracking.

Returns:

  • (Integer)

    An optional maximum length of password to attempt cracking


69
70
71
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 69

def max_length
  @max_length
end

#max_runtimeInteger

Returns An optional maximum duration of the cracking attempt in seconds.

Returns:

  • (Integer)

    An optional maximum duration of the cracking attempt in seconds


65
66
67
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 65

def max_runtime
  @max_runtime
end

#optimizeBoolean

Returns If the Optimize flag should be given to Hashcat.

Returns:

  • (Boolean)

    If the Optimize flag should be given to Hashcat


73
74
75
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 73

def optimize
  @optimize
end

#potString

Returns The file path to an alternative John pot file to use.

Returns:

  • (String)

    The file path to an alternative John pot file to use


77
78
79
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 77

def pot
  @pot
end

#rulesString

Returns The wordlist mangling rules to use inside John/Hashcat.

Returns:

  • (String)

    The wordlist mangling rules to use inside John/Hashcat


81
82
83
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 81

def rules
  @rules
end

#wordlistString

Returns The file path to the wordlist to use.

Returns:

  • (String)

    The file path to the wordlist to use


85
86
87
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 85

def wordlist
  @wordlist
end

Instance Method Details

#binary_pathNilClass, String

This method follows a decision tree to determine the path to the cracker binary we should use.

Returns:

  • (NilClass)

    if a binary path could not be found

  • (String)

    the path to the selected JtR binary


274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 274

def binary_path
  # Always prefer a manually entered path
  if cracker_path && ::File.file?(cracker_path)
    return cracker_path
  else
    # Look in the Environment PATH for the john binary
    if cracker == 'john'
      path = Rex::FileUtils.find_full_path("john") ||
        Rex::FileUtils.find_full_path("john.exe")
    elsif cracker == 'hashcat'
      path = Rex::FileUtils.find_full_path("hashcat") ||
        Rex::FileUtils.find_full_path("hashcat.exe")
    else
      raise PasswordCrackerNotFoundError, 'No suitable Cracker was selected, so a binary could not be found on the system'
    end

    if path && ::File.file?(path)
      return path
    end

    raise PasswordCrackerNotFoundError, 'No suitable john/hashcat binary was found on the system'
  end
end

#crack {|String| ... } ⇒ void

This method returns an undefined value.

This method runs the command from #crack_command and yields each line of output.

Yields:

  • (String)

    a line of output from the cracker command


302
303
304
305
306
307
308
309
310
311
312
313
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 302

def crack
  if cracker == 'john'
    results = john_crack_command
  elsif cracker == 'hashcat'
    results = hashcat_crack_command
  end
  ::IO.popen(results, "rb") do |fd|
    fd.each_line do |line|
      yield line
    end
  end
end

#cracker_session_idObject

This method is a getter for a random Session ID for the cracker. It allows us to dinstiguish between cracking sessions.

@ return [String] the Session ID to use


507
508
509
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 507

def cracker_session_id
  @session_id ||= ::Rex::Text.rand_text_alphanumeric(8)
end

#cracker_versionSring

This method returns the version of John the Ripper or Hashcat being used.

Returns:

  • (Sring)

    the version detected

Raises:


319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 319

def cracker_version
  if cracker == 'john'
    cmd = binary_path
  elsif cracker == 'hashcat'
    cmd = binary_path
    cmd << (" -V")
  end
  ::IO.popen(cmd, "rb") do |fd|
    fd.each_line do |line|
      if cracker == 'john'
        # John the Ripper 1.8.0.13-jumbo-1-bleeding-973a245b96 2018-12-17 20:12:51 +0100 OMP [linux-gnu 64-bit x86_64 AVX2 AC]
        # John the Ripper 1.9.0-jumbo-1 OMP [linux-gnu 64-bit x86_64 AVX2 AC]
        # John the Ripper password cracker, version 1.8.0.2-bleeding-jumbo_omp [64-bit AVX-autoconf]
        # John the Ripper password cracker, version 1.8.0
        return $1.strip if line =~ /John the Ripper(?: password cracker, version)? ([^\[]+)/
      elsif cracker == 'hashcat'
        # v5.1.0
        return $1 if line =~ /(v[\d\.]+)/
      end
    end
  end
  nil
end

#each_cracked_passwordArray

This runs the show command in john and yields cracked passwords.

Returns:

  • (Array)

    the output from teh command split on newlines


485
486
487
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 485

def each_cracked_password
  ::IO.popen(show_command, "rb").readlines
end

#hashcat_crack_commandArray

This method builds an array for the command to actually run the cracker. It builds the command from all of the attributes on the class.

Returns:

  • (Array)

    An array set up for IO.popen to use

Raises:


400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 400

def hashcat_crack_command
  cmd_string = binary_path
  cmd = [cmd_string,  '--session=' + cracker_session_id, '--logfile-disable']

  if pot.present?
    cmd << ("--potfile-path=" + pot)
  else
    cmd << ("--potfile-path=" + john_pot_file)
  end

  if format.present?
    cmd << ("--hash-type=" + jtr_format_to_hashcat_format(format))
  end

  if optimize.present?
    # https://hashcat.net/wiki/doku.php?id=frequently_asked_questions#what_is_the_maximum_supported_password_length_for_optimized_kernels
    # Optimized Kernels has a large impact on speed.  Here are some stats from Hashcat 5.1.0:

    # Kali Linux on Dell Precision M3800
    ## hashcat -b -w 2 -m 0
    # * Device #1: Quadro K1100M, 500/2002 MB allocatable, 2MCU
    # Speed.#1.........:   185.9 MH/s (11.15ms) @ Accel:64 Loops:16 Thr:1024 Vec:1

    ## hashcat -b -w 2 -O -m 0
    # * Device #1: Quadro K1100M, 500/2002 MB allocatable, 2MCU
    # Speed.#1.........:   463.6 MH/s (8.92ms) @ Accel:64 Loops:32 Thr:1024 Vec:1

    # Windows 10
    # PS C:\hashcat-5.1.0> .\hashcat64.exe -b -O -w 2 -m 0
    # * Device #1: GeForce RTX 2070 SUPER, 2048/8192 MB allocatable, 40MCU
    # Speed.#1.........: 13914.0 MH/s (5.77ms) @ Accel:128 Loops:64 Thr:256 Vec:1

    # PS C:\hashcat-5.1.0> .\hashcat64.exe -b -O -w 2 -m 0
    # * Device #1: GeForce RTX 2070 SUPER, 2048/8192 MB allocatable, 40MCU
    # Speed.#1.........: 31545.6 MH/s (10.36ms) @ Accel:256 Loops:128 Thr:256 Vec:1

    # This change should result in 225%-250% speed boost at the sacrifice of some password length, which most likely
    # wouldn't be tested inside of MSF since most users are using the MSF modules for word list and easy cracks.
    # Anything of length where this would cut off is most likely being done independently (outside MSF)

    cmd << ("-O")
  end

  if incremental.present?
    cmd << ("--increment")
    if increment_length.present?
      cmd << ("--increment-min=" + increment_length[0].to_s)
      cmd << ("--increment-max=" + increment_length[1].to_s)
    else
      # anything more than max 4 on even des took 8+min on an i7.
      # maybe in the future this can be adjusted or made a variable
      # but current time, we'll leave it as this seems like reasonable
      # time expectation for a module to run
      cmd << ("--increment-max=4")
    end
  end

  if rules.present?
    cmd << ("--rules-file=" + rules)
  end

  if attack.present?
    cmd << ("--attack-mode=" + attack)
  end

  if max_runtime.present?
    cmd << ("--runtime=" + max_runtime.to_s)
  end

  cmd << hash_path

  if mask.present?
    cmd << mask.to_s
  end

  # must be last
  if wordlist.present?
    cmd << (wordlist)
  end
  cmd
end

#john_config_fileString

This method returns the path to a default john.conf file.

Returns:

  • (String)

    the path to the default john.conf file


492
493
494
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 492

def john_config_file
  ::File.join(::Msf::Config.data_directory, "jtr", "john.conf")
end

#john_crack_commandArray

This method builds an array for the command to actually run the cracker. It builds the command from all of the attributes on the class.

Returns:

  • (Array)

    An array set up for IO.popen to use

Raises:


348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 348

def john_crack_command
  cmd_string = binary_path
  cmd = [cmd_string,  '--session=' + cracker_session_id, '--nolog']

  if config.present?
    cmd << ("--config=" + config)
  else
    cmd << ("--config=" + john_config_file)
  end

  if pot.present?
    cmd << ("--pot=" + pot)
  else
    cmd << ("--pot=" + john_pot_file)
  end

  if fork.present? && fork > 1
    cmd << ("--fork=" + fork.to_s)
  end

  if format.present?
    cmd << ("--format=" + format)
  end

  if wordlist.present?
    cmd << ("--wordlist=" + wordlist)
  end

  if incremental.present?
    cmd << ("--incremental=" + incremental)
  end

  if rules.present?
    cmd << ("--rules=" + rules)
  end

  if max_runtime.present?
    cmd << ("--max-run-time=" + max_runtime.to_s)
  end

  if max_length.present?
    cmd << ("--max-len=" + max_length.to_s)
  end

  cmd << hash_path
end

#john_pot_fileString

This method returns the path to a default john.pot file.

Returns:

  • (String)

    the path to the default john.pot file


499
500
501
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 499

def john_pot_file
  ::File.join(::Msf::Config.config_directory, "john.pot")
end

#jtr_format_to_hashcat_format(format) ⇒ String

This method takes a Metasploit::Framework::PasswordCracker::Cracker.frameworkframework.dbframework.db.credframework.db.cred.privateframework.db.cred.private.jtr_format (string), and returns the string number associated to the hashcat format

@param a jtr_format string

Returns:

  • (String)

    the format number for Hashcat


129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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
183
184
185
186
187
188
189
190
191
192
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 129

def jtr_format_to_hashcat_format(format)
  case format
  when 'md5crypt'
    '500'
  when 'descrypt'
    '1500'
  when 'bsdicrypt'
    '12400'
  when 'sha256crypt'
    '7400'
  when 'sha512crypt'
    '1800'
  when 'bcrypt'
    '3200'
  when 'lm', 'lanman'
    '3000'
  when 'nt', 'ntlm'
    '1000'
  when 'mssql'
    '131'
  when 'mssql05'
    '132'
  when 'mssql12'
    '1731'
  # hashcat requires a format we dont have all the data for
  # in the current dumper, so this is disabled in module and lib
  #when 'oracle', 'des,oracle'
  #  return '3100'
  when 'oracle11', 'raw-sha1,oracle'
    '112'
  when 'oracle12c', 'pbkdf2,oracle12c'
    '12300'
  when 'postgres', 'dynamic_1034', 'raw-md5,postgres'
    '12'
  when 'mysql'
    '200'
  when 'mysql-sha1'
    '300'
  when 'PBKDF2-HMAC-SHA512' # osx 10.8+
    '7100'
  when 'xsha' # osx 10.4-6
    '122'
  when 'xsha512' # osx 10.7
    '1722'
  when 'PBKDF2-HMAC-SHA1' # Atlassian
    '12001'
  when 'phpass' # Wordpress/PHPass, Joomla, phpBB3
    '400'
  when 'mediawiki' # mediawiki b type
    '3711'
  when 'android-samsung-sha1'
    '5800'
  when 'android-sha1'
    '110'
  when 'android-md5'
    '10'
  when 'hmac-md5'
    '10200'
  when 'dynamic_82'
    '1710'
  else
    nil
  end
end

#mode_incrementalObject

This method sets the appropriate parameters to run a cracker in incremental mode


196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 196

def mode_incremental
  self.increment_length = nil
  self.wordlist = nil
  self.mask = nil
  self.max_runtime = nil
  if cracker == 'john'
    self.rules = nil
    self.incremental = 'Digits'
  elsif cracker == 'hashcat'
    self.attack = '3'
    self.incremental = true
  end
end

#mode_normalObject

This method sets the john to 'normal' mode


243
244
245
246
247
248
249
250
251
252
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 243

def mode_normal
  if cracker == 'john'
    self.max_runtime = nil
    self.mask = nil
    self.wordlist = nil
    self.rules = nil
    self.incremental = nil
    self.increment_length = nil
  end
end

#mode_pinObject

This method sets the appropriate parameters to run a cracker in a pin mode (4-8 digits) on hashcat


230
231
232
233
234
235
236
237
238
239
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 230

def mode_pin
  self.rules = nil
  if cracker == 'hashcat'
    self.attack = '3'
    self.mask = '?d'*8
    self.incremental = true
    self.increment_length = [4,8]
    self.max_runtime = 300 #5min on an i7 got through 4-7 digits. 8digit was 32min more
  end
end

#mode_single(file) ⇒ Object

This method sets the john to single mode

@param a file location of the wordlist to use


258
259
260
261
262
263
264
265
266
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 258

def mode_single(file)
  if cracker == 'john'
    self.wordlist = file
    self.rules = 'single'
    self.incremental = nil
    self.increment_length = nil
    self.mask = nil
  end
end

#mode_wordlist(file) ⇒ Object

This method sets the appropriate parameters to run a cracker in wordlist mode

@param a file location of the wordlist to use


214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 214

def mode_wordlist(file)
  self.increment_length = nil
  self.incremental = nil
  self.max_runtime = nil
  self.mask = nil
  if cracker == 'john'
    self.wordlist = file
    self.rules = 'wordlist'
  elsif cracker == 'hashcat'
    self.wordlist = file
    self.attack = '0'
  end
end

#show_commandArray

This method builds the command to show the cracked passwords.

Returns:

  • (Array)

    An array set up for IO.popen to use

Raises:

  • (JohnNotFoundError)

    if a suitable John binary was never found


515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
# File 'lib/metasploit/framework/password_crackers/cracker.rb', line 515

def show_command
  cmd_string = binary_path

  pot_file = pot || john_pot_file
  if cracker=='hashcat'
    cmd = [cmd_string, "--show", "--potfile-path=#{pot_file}", "--hash-type=#{jtr_format_to_hashcat_format(format)}"]
  elsif cracker=='john'
    cmd = [cmd_string, "--show", "--pot=#{pot_file}", "--format=#{format}"]

    if config
      cmd << "--config=#{config}"
    else
      cmd << ("--config=" + john_config_file)
    end
  end
  cmd << hash_path
end