Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/analyze/crack_databases.rb
19669 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_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
'Notes' => {
40
'Stability' => [CRASH_SAFE],
41
'SideEffects' => [],
42
'Reliability' => []
43
}
44
)
45
46
register_options(
47
[
48
OptBool.new('MSSQL', [false, 'Include MSSQL hashes', true]),
49
OptBool.new('MYSQL', [false, 'Include MySQL hashes', true]),
50
OptBool.new('ORACLE', [false, 'Include Oracle hashes', true]),
51
OptBool.new('POSTGRES', [false, 'Include Postgres hashes', true]),
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
79
80
cred['username'] = fields.shift
81
cred['core_id'] = fields.pop
82
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
next unless fields.count >= 2
85
86
cred['core_id'] = fields.shift
87
case hash_type
88
when 'dynamic_1034'
89
# for postgres we get 4 fields, id:hash:un:pass.
90
cred['hash'] = fields.shift
91
cred['username'] = fields.shift
92
when 'oracle11', 'raw-sha1,oracle'
93
cred['hash'] = "#{fields.shift}#{fields.shift}" # we pull the first two fields, hash and salt
94
else
95
cred['hash'] = fields.shift
96
end
97
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
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
108
results
109
end
110
111
def run
112
tbl = tbl = cracker_results_table
113
114
# array of hashes in jtr_format in the db, converted to an OR combined regex
115
hash_types_to_crack = []
116
117
if datastore['MSSQL']
118
hash_types_to_crack << 'mssql'
119
hash_types_to_crack << 'mssql05'
120
hash_types_to_crack << 'mssql12'
121
end
122
if datastore['MYSQL']
123
hash_types_to_crack << 'mysql'
124
hash_types_to_crack << 'mysql-sha1'
125
end
126
if datastore['ORACLE']
127
# dynamic_1506 is oracle 11/12's H field, MD5.
128
129
# hashcat requires a format we dont have all the data for
130
# in the current dumper, so this is disabled in module and lib
131
if action.name == 'john'
132
hash_types_to_crack << 'oracle'
133
hash_types_to_crack << 'dynamic_1506'
134
end
135
hash_types_to_crack << 'oracle11'
136
hash_types_to_crack << 'oracle12c'
137
end
138
if datastore['POSTGRES']
139
hash_types_to_crack << 'dynamic_1034'
140
end
141
142
jobs_to_do = []
143
144
# build our job list
145
hash_types_to_crack.each do |hash_type|
146
job = hash_job(hash_type, action.name)
147
if job.nil?
148
print_status("No #{hash_type} found to crack")
149
else
150
jobs_to_do << job
151
end
152
end
153
154
# bail early of no jobs to do
155
if jobs_to_do.empty?
156
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
157
return
158
end
159
160
# array of arrays for cracked passwords.
161
# Inner array format: db_id, hash_type, username, password, method_of_crack
162
results = []
163
164
cracker = new_password_cracker(action.name)
165
166
# generate our wordlist and close the file handle.
167
wordlist = wordlist_file
168
unless wordlist
169
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
170
return
171
end
172
173
wordlist.close
174
print_status "Wordlist file written out to #{wordlist.path}"
175
176
cleanup_files = [wordlist.path]
177
178
jobs_to_do.each do |job|
179
format = job['type']
180
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
181
hash_file.puts job['formatted_hashlist']
182
hash_file.close
183
cracker.hash_path = hash_file.path
184
cleanup_files << hash_file.path
185
186
# dupe our original cracker so we can safely change options between each run
187
cracker_instance = cracker.dup
188
cracker_instance.format = format
189
190
if action.name == 'john'
191
cracker_instance.fork = datastore['FORK']
192
end
193
194
# first check if anything has already been cracked so we don't report it incorrectly
195
print_status "Checking #{format} hashes already cracked..."
196
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
197
vprint_good(append_results(tbl, results)) unless results.empty?
198
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
199
next if job['cred_ids_left_to_crack'].empty?
200
201
if action.name == 'john'
202
print_status "Cracking #{format} hashes in single mode..."
203
cracker_instance.mode_single(wordlist.path)
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, 'Single')
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
213
print_status "Cracking #{format} hashes in normal mode..."
214
cracker_instance.mode_normal
215
show_command cracker_instance
216
cracker_instance.crack do |line|
217
vprint_status line.chomp
218
end
219
results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')
220
vprint_good(append_results(tbl, results)) unless results.empty?
221
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
222
next if job['cred_ids_left_to_crack'].empty?
223
end
224
225
if datastore['INCREMENTAL']
226
print_status "Cracking #{format} hashes in incremental mode..."
227
cracker_instance.mode_incremental
228
show_command cracker_instance
229
cracker_instance.crack do |line|
230
vprint_status line.chomp
231
end
232
results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')
233
vprint_good(append_results(tbl, results)) unless results.empty?
234
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
235
next if job['cred_ids_left_to_crack'].empty?
236
end
237
238
if datastore['WORDLIST']
239
print_status "Cracking #{format} hashes in wordlist mode..."
240
cracker_instance.mode_wordlist(wordlist.path)
241
# Turn on KoreLogic rules if the user asked for it
242
if action.name == 'john' && datastore['KORELOGIC']
243
cracker_instance.rules = 'KoreLogicRules'
244
print_status 'Applying KoreLogic ruleset...'
245
end
246
show_command cracker_instance
247
cracker_instance.crack do |line|
248
vprint_status line.chomp
249
end
250
251
results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')
252
vprint_good(append_results(tbl, results)) unless results.empty?
253
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
254
next if job['cred_ids_left_to_crack'].empty?
255
end
256
257
# give a final print of results
258
print_good(append_results(tbl, results))
259
end
260
if datastore['DeleteTempFiles']
261
cleanup_files.each do |f|
262
File.delete(f)
263
end
264
end
265
end
266
end
267
268