Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rapid7
GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/credential_collection.rb
19592 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
# @!attribute ignore_private
216
# Whether to ignore private (password). This is usually set when Kerberos
217
# or Schannel authentication is requested and the credentials are
218
# retrieved from cache or from a file. This attribute should be true in
219
# these scenarios, otherwise validation will fail since the password is not
220
# provided.
221
# @return [Boolean]
222
attr_accessor :ignore_private
223
224
# @!attribute ignore_public
225
# Whether to ignore public (username). This is usually set when Schannel
226
# authentication is requested and the credentials are retrieved from a
227
# file (certificate). This attribute should be true in this case,
228
# otherwise validation will fail since the password is not provided.
229
# @return [Boolean]
230
attr_accessor :ignore_public
231
232
# @option opts [Boolean] :blank_passwords See {#blank_passwords}
233
# @option opts [String] :pass_file See {#pass_file}
234
# @option opts [String] :password See {#password}
235
# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}
236
# @option opts [Boolean] :user_as_pass See {#user_as_pass}
237
# @option opts [String] :user_file See {#user_file}
238
# @option opts [String] :username See {#username}
239
# @option opts [String] :userpass_file See {#userpass_file}
240
def initialize(opts = {})
241
super
242
self.additional_publics ||= []
243
end
244
245
# Adds a string as an additional public credential
246
# to be combined in the collection.
247
#
248
# @param [String] public_str The string to use as a public credential
249
# @return [void]
250
def add_public(public_str='')
251
additional_publics << public_str
252
end
253
254
# Combines all the provided credential sources into a stream of {Credential}
255
# objects, yielding them one at a time
256
#
257
# @yieldparam credential [Metasploit::Framework::Credential]
258
# @return [void]
259
def each_filtered
260
if ignore_private
261
if ignore_public
262
yield Metasploit::Framework::Credential.new(public: nil, private: nil, realm: realm)
263
else
264
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm)
265
end
266
elsif password_spray
267
each_unfiltered_password_first do |credential|
268
next unless self.filter.nil? || self.filter.call(credential)
269
270
yield credential
271
end
272
else
273
each_unfiltered_username_first do |credential|
274
next unless self.filter.nil? || self.filter.call(credential)
275
276
yield credential
277
end
278
end
279
end
280
281
alias each each_filtered
282
283
# When password spraying is enabled, do first passwords then usernames
284
# i.e.
285
# username1:password1
286
# username2:password1
287
# username3:password1
288
# ...
289
# username1:password2
290
# username2:password2
291
# username3:password2
292
# ...
293
# @yieldparam credential [Metasploit::Framework::Credential]
294
# @return [void]
295
def each_unfiltered_password_first
296
if user_file.present?
297
user_fd = File.open(user_file, 'r:binary')
298
end
299
300
prepended_creds.each { |c| yield c }
301
302
if anonymous_login
303
yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)
304
end
305
306
if user_as_pass
307
if user_fd
308
user_fd.each_line do |user_from_file|
309
user_from_file.chomp!
310
yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: private_type(password))
311
end
312
user_fd.seek(0)
313
end
314
end
315
316
if password.present?
317
if nil_passwords
318
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)
319
end
320
if username.present?
321
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
322
end
323
if user_as_pass
324
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
325
end
326
if blank_passwords
327
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
328
end
329
if user_fd
330
user_fd.each_line do |user_from_file|
331
user_from_file.chomp!
332
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password))
333
end
334
user_fd.seek(0)
335
end
336
end
337
338
if pass_file.present?
339
File.open(pass_file, 'r:binary') do |pass_fd|
340
pass_fd.each_line do |pass_from_file|
341
pass_from_file.chomp!
342
if username.present?
343
yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: :password)
344
end
345
next unless user_fd
346
347
user_fd.each_line do |user_from_file|
348
user_from_file.chomp!
349
yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
350
end
351
user_fd.seek(0)
352
end
353
end
354
end
355
356
if userpass_file.present?
357
File.open(userpass_file, 'r:binary') do |userpass_fd|
358
userpass_fd.each_line do |line|
359
user, pass = line.split(" ", 2)
360
if pass.blank?
361
pass = ''
362
else
363
pass.chomp!
364
end
365
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
366
end
367
end
368
end
369
370
additional_privates.each do |add_private|
371
if username.present?
372
yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
373
end
374
user_fd.each_line do |user_from_file|
375
user_from_file.chomp!
376
yield Metasploit::Framework::Credential.new(public: user_from_file, private: add_private, realm: realm, private_type: private_type(add_private))
377
end
378
user_fd.seek(0)
379
end
380
381
additional_publics.each do |add_public|
382
if password.present?
383
yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type(password) )
384
end
385
if user_as_pass
386
yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: :password)
387
end
388
if blank_passwords
389
yield Metasploit::Framework::Credential.new(public: add_public, private: "", realm: realm, private_type: :password)
390
end
391
if nil_passwords
392
yield Metasploit::Framework::Credential.new(public: add_public, private: nil, realm: realm, private_type: :password)
393
end
394
if user_fd
395
user_fd.each_line do |user_from_file|
396
user_from_file.chomp!
397
yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: private_type(user_from_file))
398
end
399
user_fd.seek(0)
400
end
401
additional_privates.each do |add_private|
402
yield Metasploit::Framework::Credential.new(public: add_public, private: add_private, realm: realm, private_type: private_type(add_private))
403
end
404
end
405
ensure
406
user_fd.close if user_fd && !user_fd.closed?
407
end
408
409
# When password spraying is not enabled, do first usernames then passwords
410
# i.e.
411
# username1:password1
412
# username1:password2
413
# username1:password3
414
# ...
415
# username2:password1
416
# username2:password2
417
# username2:password3
418
# @yieldparam credential [Metasploit::Framework::Credential]
419
# @return [void]
420
def each_unfiltered_username_first
421
if pass_file.present?
422
pass_fd = File.open(pass_file, 'r:binary')
423
end
424
425
prepended_creds.each { |c| yield c }
426
427
if anonymous_login
428
yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)
429
end
430
431
if username.present?
432
if nil_passwords
433
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)
434
end
435
if password.present?
436
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
437
end
438
if user_as_pass
439
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
440
end
441
if blank_passwords
442
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
443
end
444
if pass_fd
445
pass_fd.each_line do |pass_from_file|
446
pass_from_file.chomp!
447
yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
448
end
449
pass_fd.seek(0)
450
end
451
additional_privates.each do |add_private|
452
yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
453
end
454
end
455
456
if user_file.present?
457
File.open(user_file, 'r:binary') do |user_fd|
458
user_fd.each_line do |user_from_file|
459
user_from_file.chomp!
460
if nil_passwords
461
yield Metasploit::Framework::Credential.new(public: user_from_file, private: nil, realm: realm, private_type: :password)
462
end
463
if password.present?
464
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password) )
465
end
466
if user_as_pass
467
yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: :password)
468
end
469
if blank_passwords
470
yield Metasploit::Framework::Credential.new(public: user_from_file, private: "", realm: realm, private_type: :password)
471
end
472
if pass_fd
473
pass_fd.each_line do |pass_from_file|
474
pass_from_file.chomp!
475
yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
476
end
477
pass_fd.seek(0)
478
end
479
additional_privates.each do |add_private|
480
yield Metasploit::Framework::Credential.new(public: user_from_file, private: add_private, realm: realm, private_type: private_type(add_private))
481
end
482
end
483
end
484
end
485
486
if userpass_file.present?
487
File.open(userpass_file, 'r:binary') do |userpass_fd|
488
userpass_fd.each_line do |line|
489
user, pass = line.split(" ", 2)
490
if pass.blank?
491
pass = ''
492
else
493
pass.chomp!
494
end
495
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
496
end
497
end
498
end
499
500
additional_publics.each do |add_public|
501
if password.present?
502
yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type(password) )
503
end
504
if user_as_pass
505
yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: :password)
506
end
507
if blank_passwords
508
yield Metasploit::Framework::Credential.new(public: add_public, private: "", realm: realm, private_type: :password)
509
end
510
if pass_fd
511
pass_fd.each_line do |pass_from_file|
512
pass_from_file.chomp!
513
yield Metasploit::Framework::Credential.new(public: add_public, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
514
end
515
pass_fd.seek(0)
516
end
517
additional_privates.each do |add_private|
518
yield Metasploit::Framework::Credential.new(public: add_public, private: add_private, realm: realm, private_type: private_type(add_private))
519
end
520
end
521
ensure
522
pass_fd.close if pass_fd && !pass_fd.closed?
523
end
524
525
# Returns true when #each will have no results to iterate
526
#
527
# @return [Boolean]
528
def empty?
529
prepended_creds.empty? && !has_users? && !anonymous_login || (has_users? && !has_privates?)
530
end
531
532
# Returns true when there are any user values set
533
#
534
# @return [Boolean]
535
def has_users?
536
username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? || !!ignore_public
537
end
538
539
# Returns true when there are any private values set
540
#
541
# @return [Boolean]
542
def has_privates?
543
super || userpass_file.present? || user_as_pass || !!ignore_private
544
end
545
546
end
547
end
548
549