Skip to content

Commit 8914520

Browse files
authored
Land #20418, adds auto selection feature for password crackers
Adds auto selection of cracker for password crackers
2 parents 6f97dbe + c4a2189 commit 8914520

File tree

7 files changed

+130
-86
lines changed

7 files changed

+130
-86
lines changed

lib/metasploit/framework/password_crackers/cracker.rb

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,10 @@ def initialize(attributes = {})
119119
public_send("#{attribute}=", value)
120120
end
121121
end
122+
123+
def get_type
124+
self.cracker
125+
end
122126

123127
# This method takes a {framework.db.cred.private.jtr_format} (string), and
124128
# returns the string number associated to the hashcat format
@@ -300,22 +304,19 @@ def binary_path
300304
if cracker_path && ::File.file?(cracker_path)
301305
return cracker_path
302306
else
303-
# Look in the Environment PATH for the john binary
304-
if cracker == 'john'
305-
path = Rex::FileUtils.find_full_path('john') ||
306-
Rex::FileUtils.find_full_path('john.exe')
307-
elsif cracker == 'hashcat'
308-
path = Rex::FileUtils.find_full_path('hashcat') ||
309-
Rex::FileUtils.find_full_path('hashcat.exe')
307+
case cracker
308+
when 'hashcat'
309+
path = get_hashcat
310+
when 'john'
311+
path = get_john
312+
when 'auto'
313+
path = get_john || get_hashcat
310314
else
311-
raise PasswordCrackerNotFoundError, 'No suitable Cracker was selected, so a binary could not be found on the system'
312-
end
313-
314-
if path && ::File.file?(path)
315-
return path
315+
raise PasswordCrackerNotFoundError, 'No suitable Cracker was selected, so a binary could not be found on the system JOHN || HASHCAT'
316316
end
317+
raise PasswordCrackerNotFoundError, 'No suitable john/hashcat binary was found on the system' unless path && ::File.file?(path)
317318

318-
raise PasswordCrackerNotFoundError, 'No suitable john/hashcat binary was found on the system'
319+
return path
319320
end
320321
end
321322

@@ -575,6 +576,20 @@ def show_command
575576
end
576577
cmd << hash_path
577578
end
579+
580+
def get_hashcat
581+
# Look in the Environment PATH for the hashcat binary
582+
self.cracker = 'hashcat'
583+
Rex::FileUtils.find_full_path('hashcat') ||
584+
Rex::FileUtils.find_full_path('hashcat.exe')
585+
end
586+
587+
def get_john
588+
self.cracker = 'john'
589+
# Look in the Environment PATH for the john binary
590+
Rex::FileUtils.find_full_path('john') ||
591+
Rex::FileUtils.find_full_path('john.exe')
592+
end
578593

579594
end
580595
end

