CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

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