Skip to content

Commit 2f27fe4

Browse files
Merge pull request #19653 from Mathiou04/fix_bugs_in_credentials_collection_enumerator_and_refacto
Fixing multiple bugs in credential generation + refactoring
2 parents 704a8f1 + 736d89b commit 2f27fe4

File tree

3 files changed

+403
-264
lines changed

3 files changed

+403
-264
lines changed

lib/metasploit/framework/credential_collection.rb

Lines changed: 99 additions & 190 deletions
Original file line numberDiff line numberDiff line change
@@ -212,23 +212,6 @@ class CredentialCollection < PrivateCredentialCollection
212212
# @return [Boolean]
213213
attr_accessor :anonymous_login
214214

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-
232215
# @option opts [Boolean] :blank_passwords See {#blank_passwords}
233216
# @option opts [String] :pass_file See {#pass_file}
234217
# @option opts [String] :password See {#password}
@@ -257,29 +240,29 @@ def add_public(public_str='')
257240
# @yieldparam credential [Metasploit::Framework::Credential]
258241
# @return [void]
259242
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)
243+
each_unfiltered do |credential|
244+
next unless self.filter.nil? || self.filter.call(credential)
275245

276-
yield credential
277-
end
246+
yield credential
278247
end
279248
end
280249

281250
alias each each_filtered
282251

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+
283266
# When password spraying is enabled, do first passwords then usernames
284267
# i.e.
285268
# username1:password1
@@ -293,117 +276,72 @@ def each_filtered
293276
# @yieldparam credential [Metasploit::Framework::Credential]
294277
# @return [void]
295278
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)
279+
if nil_passwords
280+
each_username do |username|
281+
yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)
313282
end
314283
end
315284

316285
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?
286+
each_username do |username|
321287
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))
322288
end
323-
if user_as_pass
289+
end
290+
291+
if user_as_pass
292+
each_username do |username|
324293
yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)
325294
end
326-
if blank_passwords
295+
end
296+
297+
if blank_passwords
298+
each_username do |username|
327299
yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)
328300
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
336301
end
337302

338303
if pass_file.present?
339304
File.open(pass_file, 'r:binary') do |pass_fd|
340305
pass_fd.each_line do |pass_from_file|
341306
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
346307

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))
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))
350310
end
351-
user_fd.seek(0)
352311
end
353312
end
354313
end
355314

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
315+
each_user_pass_from_userpass_file do |user, pass|
316+
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
368317
end
369318

370319
additional_privates.each do |add_private|
371-
if username.present?
320+
each_username do |username|
372321
yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))
373322
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)
379323
end
324+
end
380325

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
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|
395334
user_fd.each_line do |user_from_file|
396335
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))
336+
yield user_from_file
398337
end
399338
user_fd.seek(0)
400339
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
404340
end
405-
ensure
406-
user_fd.close if user_fd && !user_fd.closed?
341+
342+
additional_publics.each do |add_public|
343+
yield add_public
344+
end
407345
end
408346

409347
# When password spraying is not enabled, do first usernames then passwords
@@ -418,108 +356,79 @@ def each_unfiltered_password_first
418356
# @yieldparam credential [Metasploit::Framework::Credential]
419357
# @return [void]
420358
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-
431359
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))
360+
each_password(username) do |password, private_type|
361+
yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type)
453362
end
454363
end
455364

456365
if user_file.present?
457366
File.open(user_file, 'r:binary') do |user_fd|
458367
user_fd.each_line do |user_from_file|
459368
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))
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)
481371
end
482372
end
483373
end
484374
end
485375

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
376+
each_user_pass_from_userpass_file do |user, pass|
377+
yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)
498378
end
499379

500380
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) )
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)
503383
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
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|
511407
pass_fd.each_line do |pass_from_file|
512408
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))
409+
yield [pass_from_file, private_type(pass_from_file)]
514410
end
515411
pass_fd.seek(0)
516412
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))
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]
519430
end
520431
end
521-
ensure
522-
pass_fd.close if pass_fd && !pass_fd.closed?
523432
end
524433

525434
# Returns true when #each will have no results to iterate
@@ -533,14 +442,14 @@ def empty?
533442
#
534443
# @return [Boolean]
535444
def has_users?
536-
username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? || !!ignore_public
445+
username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty?
537446
end
538447

539448
# Returns true when there are any private values set
540449
#
541450
# @return [Boolean]
542451
def has_privates?
543-
super || userpass_file.present? || user_as_pass || !!ignore_private
452+
super || userpass_file.present? || user_as_pass
544453
end
545454

546455
end

0 commit comments

Comments
 (0)