modules/auxiliary/analyze/crack_aix.rb

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ def initialize
2525
'Actions' => [
2626
['john', { 'Description' => 'Use John the Ripper' }],
2727
['hashcat', { 'Description' => 'Use Hashcat' }],
28+
['auto', { 'Description' => 'Auto-selection of cracker' }]
2829
],
29-
'DefaultAction' => 'john',
30+
'DefaultAction' => 'auto',
3031
'Notes' => {
3132
'Stability' => [CRASH_SAFE],
3233
'SideEffects' => [],
@@ -45,9 +46,9 @@ def initialize
4546
def show_command(cracker_instance)
4647
return unless datastore['ShowCommand']
4748

48-
if action.name == 'john'
49+
if @cracker_type == 'john'
4950
cmd = cracker_instance.john_crack_command
50-
elsif action.name == 'hashcat'
51+
elsif @cracker_type == 'hashcat'
5152
cmd = cracker_instance.hashcat_crack_command
5253
end
5354
print_status(" Cracking Command: #{cmd.join(' ')}")
@@ -63,12 +64,12 @@ def check_results(passwords, results, hash_type, method)
6364
next unless fields.count >= 3
6465

6566
cred = { 'hash_type' => hash_type, 'method' => method }
66-
if action.name == 'john'
67+
if @cracker_type == 'john'
6768
cred['username'] = fields.shift
6869
cred['core_id'] = fields.pop
6970
4.times { fields.pop } # Get rid of extra :
7071
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
71-
elsif action.name == 'hashcat'
72+
elsif @cracker_type == 'hashcat'
7273
cred['core_id'] = fields.shift
7374
cred['hash'] = fields.shift
7475
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
@@ -85,14 +86,20 @@ def check_results(passwords, results, hash_type, method)
8586
end
8687

8788
def run
88-
tbl = tbl = cracker_results_table
89+
tbl = cracker_results_table
90+
cracker = new_password_cracker(action.name)
91+
if action.name == 'auto'
92+
@cracker_type = cracker.get_type
93+
else
94+
@cracker_type = action.name
95+
end
8996

9097
hash_types_to_crack = ['descrypt']
9198
jobs_to_do = []
9299

93100
# build our job list
94101
hash_types_to_crack.each do |hash_type|
95-
job = hash_job(hash_type, action.name)
102+
job = hash_job(hash_type, @cracker_type)
96103
if job.nil?
97104
print_status("No #{hash_type} found to crack")
98105
else
@@ -110,8 +117,6 @@ def run
110117
# Inner array format: db_id, hash_type, username, password, method_of_crack
111118
results = []
112119

113-
cracker = new_password_cracker(action.name)
114-
115120
# generate our wordlist and close the file handle. max length of DES is 8
116121
wordlist = wordlist_file(8)
117122
unless wordlist
@@ -136,7 +141,7 @@ def run
136141
cracker_instance = cracker.dup
137142
cracker_instance.format = format
138143

139-
if action.name == 'john'
144+
if @cracker_type == 'john'
140145
cracker_instance.fork = datastore['FORK']
141146
end
142147

@@ -147,7 +152,7 @@ def run
147152
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
148153
next if job['cred_ids_left_to_crack'].empty?
149154

150-
if action.name == 'john'
155+
if @cracker_type == 'john'
151156
print_status "Cracking #{format} hashes in single mode..."
152157
cracker_instance.mode_single(wordlist.path)
153158
show_command cracker_instance
@@ -189,7 +194,7 @@ def run
189194
print_status "Cracking #{format} hashes in wordlist mode..."
190195
cracker_instance.mode_wordlist(wordlist.path)
191196
# Turn on KoreLogic rules if the user asked for it
192-
if action.name == 'john' && datastore['KORELOGIC']
197+
if @cracker_type == 'john' && datastore['KORELOGIC']
193198
cracker_instance.rules = 'KoreLogicRules'
194199
print_status 'Applying KoreLogic ruleset...'
195200
end

modules/auxiliary/analyze/crack_databases.rb

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,9 @@ def initialize
3434
'Actions' => [
3535
['john', { 'Description' => 'Use John the Ripper' }],
3636
['hashcat', { 'Description' => 'Use Hashcat' }],
37+
['auto', { 'Description' => 'Auto-selection of cracker' }]
3738
],
38-
'DefaultAction' => 'john',
39+
'DefaultAction' => 'auto',
3940
'Notes' => {
4041
'Stability' => [CRASH_SAFE],
4142
'SideEffects' => [],
@@ -58,9 +59,9 @@ def initialize
5859
def show_command(cracker_instance)
5960
return unless datastore['ShowCommand']
6061

61-
if action.name == 'john'
62+
if @cracker_type == 'john'
6263
cmd = cracker_instance.john_crack_command
63-
elsif action.name == 'hashcat'
64+
elsif @cracker_type == 'hashcat'
6465
cmd = cracker_instance.hashcat_crack_command
6566
end
6667
print_status(" Cracking Command: #{cmd.join(' ')}")
@@ -74,13 +75,13 @@ def check_results(passwords, results, hash_type, method)
7475
fields = password_line.split(':')
7576
cred = { 'hash_type' => hash_type, 'method' => method }
7677

77-
if action.name == 'john'
78+
if @cracker_type == 'john'
7879
next unless fields.count >= 3
7980

8081
cred['username'] = fields.shift
8182
cred['core_id'] = fields.pop
8283
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
83-
elsif action.name == 'hashcat'
84+
elsif @cracker_type == 'hashcat'
8485
next unless fields.count >= 2
8586

8687
cred['core_id'] = fields.shift
@@ -109,7 +110,13 @@ def check_results(passwords, results, hash_type, method)
109110
end
110111

111112
def run
112-
tbl = tbl = cracker_results_table
113+
tbl = cracker_results_table
114+
cracker = new_password_cracker(action.name)
115+
if action.name == 'auto'
116+
@cracker_type = cracker.get_type
117+
else
118+
@cracker_type = action.name
119+
end
113120

114121
# array of hashes in jtr_format in the db, converted to an OR combined regex
115122
hash_types_to_crack = []
@@ -128,7 +135,7 @@ def run
128135

129136
# hashcat requires a format we dont have all the data for
130137
# in the current dumper, so this is disabled in module and lib
131-
if action.name == 'john'
138+
if @cracker_type == 'john'
132139
hash_types_to_crack << 'oracle'
133140
hash_types_to_crack << 'dynamic_1506'
134141
end
@@ -143,7 +150,7 @@ def run
143150

144151
# build our job list
145152
hash_types_to_crack.each do |hash_type|
146-
job = hash_job(hash_type, action.name)
153+
job = hash_job(hash_type, cracker.cracker)
147154
if job.nil?
148155
print_status("No #{hash_type} found to crack")
149156
else
@@ -161,8 +168,6 @@ def run
161168
# Inner array format: db_id, hash_type, username, password, method_of_crack
162169
results = []
163170

164-
cracker = new_password_cracker(action.name)
165-
166171
# generate our wordlist and close the file handle.
167172
wordlist = wordlist_file
168173
unless wordlist
@@ -187,7 +192,7 @@ def run
187192
cracker_instance = cracker.dup
188193
cracker_instance.format = format
189194

190-
if action.name == 'john'
195+
if @cracker_type == 'john'
191196
cracker_instance.fork = datastore['FORK']
192197
end
193198

@@ -198,7 +203,7 @@ def run
198203
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
199204
next if job['cred_ids_left_to_crack'].empty?
200205

201-
if action.name == 'john'
206+
if @cracker_type == 'john'
202207
print_status "Cracking #{format} hashes in single mode..."
203208
cracker_instance.mode_single(wordlist.path)
204209
show_command cracker_instance
@@ -239,7 +244,7 @@ def run
239244
print_status "Cracking #{format} hashes in wordlist mode..."
240245
cracker_instance.mode_wordlist(wordlist.path)
241246
# Turn on KoreLogic rules if the user asked for it
242-
if action.name == 'john' && datastore['KORELOGIC']
247+
if @cracker_type == 'john' && datastore['KORELOGIC']
243248
cracker_instance.rules = 'KoreLogicRules'
244249
print_status 'Applying KoreLogic ruleset...'
245250
end

modules/auxiliary/analyze/crack_linux.rb

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,9 @@ def initialize
3232
'Actions' => [
3333
['john', { 'Description' => 'Use John the Ripper' }],
3434
['hashcat', { 'Description' => 'Use Hashcat' }],
35+
['auto', { 'Description' => 'Auto-selection of cracker' }]
3536
],
36-
'DefaultAction' => 'john',
37+
'DefaultAction' => 'auto',
3738
'Notes' => {
3839
'Stability' => [CRASH_SAFE],
3940
'SideEffects' => [],
@@ -58,9 +59,9 @@ def initialize
5859
def show_command(cracker_instance)
5960
return unless datastore['ShowCommand']
6061

61-
if action.name == 'john'
62+
if @cracker_type == 'john'
6263
cmd = cracker_instance.john_crack_command
63-
elsif action.name == 'hashcat'
64+
elsif @cracker_type == 'hashcat'
6465
cmd = cracker_instance.hashcat_crack_command
6566
end
6667
print_status(" Cracking Command: #{cmd.join(' ')}")
@@ -74,14 +75,14 @@ def check_results(passwords, results, hash_type, method)
7475
fields = password_line.split(':')
7576
cred = { 'hash_type' => hash_type, 'method' => method }
7677

77-
if action.name == 'john'
78+
if @cracker_type == 'john'
7879
next unless fields.count >= 3 # If we don't have an expected minimum number of fields, this is probably not a hash line
7980

8081
cred['username'] = fields.shift
8182
cred['core_id'] = fields.pop
8283
4.times { fields.pop } # Get rid of extra :
8384
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
84-
elsif action.name == 'hashcat'
85+
elsif @cracker_type == 'hashcat'
8586
next unless fields.count >= 2 # If we don't have an expected minimum number of fields, this is probably not a hash line
8687

8788
cred['core_id'] = fields.shift
@@ -100,7 +101,13 @@ def check_results(passwords, results, hash_type, method)
100101
end
101102

102103
def run
103-
tbl = tbl = cracker_results_table
104+
tbl = cracker_results_table
105+
cracker = new_password_cracker(action.name)
106+
if action.name == 'auto'
107+
@cracker_type = cracker.get_type
108+
else
109+
@cracker_type = action.name
110+
end
104111

105112
# array of hashes in jtr_format in the db, converted to an OR combined regex
106113
hash_types_to_crack = []
@@ -115,7 +122,7 @@ def run
115122

116123
# build our job list
117124
hash_types_to_crack.each do |hash_type|
118-
job = hash_job(hash_type, action.name)
125+
job = hash_job(hash_type, @cracker_type)
119126
if job.nil?
120127
print_status("No #{hash_type} found to crack")
121128
else
@@ -133,8 +140,6 @@ def run
133140
# Inner array format: db_id, hash_type, username, password, method_of_crack
134141
results = []
135142

136-
cracker = new_password_cracker(action.name)
137-
138143
# generate our wordlist and close the file handle.
139144
wordlist = wordlist_file
140145
unless wordlist
@@ -158,7 +163,7 @@ def run
158163
cracker_instance = cracker.dup
159164
cracker_instance.format = format
160165

161-
if action.name == 'john'
166+
if @cracker_type == 'john'
162167
cracker_instance.fork = datastore['FORK']
163168
end
164169

@@ -169,7 +174,7 @@ def run
169174
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
170175
next if job['cred_ids_left_to_crack'].empty?
171176

172-
if action.name == 'john'
177+
if @cracker_type == 'john'
173178
print_status "Cracking #{format} hashes in single mode..."
174179
cracker_instance.mode_single(wordlist.path)
175180
show_command cracker_instance
@@ -211,7 +216,7 @@ def run
211216
print_status "Cracking #{format} hashes in wordlist mode..."
212217
cracker_instance.mode_wordlist(wordlist.path)
213218
# Turn on KoreLogic rules if the user asked for it
214-
if action.name == 'john' && datastore['KORELOGIC']
219+
if @cracker_type == 'john' && datastore['KORELOGIC']
215220
cracker_instance.rules = 'KoreLogicRules'
216221
print_status 'Applying KoreLogic ruleset...'
217222
end

0 commit comments

Comments
 (0)