Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/modules/auxiliary/analyze/crack_osx.rb
21532 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: OSX',
12
'Description' => %(
13
This module uses John the Ripper or Hashcat to identify weak passwords that have been
14
acquired from OSX systems. The module will only crack xsha from OSX 10.4-10.6, xsha512
15
from 10.7, and PBKDF2 from OSX 10.8+.
16
XSHA is 122 in hashcat.
17
XSHA512 is 1722 in hashcat.
18
PBKDF2 (PBKDF2-HMAC-SHA512) is 7100 in hashcat.
19
),
20
'Author' => [
21
'h00die' # hashcat integration
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('XSHA', [false, 'Include XSHA hashes from 10.4-10.6', true]),
40
OptBool.new('XSHA512', [false, 'Include XSHA512 hashes from 10.7', true]),
41
OptBool.new('PBKDF2', [false, 'Include PBKDF2-HMAC-SHA512 hashes from 10.8+', 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 @cracker_type == 'john'
52
cmd = cracker_instance.john_crack_command
53
elsif @cracker_type == '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 @cracker_type == 'john'
68
next unless fields.count >= 3
69
70
cred['username'] = fields.shift
71
cred['core_id'] = fields.pop
72
unless hash_type == 'PBKDF2-HMAC-SHA512'
73
4.times { fields.pop } # Get rid of extra :
74
end
75
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
76
elsif @cracker_type == 'hashcat'
77
next unless fields.count >= 3
78
79
cred['core_id'] = fields.shift
80
cred['hash'] = fields.shift
81
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
82
next if cred['core_id'].include?("Hashfile '") && cred['core_id'].include?("' on line ") # skip error lines
83
84
# we don't have the username since we overloaded it with the core_id (since its a better fit for us)
85
# so we can now just go grab the username from the DB
86
cred['username'] = framework.db.creds(workspace: myworkspace, id: cred['core_id'])[0].public.username
87
end
88
results = process_cracker_results(results, cred)
89
end
90
91
results
92
end
93
94
def run
95
tbl = cracker_results_table
96
cracker = new_password_cracker(action.name)
97
if action.name == 'auto'
98
@cracker_type = cracker.get_type
99
else
100
@cracker_type = action.name
101
end
102
103
# array of hashes in jtr_format in the db, converted to an OR combined regex
104
hash_types_to_crack = []
105
hash_types_to_crack << 'xsha' if datastore['XSHA']
106
hash_types_to_crack << 'xsha512' if datastore['XSHA512']
107
hash_types_to_crack << 'PBKDF2-HMAC-SHA512' if datastore['PBKDF2']
108
jobs_to_do = []
109
110
# build our job list
111
hash_types_to_crack.each do |hash_type|
112
job = hash_job(hash_type, @cracker_type)
113
if job.nil?
114
print_status("No #{hash_type} found to crack")
115
else
116
jobs_to_do << job
117
end
118
end
119
120
# bail early of no jobs to do
121
if jobs_to_do.empty?
122
print_good("No uncracked password hashes found for: #{hash_types_to_crack.join(', ')}")
123
return
124
end
125
126
# array of arrays for cracked passwords.
127
# Inner array format: db_id, hash_type, username, password, method_of_crack
128
results = []
129
130
# generate our wordlist and close the file handle.
131
wordlist = wordlist_file
132
unless wordlist
133
print_error('This module cannot run without a database connected. Use db_connect to connect to a database.')
134
return
135
end
136
137
wordlist.close
138
print_status "Wordlist file written out to #{wordlist.path}"
139
140
cleanup_files = [wordlist.path]
141
142
jobs_to_do.each do |job|
143
format = job['type']
144
hash_file = Rex::Quickfile.new("hashes_#{job['type']}_")
145
hash_file.puts job['formatted_hashlist']
146
hash_file.close
147
cracker.hash_path = hash_file.path
148
cleanup_files << hash_file.path
149
# dupe our original cracker so we can safely change options between each run
150
cracker_instance = cracker.dup
151
cracker_instance.format = format
152
if @cracker_type == 'john'
153
cracker_instance.fork = datastore['FORK']
154
end
155
156
# first check if anything has already been cracked so we don't report it incorrectly
157
print_status "Checking #{format} hashes already cracked..."
158
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
159
vprint_good(append_results(tbl, results)) unless results.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