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