CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutSign UpSign In
rapid7

Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.

GitHub Repository: rapid7/metasploit-framework
Path: blob/master/lib/metasploit/framework/credential_collection.rb
Views: 11779
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
if password_spray
244
each_unfiltered_password_first do |credential|
245
next unless self.filter.nil? || self.filter.call(credential)
246
247
yield credential
248
end
249
else
250
each_unfiltered_username_first do |credential|
251
next unless self.filter.nil? || self.filter.call(credential)
252
253
yield credential
254
end
255
end
256
end
257
258
alias each each_filtered
259
260
# When password spraying is enabled, do first passwords then usernames
261
# i.e.
262
# username1:password1
263
# username2:password1
264
# username3:password1
265
# ...
266
# username1:password2
267
# username2:password2
268
# username3:password2
269
# ...
270
# @yieldparam credential [Metasploit::Framework::Credential]
271
# @return [void]
272
def each_unfiltered_password_first
273
if user_file.present?
274
user_fd = File.open(user_file, 'r:binary')
275
end
276
277
prepended_creds.each { |c| yield c }
278
279
if anonymous_login
280
yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)
281
end
282
283
if user_as_pass
284
if user_fd
285
user_fd.each_line do |user_from_file|
286
user_from_file.chomp!
287
yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: private_type(password))
288
end
289
user_fd.seek(0)
290
end
291
end
292
293
if password.present?
294
if nil_passwords
295
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)
296
end
297
if username.present?
298
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
299
end
300
if user_as_pass
301
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
302
end
303
if blank_passwords
304
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
305
end
306
if user_fd
307
user_fd.each_line do |user_from_file|
308
user_from_file.chomp!
309
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password))
310
end
311
user_fd.seek(0)
312
end
313
end
314
315
if pass_file.present?
316
File.open(pass_file, 'r:binary') do |pass_fd|
317
pass_fd.each_line do |pass_from_file|
318
pass_from_file.chomp!
319
if username.present?
320
yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: :password)
321
end
322
next unless user_fd
323
324
user_fd.each_line do |user_from_file|
325
user_from_file.chomp!
326
yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
327
end
328
user_fd.seek(0)
329
end
330
end
331
end
332
333
if userpass_file.present?
334
File.open(userpass_file, 'r:binary') do |userpass_fd|
335
userpass_fd.each_line do |line|
336
user, pass = line.split(" ", 2)
337
if pass.blank?
338
pass = ''
339
else
340
pass.chomp!
341
end
342
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
343
end
344
end
345
end
346
347
additional_privates.each do |add_private|
348
if username.present?
349
yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
350
end
351
user_fd.each_line do |user_from_file|
352
user_from_file.chomp!
353
yield Metasploit::Framework::Credential.new(public: user_from_file, private: add_private, realm: realm, private_type: private_type(add_private))
354
end
355
user_fd.seek(0)
356
end
357
358
additional_publics.each do |add_public|
359
if password.present?
360
yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type(password) )
361
end
362
if user_as_pass
363
yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: :password)
364
end
365
if blank_passwords
366
yield Metasploit::Framework::Credential.new(public: add_public, private: "", realm: realm, private_type: :password)
367
end
368
if nil_passwords
369
yield Metasploit::Framework::Credential.new(public: add_public, private: nil, realm: realm, private_type: :password)
370
end
371
if user_fd
372
user_fd.each_line do |user_from_file|
373
user_from_file.chomp!
374
yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: private_type(user_from_file))
375
end
376
user_fd.seek(0)
377
end
378
additional_privates.each do |add_private|
379
yield Metasploit::Framework::Credential.new(public: add_public, private: add_private, realm: realm, private_type: private_type(add_private))
380
end
381
end
382
ensure
383
user_fd.close if user_fd && !user_fd.closed?
384
end
385
386
# When password spraying is not enabled, do first usernames then passwords
387
# i.e.
388
# username1:password1
389
# username1:password2
390
# username1:password3
391
# ...
392
# username2:password1
393
# username2:password2
394
# username2:password3
395
# @yieldparam credential [Metasploit::Framework::Credential]
396
# @return [void]
397
def each_unfiltered_username_first
398
if pass_file.present?
399
pass_fd = File.open(pass_file, 'r:binary')
400
end
401
402
prepended_creds.each { |c| yield c }
403
404
if anonymous_login
405
yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)
406
end
407
408
if username.present?
409
if nil_passwords
410
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)
411
end
412
if password.present?
413
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
414
end
415
if user_as_pass
416
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
417
end
418
if blank_passwords
419
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
420
end
421
if pass_fd
422
pass_fd.each_line do |pass_from_file|
423
pass_from_file.chomp!
424
yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
425
end
426
pass_fd.seek(0)
427
end
428
additional_privates.each do |add_private|
429
yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
430
end
431
end
432
433
if user_file.present?
434
File.open(user_file, 'r:binary') do |user_fd|
435
user_fd.each_line do |user_from_file|
436
user_from_file.chomp!
437
if nil_passwords
438
yield Metasploit::Framework::Credential.new(public: user_from_file, private: nil, realm: realm, private_type: :password)
439
end
440
if password.present?
441
yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password) )
442
end
443
if user_as_pass
444
yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: :password)
445
end
446
if blank_passwords
447
yield Metasploit::Framework::Credential.new(public: user_from_file, private: "", realm: realm, private_type: :password)
448
end
449
if pass_fd
450
pass_fd.each_line do |pass_from_file|
451
pass_from_file.chomp!
452
yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
453
end
454
pass_fd.seek(0)
455
end
456
additional_privates.each do |add_private|
457
yield Metasploit::Framework::Credential.new(public: user_from_file, private: add_private, realm: realm, private_type: private_type(add_private))
458
end
459
end
460
end
461
end
462
463
if userpass_file.present?
464
File.open(userpass_file, 'r:binary') do |userpass_fd|
465
userpass_fd.each_line do |line|
466
user, pass = line.split(" ", 2)
467
if pass.blank?
468
pass = ''
469
else
470
pass.chomp!
471
end
472
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
473
end
474
end
475
end
476
477
additional_publics.each do |add_public|
478
if password.present?
479
yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type(password) )
480
end
481
if user_as_pass
482
yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: :password)
483
end
484
if blank_passwords
485
yield Metasploit::Framework::Credential.new(public: add_public, private: "", realm: realm, private_type: :password)
486
end
487
if pass_fd
488
pass_fd.each_line do |pass_from_file|
489
pass_from_file.chomp!
490
yield Metasploit::Framework::Credential.new(public: add_public, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))
491
end
492
pass_fd.seek(0)
493
end
494
additional_privates.each do |add_private|
495
yield Metasploit::Framework::Credential.new(public: add_public, private: add_private, realm: realm, private_type: private_type(add_private))
496
end
497
end
498
ensure
499
pass_fd.close if pass_fd && !pass_fd.closed?
500
end
501
502
# Returns true when #each will have no results to iterate
503
#
504
# @return [Boolean]
505
def empty?
506
prepended_creds.empty? && !has_users? && !anonymous_login || (has_users? && !has_privates?)
507
end
508
509
# Returns true when there are any user values set
510
#
511
# @return [Boolean]
512
def has_users?
513
username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty?
514
end
515
516
# Returns true when there are any private values set
517
#
518
# @return [Boolean]
519
def has_privates?
520
super || userpass_file.present? || user_as_pass
521
end
522
523
end
524
end
525
526