Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/analyze/crack_linux.rb
21532 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
['auto', { 'Description' => 'Auto-selection of cracker' }]
36
],
37
'DefaultAction' => 'auto',
38
'Notes' => {
39
'Stability' => [CRASH_SAFE],
40
'SideEffects' => [],
41
'Reliability' => []
42
}
43
)
44
45
register_options(
46
[
47
OptBool.new('MD5', [false, 'Include MD5 hashes', true]),
48
OptBool.new('DES', [false, 'Indlude DES hashes', true]),
49
OptBool.new('BSDI', [false, 'Include BSDI hashes', true]),
50
OptBool.new('BLOWFISH', [false, 'Include BLOWFISH hashes (Very Slow)', false]),
51
OptBool.new('SHA256', [false, 'Include SHA256 hashes (Very Slow)', false]),
52
OptBool.new('SHA512', [false, 'Include SHA512 hashes (Very Slow)', false]),
53
OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),
54
OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])
55
]
56
)
57
end
58
59
def show_command(cracker_instance)
60
return unless datastore['ShowCommand']
61
62
if @cracker_type == 'john'
63
cmd = cracker_instance.john_crack_command
64
elsif @cracker_type == 'hashcat'
65
cmd = cracker_instance.hashcat_crack_command
66
end
67
print_status(" Cracking Command: #{cmd.join(' ')}")
68
end
69
70
def check_results(passwords, results, hash_type, method)
71
passwords.each do |password_line|
72
password_line.chomp!
73
next if password_line.blank?
74
75
fields = password_line.split(':')
76
cred = { 'hash_type' => hash_type, 'method' => method }
77
78
if @cracker_type == 'john'
79
next unless fields.count >= 3 # If we don't have an expected minimum number of fields, this is probably not a hash line
80
81
cred['username'] = fields.shift
82
cred['core_id'] = fields.pop
83
4.times { fields.pop } # Get rid of extra :
84
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
85
elsif @cracker_type == 'hashcat'
86
next unless fields.count >= 2 # If we don't have an expected minimum number of fields, this is probably not a hash line
87
88
cred['core_id'] = fields.shift
89
cred['hash'] = fields.shift
90
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
91
next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines
92
93
# we don't have the username since we overloaded it with the core_id (since its a better fit for us)
94
# so we can now just go grab the username from the DB
95
cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username
96
end
97
results = process_cracker_results(results, cred)
98
end
99
100
results
101
end
102
103
def run
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
111
112
# array of hashes in jtr_format in the db, converted to an OR combined regex
113
hash_types_to_crack = []
114
hash_types_to_crack << 'md5crypt' if datastore['MD5']
115
hash_types_to_crack << 'descrypt' if datastore['DES']
116
hash_types_to_crack << 'bsdicrypt' if datastore['BSDI']
117
hash_types_to_crack << 'bcrypt' if datastore['BLOWFISH']
118
hash_types_to_crack << 'sha256crypt' if datastore['SHA256']
119
hash_types_to_crack << 'sha512crypt' if datastore['SHA512']
120
121
jobs_to_do = []
122
123
# build our job list
124
hash_types_to_crack.each do |hash_type|
125
job = hash_job(hash_type, @cracker_type)
126
if job.nil?
127
print_status("No #{hash_type} found to crack")
128
else
129
jobs_to_do << job
130
end
131
end
132
133
# bail early of no jobs to do
134
if jobs_to_do.empty?
135
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
136
return
137
end
138
139
# array of arrays for cracked passwords.
140
# Inner array format: db_id, hash_type, username, password, method_of_crack
141
results = []
142
143
# generate our wordlist and close the file handle.
144
wordlist = wordlist_file
145
unless wordlist
146
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
147
return
148
end
149
150
wordlist.close
151
print_status "Wordlist file written out to #{wordlist.path}"
152
153
cleanup_files = [wordlist.path]
154
jobs_to_do.each do |job|
155
format = job['type']
156
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
157
hash_file.puts job['formatted_hashlist']
158
hash_file.close
159
cracker.hash_path = hash_file.path
160
cleanup_files << hash_file.path
161
162
# dupe our original cracker so we can safely change options between each run
163
cracker_instance = cracker.dup
164
cracker_instance.format = format
165
166
if @cracker_type == 'john'
167
cracker_instance.fork = datastore['FORK']
168
end
169
170
# first check if anything has already been cracked so we don't report it incorrectly
171
print_status "Checking #{format} hashes already cracked..."
172
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
173
vprint_good(append_results(tbl, results)) unless results.empty?
174
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
175
next if job['cred_ids_left_to_crack'].empty?
176
177
if @cracker_type == 'john'
178
print_status "Cracking #{format} hashes in single mode..."
179
cracker_instance.mode_single(wordlist.path)
180
show_command cracker_instance
181
cracker_instance.crack do |line|
182
vprint_status line.chomp
183
end
184
results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')
185
vprint_good(append_results(tbl, results)) unless results.empty?
186
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
187
next if job['cred_ids_left_to_crack'].empty?
188
189
print_status "Cracking #{format} hashes in normal mode..."
190
cracker_instance.mode_normal
191
show_command cracker_instance
192
cracker_instance.crack do |line|
193
vprint_status line.chomp
194
end
195
results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')
196
vprint_good(append_results(tbl, results)) unless results.empty?
197
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
198
next if job['cred_ids_left_to_crack'].empty?
199
end
200
201
if datastore['INCREMENTAL']
202
print_status "Cracking #{format} hashes in incremental mode..."
203
cracker_instance.mode_incremental
204
show_command cracker_instance
205
cracker_instance.crack do |line|
206
vprint_status line.chomp
207
end
208
results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')
209
vprint_good(append_results(tbl, results)) unless results.empty?
210
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
211
next if job['cred_ids_left_to_crack'].empty?
212
end
213
214
next unless datastore['WORDLIST']
215
216
print_status "Cracking #{format} hashes in wordlist mode..."
217
cracker_instance.mode_wordlist(wordlist.path)
218
# Turn on KoreLogic rules if the user asked for it
219
if @cracker_type == 'john' && datastore['KORELOGIC']
220
cracker_instance.rules = 'KoreLogicRules'
221
print_status 'Applying KoreLogic ruleset...'
222
end
223
show_command cracker_instance
224
cracker_instance.crack do |line|
225
vprint_status line.chomp
226
end
227
228
results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')
229
vprint_good(append_results(tbl, results)) unless results.empty?
230
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
231
next if job['cred_ids_left_to_crack'].empty?
232
end
233
234
# give a final print of results
235
print_good(append_results(tbl, results))
236
237
if datastore['DeleteTempFiles']
238
cleanup_files.each do |f|
239
File.delete(f)
240
end
241
end
242
end
243
end
244
245