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