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