Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/analyze/crack_linux.rb
19758 views
1
##
2
# This module requires Metasploit: https://metasploit.com/download
3
# Current source: https://github.com/rapid7/metasploit-framework
4
##
5
6
class MetasploitModule < Msf::Auxiliary
7
include Msf::Auxiliary::PasswordCracker
8
include Msf::Exploit::Deprecated
9
moved_from 'auxiliary/analyze/jtr_linux'
10
11
def initialize
12
super(
13
'Name' => 'Password Cracker: Linux',
14
'Description' => %{
15
This module uses John the Ripper or Hashcat to identify weak passwords that have been
16
acquired from unshadowed passwd files from Unix/Linux systems. The module will only crack
17
MD5, BSDi and DES implementations by default. However, it can also crack
18
Blowfish and SHA(256/512), but it is much slower.
19
MD5 is format 500 in hashcat.
20
DES is format 1500 in hashcat.
21
BSDI is format 12400 in hashcat.
22
BLOWFISH is format 3200 in hashcat.
23
SHA256 is format 7400 in hashcat.
24
SHA512 is format 1800 in hashcat.
25
},
26
'Author' => [
27
'theLightCosine',
28
'hdm',
29
'h00die' # hashcat integration
30
],
31
'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)
32
'Actions' => [
33
['john', { 'Description' => 'Use John the Ripper' }],
34
['hashcat', { 'Description' => 'Use Hashcat' }],
35
],
36
'DefaultAction' => 'john',
37
'Notes' => {
38
'Stability' => [CRASH_SAFE],
39
'SideEffects' => [],
40
'Reliability' => []
41
}
42
)
43
44
register_options(
45
[
46
OptBool.new('MD5', [false, 'Include MD5 hashes', true]),
47
OptBool.new('DES', [false, 'Indlude DES hashes', true]),
48
OptBool.new('BSDI', [false, 'Include BSDI hashes', true]),
49
OptBool.new('BLOWFISH', [false, 'Include BLOWFISH hashes (Very Slow)', false]),
50
OptBool.new('SHA256', [false, 'Include SHA256 hashes (Very Slow)', false]),
51
OptBool.new('SHA512', [false, 'Include SHA512 hashes (Very Slow)', false]),
52
OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),
53
OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])
54
]
55
)
56
end
57
58
def show_command(cracker_instance)
59
return unless datastore['ShowCommand']
60
61
if action.name == 'john'
62
cmd = cracker_instance.john_crack_command
63
elsif action.name == 'hashcat'
64
cmd = cracker_instance.hashcat_crack_command
65
end
66
print_status(" Cracking Command: #{cmd.join(' ')}")
67
end
68
69
def check_results(passwords, results, hash_type, method)
70
passwords.each do |password_line|
71
password_line.chomp!
72
next if password_line.blank?
73
74
fields = password_line.split(':')
75
cred = { 'hash_type' => hash_type, 'method' => method }
76
77
if action.name == 'john'
78
next unless fields.count >= 3 # If we don't have an expected minimum number of fields, this is probably not a hash line
79
80
cred['username'] = fields.shift
81
cred['core_id'] = fields.pop
82
4.times { fields.pop } # Get rid of extra :
83
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
next unless fields.count >= 2 # If we don't have an expected minimum number of fields, this is probably not a hash line
86
87
cred['core_id'] = fields.shift
88
cred['hash'] = fields.shift
89
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
90
next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines
91
92
# we don't have the username since we overloaded it with the core_id (since its a better fit for us)
93
# so we can now just go grab the username from the DB
94
cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username
95
end
96
results = process_cracker_results(results, cred)
97
end
98
99
results
100
end
101
102
def run
103
tbl = tbl = cracker_results_table
104
105
# array of hashes in jtr_format in the db, converted to an OR combined regex
106
hash_types_to_crack = []
107
hash_types_to_crack << 'md5crypt' if datastore['MD5']
108
hash_types_to_crack << 'descrypt' if datastore['DES']
109
hash_types_to_crack << 'bsdicrypt' if datastore['BSDI']
110
hash_types_to_crack << 'bcrypt' if datastore['BLOWFISH']
111
hash_types_to_crack << 'sha256crypt' if datastore['SHA256']
112
hash_types_to_crack << 'sha512crypt' if datastore['SHA512']
113
114
jobs_to_do = []
115
116
# build our job list
117
hash_types_to_crack.each do |hash_type|
118
job = hash_job(hash_type, action.name)
119
if job.nil?
120
print_status("No #{hash_type} found to crack")
121
else
122
jobs_to_do << job
123
end
124
end
125
126
# bail early of no jobs to do
127
if jobs_to_do.empty?
128
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
129
return
130
end
131
132
# array of arrays for cracked passwords.
133
# Inner array format: db_id, hash_type, username, password, method_of_crack
134
results = []
135
136
cracker = new_password_cracker(action.name)
137
138
# generate our wordlist and close the file handle.
139
wordlist = wordlist_file
140
unless wordlist
141
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
142
return
143
end
144
145
wordlist.close
146
print_status "Wordlist file written out to #{wordlist.path}"
147
148
cleanup_files = [wordlist.path]
149
jobs_to_do.each do |job|
150
format = job['type']
151
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
152
hash_file.puts job['formatted_hashlist']
153
hash_file.close
154
cracker.hash_path = hash_file.path
155
cleanup_files << hash_file.path
156
157
# dupe our original cracker so we can safely change options between each run
158
cracker_instance = cracker.dup
159
cracker_instance.format = format
160
161
if action.name == 'john'
162
cracker_instance.fork = datastore['FORK']
163
end
164
165
# first check if anything has already been cracked so we don't report it incorrectly
166
print_status "Checking #{format} hashes already cracked..."
167
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
168
vprint_good(append_results(tbl, results)) unless results.empty?
169
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
170
next if job['cred_ids_left_to_crack'].empty?
171
172
if action.name == 'john'
173
print_status "Cracking #{format} hashes in single mode..."
174
cracker_instance.mode_single(wordlist.path)
175
show_command cracker_instance
176
cracker_instance.crack do |line|
177
vprint_status line.chomp
178
end
179
results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')
180
vprint_good(append_results(tbl, results)) unless results.empty?
181
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
182
next if job['cred_ids_left_to_crack'].empty?
183
184
print_status "Cracking #{format} hashes in normal mode..."
185
cracker_instance.mode_normal
186
show_command cracker_instance
187
cracker_instance.crack do |line|
188
vprint_status line.chomp
189
end
190
results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')
191
vprint_good(append_results(tbl, results)) unless results.empty?
192
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
193
next if job['cred_ids_left_to_crack'].empty?
194
end
195
196
if datastore['INCREMENTAL']
197
print_status "Cracking #{format} hashes in incremental mode..."
198
cracker_instance.mode_incremental
199
show_command cracker_instance
200
cracker_instance.crack do |line|
201
vprint_status line.chomp
202
end
203
results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')
204
vprint_good(append_results(tbl, results)) unless results.empty?
205
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
206
next if job['cred_ids_left_to_crack'].empty?
207
end
208
209
next unless datastore['WORDLIST']
210
211
print_status "Cracking #{format} hashes in wordlist mode..."
212
cracker_instance.mode_wordlist(wordlist.path)
213
# Turn on KoreLogic rules if the user asked for it
214
if action.name == 'john' && datastore['KORELOGIC']
215
cracker_instance.rules = 'KoreLogicRules'
216
print_status 'Applying KoreLogic ruleset...'
217
end
218
show_command cracker_instance
219
cracker_instance.crack do |line|
220
vprint_status line.chomp
221
end
222
223
results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')
224
vprint_good(append_results(tbl, results)) unless results.empty?
225
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
226
next if job['cred_ids_left_to_crack'].empty?
227
end
228
229
# give a final print of results
230
print_good(append_results(tbl, results))
231
232
if datastore['DeleteTempFiles']
233
cleanup_files.each do |f|
234
File.delete(f)
235
end
236
end
237
end
238
end
239
240