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_databases.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_mssql_fast'
10
moved_from 'auxiliary/analyze/jtr_mysql_fast'
11
moved_from 'auxiliary/analyze/jtr_oracle_fast'
12
moved_from 'auxiliary/analyze/jtr_postgres_fast'
13
14
def initialize
15
super(
16
'Name' => 'Password Cracker: Databases',
17
'Description' => %(
18
This module uses John the Ripper or Hashcat to identify weak passwords that have been
19
acquired from the mssql_hashdump, mysql_hashdump, postgres_hashdump, or oracle_hashdump modules.
20
Passwords that have been successfully cracked are then saved as proper credentials.
21
Due to the complexity of some of the hash types, they can be very slow. Setting the
22
ITERATION_TIMEOUT is highly recommended.
23
MSSQL is 131, 132, and 1731 in hashcat.
24
MYSQL is 200, and 300 in hashcat.
25
ORACLE is 112, and 12300 in hashcat.
26
POSTGRES is 12 in hashcat.
27
),
28
'Author' => [
29
'theLightCosine',
30
'hdm',
31
'h00die' # hashcat integration
32
],
33
'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)
34
'Actions' => [
35
['john', { 'Description' => 'Use John the Ripper' }],
36
['hashcat', { 'Description' => 'Use Hashcat' }],
37
],
38
'DefaultAction' => 'john',
39
)
40
41
register_options(
42
[
43
OptBool.new('MSSQL', [false, 'Include MSSQL hashes', true]),
44
OptBool.new('MYSQL', [false, 'Include MySQL hashes', true]),
45
OptBool.new('ORACLE', [false, 'Include Oracle hashes', true]),
46
OptBool.new('POSTGRES', [false, 'Include Postgres hashes', true]),
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
75
76
cred['username'] = fields.shift
77
cred['core_id'] = fields.pop
78
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
79
elsif action.name == 'hashcat'
80
next unless fields.count >= 2
81
82
case hash_type
83
when 'dynamic_1034'
84
# for postgres we get 4 fields, id:hash:un:pass.
85
cred['core_id'] = fields.shift
86
cred['hash'] = fields.shift
87
cred['username'] = fields.shift
88
cred['password'] = fields.join(':')
89
when 'oracle11', 'raw-sha1,oracle'
90
cred['core_id'] = fields.shift
91
cred['hash'] = "#{fields.shift}#{fields.shift}" # we pull the first two fields, hash and salt
92
cred['password'] = fields.join(':')
93
else
94
cred['core_id'] = fields.shift
95
cred['hash'] = fields.shift
96
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
97
end
98
99
next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines
100
101
# we don't have the username since we overloaded it with the core_id (since its a better fit for us)
102
# so we can now just go grab the username from the DB
103
cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username
104
end
105
results = process_cracker_results(results, cred)
106
end
107
results
108
end
109
110
tbl = tbl = cracker_results_table
111
112
# array of hashes in jtr_format in the db, converted to an OR combined regex
113
hash_types_to_crack = []
114
115
if datastore['MSSQL']
116
hash_types_to_crack << 'mssql'
117
hash_types_to_crack << 'mssql05'
118
hash_types_to_crack << 'mssql12'
119
end
120
if datastore['MYSQL']
121
hash_types_to_crack << 'mysql'
122
hash_types_to_crack << 'mysql-sha1'
123
end
124
if datastore['ORACLE']
125
# dynamic_1506 is oracle 11/12's H field, MD5.
126
127
# hashcat requires a format we dont have all the data for
128
# in the current dumper, so this is disabled in module and lib
129
if action.name == 'john'
130
hash_types_to_crack << 'oracle'
131
hash_types_to_crack << 'dynamic_1506'
132
end
133
hash_types_to_crack << 'oracle11'
134
hash_types_to_crack << 'oracle12c'
135
end
136
if datastore['POSTGRES']
137
hash_types_to_crack << 'dynamic_1034'
138
end
139
140
jobs_to_do = []
141
142
# build our job list
143
hash_types_to_crack.each do |hash_type|
144
job = hash_job(hash_type, action.name)
145
if job.nil?
146
print_status("No #{hash_type} found to crack")
147
else
148
jobs_to_do << job
149
end
150
end
151
152
# bail early of no jobs to do
153
if jobs_to_do.empty?
154
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
155
return
156
end
157
158
# array of arrays for cracked passwords.
159
# Inner array format: db_id, hash_type, username, password, method_of_crack
160
results = []
161
162
cracker = new_password_cracker(action.name)
163
164
# generate our wordlist and close the file handle.
165
wordlist = wordlist_file
166
unless wordlist
167
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
168
return
169
end
170
171
wordlist.close
172
print_status "Wordlist file written out to #{wordlist.path}"
173
174
cleanup_files = [wordlist.path]
175
176
jobs_to_do.each do |job|
177
format = job['type']
178
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
179
hash_file.puts job['formatted_hashlist']
180
hash_file.close
181
cracker.hash_path = hash_file.path
182
cleanup_files << hash_file.path
183
184
# dupe our original cracker so we can safely change options between each run
185
cracker_instance = cracker.dup
186
cracker_instance.format = format
187
188
if action.name == 'john'
189
cracker_instance.fork = datastore['FORK']
190
end
191
192
# first check if anything has already been cracked so we don't report it incorrectly
193
print_status "Checking #{format} hashes already cracked..."
194
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
195
vprint_good(append_results(tbl, results)) unless results.empty?
196
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
197
next if job['cred_ids_left_to_crack'].empty?
198
199
if action.name == 'john'
200
print_status "Cracking #{format} hashes in single mode..."
201
cracker_instance.mode_single(wordlist.path)
202
show_command cracker_instance
203
cracker_instance.crack do |line|
204
vprint_status line.chomp
205
end
206
results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')
207
vprint_good(append_results(tbl, results)) unless results.empty?
208
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
209
next if job['cred_ids_left_to_crack'].empty?
210
211
print_status "Cracking #{format} hashes in normal mode..."
212
cracker_instance.mode_normal
213
show_command cracker_instance
214
cracker_instance.crack do |line|
215
vprint_status line.chomp
216
end
217
results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')
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
if datastore['INCREMENTAL']
224
print_status "Cracking #{format} hashes in incremental mode..."
225
cracker_instance.mode_incremental
226
show_command cracker_instance
227
cracker_instance.crack do |line|
228
vprint_status line.chomp
229
end
230
results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')
231
vprint_good(append_results(tbl, results)) unless results.empty?
232
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
233
next if job['cred_ids_left_to_crack'].empty?
234
end
235
236
if datastore['WORDLIST']
237
print_status "Cracking #{format} hashes in wordlist mode..."
238
cracker_instance.mode_wordlist(wordlist.path)
239
# Turn on KoreLogic rules if the user asked for it
240
if action.name == 'john' && datastore['KORELOGIC']
241
cracker_instance.rules = 'KoreLogicRules'
242
print_status 'Applying KoreLogic ruleset...'
243
end
244
show_command cracker_instance
245
cracker_instance.crack do |line|
246
vprint_status line.chomp
247
end
248
249
results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')
250
vprint_good(append_results(tbl, results)) unless results.empty?
251
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
252
next if job['cred_ids_left_to_crack'].empty?
253
end
254
255
# give a final print of results
256
print_good(append_results(tbl, results))
257
end
258
if datastore['DeleteTempFiles']
259
cleanup_files.each do |f|
260
File.delete(f)
261
end
262
end
263
end
264
end
265
266