CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!

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