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