Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/credential_collection.rb
22040 views
1
require 'metasploit/framework/credential'
2
3
module Metasploit::Framework
4
5
class PrivateCredentialCollection
6
# @!attribute additional_privates
7
# Additional private values that should be tried
8
# @return [Array<String>]
9
attr_accessor :additional_privates
10
11
# @!attribute blank_passwords
12
# Whether each username should be tried with a blank password
13
# @return [Boolean]
14
attr_accessor :blank_passwords
15
16
# @!attribute nil_passwords
17
# Whether each username should be tried with a nil password
18
# @return [Boolean]
19
attr_accessor :nil_passwords
20
21
# @!attribute pass_file
22
# Path to a file containing passwords, one per line
23
# @return [String]
24
attr_accessor :pass_file
25
26
# @!attribute password
27
# The password that should be tried
28
# @return [String]
29
attr_accessor :password
30
31
# @!attribute prepended_creds
32
# List of credentials to be tried before any others
33
#
34
# @see #prepend_cred
35
# @return [Array<Credential>]
36
attr_accessor :prepended_creds
37
38
# @!attribute realm
39
# The authentication realm associated with this password
40
# @return [String]
41
attr_accessor :realm
42
43
# @!attribute filter
44
# A block that can be used to filter credential objects
45
attr_accessor :filter
46
47
# @option opts [Boolean] :nil_passwords See {#nil_passwords}
48
# @option opts [Boolean] :blank_passwords See {#blank_passwords}
49
# @option opts [String] :pass_file See {#pass_file}
50
# @option opts [String] :password See {#password}
51
# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}
52
# @option opts [Boolean] :user_as_pass See {#user_as_pass}
53
# @option opts [String] :user_file See {#user_file}
54
# @option opts [String] :username See {#username}
55
# @option opts [String] :userpass_file See {#userpass_file}
56
# @option opts [String] :usernames_only See {#usernames_only}
57
def initialize(opts = {})
58
opts.each do |attribute, value|
59
public_send("#{attribute}=", value)
60
end
61
self.prepended_creds ||= []
62
self.additional_privates ||= []
63
self.filter = nil
64
end
65
66
# Adds a string as an additional private credential
67
# to be combined in the collection.
68
#
69
# @param [String] private_str The string to use as a private credential
70
# @return [void]
71
def add_private(private_str='')
72
additional_privates << private_str
73
end
74
75
# Add {Credential credentials} that will be yielded by {#each}
76
#
77
# @see prepended_creds
78
# @param [Credential] cred
79
# @return [self]
80
def prepend_cred(cred)
81
prepended_creds.unshift cred
82
self
83
end
84
85
# Combines all the provided credential sources into a stream of {Credential}
86
# objects, yielding them one at a time
87
#
88
# @yieldparam credential [Metasploit::Framework::Credential]
89
# @return [void]
90
def each_filtered
91
each_unfiltered do |credential|
92
next unless self.filter.nil? || self.filter.call(credential)
93
94
yield credential
95
end
96
end
97
98
# Combines all the provided credential sources into a stream of {Credential}
99
# objects, yielding them one at a time
100
#
101
# @yieldparam credential [Metasploit::Framework::Credential]
102
# @return [void]
103
def each_unfiltered
104
if pass_file.present?
105
pass_fd = File.open(pass_file, 'r:binary')
106
end
107
108
prepended_creds.each { |c| yield c }
109
110
if password.present?
111
yield Metasploit::Framework::Credential.new(private: password, realm: realm, private_type: private_type(password))
112
end
113
if blank_passwords
114
yield Metasploit::Framework::Credential.new(private: "", realm: realm, private_type: :password)
115
end
116
if nil_passwords
117
yield Metasploit::Framework::Credential.new(private: nil, realm: realm, private_type: :password)
118
end
119
if pass_fd
120
pass_fd.each_line do |pass_from_file|
121
pass_from_file.chomp!
122
yield Metasploit::Framework::Credential.new(private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
123
end
124
pass_fd.seek(0)
125
end
126
additional_privates.each do |add_private|
127
yield Metasploit::Framework::Credential.new(private: add_private, realm: realm, private_type: private_type(add_private))
128
end
129
130
ensure
131
pass_fd.close if pass_fd && !pass_fd.closed?
132
end
133
134
# Returns true when #each will have no results to iterate
135
#
136
# @return [Boolean]
137
def empty?
138
prepended_creds.empty? && !has_privates?
139
end
140
141
# Returns true when a filter is defined
142
#
143
# @return [Boolean]
144
def filtered?
145
!self.filter.nil?
146
end
147
148
# Returns true when there are any private values set
149
#
150
# @return [Boolean]
151
def has_privates?
152
password.present? || pass_file.present? || !additional_privates.empty? || blank_passwords || nil_passwords
153
end
154
155
alias each each_filtered
156
157
protected
158
159
# Analyze a private value to determine its type by checking it against a known list of regular expressions
160
#
161
# @param [String] private The string to analyze
162
# @return [Symbol]
163
def private_type(private)
164
if private =~ /[0-9a-f]{32}:[0-9a-f]{32}/
165
:ntlm_hash
166
elsif private =~ /^md5([a-f0-9]{32})$/
167
:postgres_md5
168
else
169
:password
170
end
171
end
172
end
173
174
class CredentialCollection < PrivateCredentialCollection
175
# @!attribute password_spray
176
# Whether password spray is enabled. When true, each password is tried against each username first.
177
# Otherwise the default bruteforce logic will attempt all passwords against the first user, before
178
# continuing to the next user
179
#
180
# @return [Boolean]
181
attr_accessor :password_spray
182
183
# @!attribute additional_publics
184
# Additional public values that should be tried
185
#
186
# @return [Array<String>]
187
attr_accessor :additional_publics
188
189
# @!attribute user_as_pass
190
# Whether each username should be tried as a password for that user
191
# @return [Boolean]
192
attr_accessor :user_as_pass
193
194
# @!attribute user_file
195
# Path to a file containing usernames, one per line
196
# @return [String]
197
attr_accessor :user_file
198
199
# @!attribute username
200
# The username that should be tried
201
# @return [String]
202
attr_accessor :username
203
204
# @!attribute userpass_file
205
# Path to a file containing usernames and passwords separated by a space,
206
# one pair per line
207
# @return [String]
208
attr_accessor :userpass_file
209
210
# @!attribute anonymous_login
211
# Whether to attempt an anonymous login (blank user/pass)
212
# @return [Boolean]
213
attr_accessor :anonymous_login
214
215
# @option opts [Boolean] :blank_passwords See {#blank_passwords}
216
# @option opts [String] :pass_file See {#pass_file}
217
# @option opts [String] :password See {#password}
218
# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}
219
# @option opts [Boolean] :user_as_pass See {#user_as_pass}
220
# @option opts [String] :user_file See {#user_file}
221
# @option opts [String] :username See {#username}
222
# @option opts [String] :userpass_file See {#userpass_file}
223
def initialize(opts = {})
224
super
225
self.additional_publics ||= []
226
end
227
228
# Adds a string as an additional public credential
229
# to be combined in the collection.
230
#
231
# @param [String] public_str The string to use as a public credential
232
# @return [void]
233
def add_public(public_str='')
234
additional_publics << public_str
235
end
236
237
# Combines all the provided credential sources into a stream of {Credential}
238
# objects, yielding them one at a time
239
#
240
# @yieldparam credential [Metasploit::Framework::Credential]
241
# @return [void]
242
def each_filtered
243
each_unfiltered do |credential|
244
next unless self.filter.nil? || self.filter.call(credential)
245
246
yield credential
247
end
248
end
249
250
alias each each_filtered
251
252
def each_unfiltered(&block)
253
prepended_creds.each { |c| yield c }
254
255
if anonymous_login
256
yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)
257
end
258
259
if password_spray
260
each_unfiltered_password_first(&block)
261
else
262
each_unfiltered_username_first(&block)
263
end
264
end
265
266
# When password spraying is enabled, do first passwords then usernames
267
# i.e.
268
# username1:password1
269
# username2:password1
270
# username3:password1
271
# ...
272
# username1:password2
273
# username2:password2
274
# username3:password2
275
# ...
276
# @yieldparam credential [Metasploit::Framework::Credential]
277
# @return [void]
278
def each_unfiltered_password_first
279
if nil_passwords
280
each_username do |username|
281
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)
282
end
283
end
284
285
if password.present?
286
each_username do |username|
287
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
288
end
289
end
290
291
if user_as_pass
292
each_username do |username|
293
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
294
end
295
end
296
297
if blank_passwords
298
each_username do |username|
299
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
300
end
301
end
302
303
if pass_file.present?
304
File.open(pass_file, 'r:binary') do |pass_fd|
305
pass_fd.each_line do |pass_from_file|
306
pass_from_file.chomp!
307
308
each_username do |username|
309
yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
310
end
311
end
312
end
313
end
314
315
each_user_pass_from_userpass_file do |user, pass|
316
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
317
end
318
319
additional_privates.each do |add_private|
320
each_username do |username|
321
yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
322
end
323
end
324
end
325
326
# Iterates over all possible usernames
327
def each_username
328
if username.present?
329
yield username
330
end
331
332
if user_file.present?
333
File.open(user_file, 'r:binary') do |user_fd|
334
user_fd.each_line do |user_from_file|
335
user_from_file.chomp!
336
yield user_from_file
337
end
338
user_fd.seek(0)
339
end
340
end
341
342
additional_publics.each do |add_public|
343
yield add_public
344
end
345
end
346
347
# When password spraying is not enabled, do first usernames then passwords
348
# i.e.
349
# username1:password1
350
# username1:password2
351
# username1:password3
352
# ...
353
# username2:password1
354
# username2:password2
355
# username2:password3
356
# @yieldparam credential [Metasploit::Framework::Credential]
357
# @return [void]
358
def each_unfiltered_username_first
359
if username.present?
360
each_password(username) do |password, private_type|
361
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type)
362
end
363
end
364
365
if user_file.present?
366
File.open(user_file, 'r:binary') do |user_fd|
367
user_fd.each_line do |user_from_file|
368
user_from_file.chomp!
369
each_password(user_from_file) do |password, private_type|
370
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type)
371
end
372
end
373
end
374
end
375
376
each_user_pass_from_userpass_file do |user, pass|
377
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
378
end
379
380
additional_publics.each do |add_public|
381
each_password(add_public) do |password, private_type|
382
yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type)
383
end
384
end
385
end
386
387
# Iterates over all possible passwords
388
def each_password(user)
389
if nil_passwords
390
yield [nil, :password]
391
end
392
393
if password.present?
394
yield [password, private_type(password)]
395
end
396
397
if user_as_pass
398
yield [user, :password]
399
end
400
401
if blank_passwords
402
yield ["", :password]
403
end
404
405
if pass_file
406
File.open(pass_file, 'r:binary') do |pass_fd|
407
pass_fd.each_line do |pass_from_file|
408
pass_from_file.chomp!
409
yield [pass_from_file, private_type(pass_from_file)]
410
end
411
pass_fd.seek(0)
412
end
413
end
414
415
additional_privates.each do |add_private|
416
yield [add_private, private_type(add_private)]
417
end
418
end
419
420
# Iterates on userpass file if present
421
def each_user_pass_from_userpass_file
422
return unless userpass_file.present?
423
424
File.open(userpass_file, 'r:binary') do |userpass_fd|
425
userpass_fd.each_line do |line|
426
user, pass = line.split(" ", 2)
427
pass = pass.blank? ? '' : pass.chomp!
428
429
yield [user, pass]
430
end
431
end
432
end
433
434
# Returns true when #each will have no results to iterate
435
#
436
# @return [Boolean]
437
def empty?
438
prepended_creds.empty? && !has_users? && !anonymous_login || (has_users? && !has_privates?)
439
end
440
441
# Returns true when there are any user values set
442
#
443
# @return [Boolean]
444
def has_users?
445
username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty?
446
end
447
448
# Returns true when there are any private values set
449
#
450
# @return [Boolean]
451
def has_privates?
452
super || userpass_file.present? || user_as_pass
453
end
454
455
end
456
end
457
458