Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/analyze/crack_webapps.rb
21545 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
9
def initialize
10
super(
11
'Name' => 'Password Cracker: Webapps',
12
'Description' => %(
13
This module uses John the Ripper or Hashcat to identify weak passwords that have been
14
acquired from various web applications.
15
Atlassian uses PBKDF2-HMAC-SHA1 which is 12001 in hashcat.
16
PHPass uses phpass which is 400 in hashcat.
17
Mediawiki is MD5 based and is 3711 in hashcat.
18
Apache Superset, some Flask and Werkzeug apps is pbkdf2-sha256 and is 10900 in hashcat
19
),
20
'Author' => [
21
'h00die'
22
],
23
'License' => MSF_LICENSE, # JtR itself is GPLv2, but this wrapper is MSF (BSD)
24
'Actions' => [
25
['john', { 'Description' => 'Use John the Ripper' }],
26
['hashcat', { 'Description' => 'Use Hashcat' }],
27
['auto', { 'Description' => 'Auto-selection of cracker' }]
28
],
29
'DefaultAction' => 'auto',
30
'Notes' => {
31
'Stability' => [CRASH_SAFE],
32
'SideEffects' => [],
33
'Reliability' => []
34
}
35
)
36
37
register_options(
38
[
39
OptBool.new('ATLASSIAN', [false, 'Include Atlassian hashes', true]),
40
OptBool.new('MEDIAWIKI', [false, 'Include MediaWiki hashes', true]),
41
OptBool.new('PHPASS', [false, 'Include Wordpress/PHPass, Joomla, phpBB3 hashes', true]),
42
OptBool.new('PBKDF2', [false, 'Apache Superset, some Flask and Werkzeug apps hashes', true]),
43
OptBool.new('INCREMENTAL', [false, 'Run in incremental mode', true]),
44
OptBool.new('WORDLIST', [false, 'Run in wordlist mode', true])
45
]
46
)
47
end
48
49
def show_command(cracker_instance)
50
return unless datastore['ShowCommand']
51
52
if @cracker_type == 'john'
53
cmd = cracker_instance.john_crack_command
54
elsif @cracker_type == 'hashcat'
55
cmd = cracker_instance.hashcat_crack_command
56
end
57
print_status(" Cracking Command: #{cmd.join(' ')}")
58
end
59
60
def check_results(passwords, results, hash_type, method)
61
passwords.each do |password_line|
62
password_line.chomp!
63
next if password_line.blank?
64
65
fields = password_line.split(':')
66
cred = { 'hash_type' => hash_type, 'method' => method }
67
# If we don't have an expected minimum number of fields, this is probably not a hash line
68
if @cracker_type == 'john'
69
next unless fields.count >= 3
70
71
cred['username'] = fields.shift
72
cred['core_id'] = fields.pop
73
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
74
elsif @cracker_type == 'hashcat'
75
next unless fields.count >= 2
76
77
cred['core_id'] = fields.shift
78
cred['hash'] = fields.shift
79
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
80
next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines
81
82
# we don't have the username since we overloaded it with the core_id (since its a better fit for us)
83
# so we can now just go grab the username from the DB
84
cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username
85
end
86
results = process_cracker_results(results, cred)
87
end
88
89
results
90
end
91
92
def run
93
tbl = cracker_results_table
94
cracker = new_password_cracker(action.name)
95
if action.name == 'auto'
96
@cracker_type = cracker.get_type
97
else
98
@cracker_type = action.name
99
end
100
101
hash_types_to_crack = []
102
hash_types_to_crack << 'PBKDF2-HMAC-SHA1' if datastore['ATLASSIAN']
103
hash_types_to_crack << 'phpass' if datastore['PHPASS']
104
hash_types_to_crack << 'mediawiki' if datastore['MEDIAWIKI']
105
hash_types_to_crack << 'pbkdf2-sha256' if datastore['PBKDF2']
106
jobs_to_do = []
107
108
# build our job list
109
hash_types_to_crack.each do |hash_type|
110
job = hash_job(hash_type, @cracker_type)
111
if job.nil?
112
print_status("No #{hash_type} found to crack")
113
else
114
jobs_to_do << job
115
end
116
end
117
118
# bail early of no jobs to do
119
if jobs_to_do.empty?
120
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
121
return
122
end
123
124
# array of arrays for cracked passwords.
125
# Inner array format: db_id, hash_type, username, password, method_of_crack
126
results = []
127
128
# generate our wordlist and close the file handle.
129
wordlist = wordlist_file
130
unless wordlist
131
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
132
return
133
end
134
135
wordlist.close
136
print_status "Wordlist file written out to #{wordlist.path}"
137
138
cleanup_files = [wordlist.path]
139
140
jobs_to_do.each do |job|
141
format = job['type']
142
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
143
hash_file.puts job['formatted_hashlist']
144
hash_file.close
145
cracker.hash_path = hash_file.path
146
cleanup_files << hash_file.path
147
# dupe our original cracker so we can safely change options between each run
148
cracker_instance = cracker.dup
149
cracker_instance.format = format
150
if @cracker_type == 'john'
151
cracker_instance.fork = datastore['FORK']
152
end
153
154
# first check if anything has already been cracked so we don't report it incorrectly
155
print_status "Checking #{format} hashes already cracked..."
156
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
157
vprint_good(append_results(tbl, results)) unless results.empty?
158
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
159
next if job['cred_ids_left_to_crack'].empty?
160
161
if @cracker_type == 'john'
162
print_status "Cracking #{format} hashes in single mode..."
163
cracker_instance.mode_single(wordlist.path)
164
show_command cracker_instance
165
cracker_instance.crack do |line|
166
vprint_status line.chomp
167
end
168
results = check_results(cracker_instance.each_cracked_password, results, format, 'Single')
169
vprint_good(append_results(tbl, results)) unless results.empty?
170
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
171
next if job['cred_ids_left_to_crack'].empty?
172
173
print_status "Cracking #{format} hashes in normal mode..."
174
cracker_instance.mode_normal
175
show_command cracker_instance
176
cracker_instance.crack do |line|
177
vprint_status line.chomp
178
end
179
results = check_results(cracker_instance.each_cracked_password, results, format, 'Normal')
180
vprint_good(append_results(tbl, results)) unless results.empty?
181
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
182
next if job['cred_ids_left_to_crack'].empty?
183
end
184
185
if datastore['INCREMENTAL']
186
print_status "Cracking #{format} hashes in incremental mode..."
187
cracker_instance.mode_incremental
188
show_command cracker_instance
189
cracker_instance.crack do |line|
190
vprint_status line.chomp
191
end
192
results = check_results(cracker_instance.each_cracked_password, results, format, 'Incremental')
193
vprint_good(append_results(tbl, results)) unless results.empty?
194
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
195
next if job['cred_ids_left_to_crack'].empty?
196
end
197
198
if datastore['WORDLIST']
199
print_status "Cracking #{format} hashes in wordlist mode..."
200
cracker_instance.mode_wordlist(wordlist.path)
201
# Turn on KoreLogic rules if the user asked for it
202
if @cracker_type == 'john' && datastore['KORELOGIC']
203
cracker_instance.rules = 'KoreLogicRules'
204
print_status 'Applying KoreLogic ruleset...'
205
end
206
show_command cracker_instance
207
cracker_instance.crack do |line|
208
vprint_status line.chomp
209
end
210
211
results = check_results(cracker_instance.each_cracked_password, results, format, 'Wordlist')
212
vprint_good(append_results(tbl, results)) unless results.empty?
213
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
214
next if job['cred_ids_left_to_crack'].empty?
215
end
216
217
# give a final print of results
218
print_good(append_results(tbl, results))
219
end
220
if datastore['DeleteTempFiles']
221
cleanup_files.each do |f|
222
File.delete(f)
223
end
224
end
225
end
226
end
227
228