Path: blob/master/lib/metasploit/framework/credential_collection.rb
22040 views
require 'metasploit/framework/credential'12module Metasploit::Framework34class PrivateCredentialCollection5# @!attribute additional_privates6# Additional private values that should be tried7# @return [Array<String>]8attr_accessor :additional_privates910# @!attribute blank_passwords11# Whether each username should be tried with a blank password12# @return [Boolean]13attr_accessor :blank_passwords1415# @!attribute nil_passwords16# Whether each username should be tried with a nil password17# @return [Boolean]18attr_accessor :nil_passwords1920# @!attribute pass_file21# Path to a file containing passwords, one per line22# @return [String]23attr_accessor :pass_file2425# @!attribute password26# The password that should be tried27# @return [String]28attr_accessor :password2930# @!attribute prepended_creds31# List of credentials to be tried before any others32#33# @see #prepend_cred34# @return [Array<Credential>]35attr_accessor :prepended_creds3637# @!attribute realm38# The authentication realm associated with this password39# @return [String]40attr_accessor :realm4142# @!attribute filter43# A block that can be used to filter credential objects44attr_accessor :filter4546# @option opts [Boolean] :nil_passwords See {#nil_passwords}47# @option opts [Boolean] :blank_passwords See {#blank_passwords}48# @option opts [String] :pass_file See {#pass_file}49# @option opts [String] :password See {#password}50# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}51# @option opts [Boolean] :user_as_pass See {#user_as_pass}52# @option opts [String] :user_file See {#user_file}53# @option opts [String] :username See {#username}54# @option opts [String] :userpass_file See {#userpass_file}55# @option opts [String] :usernames_only See {#usernames_only}56def initialize(opts = {})57opts.each do |attribute, value|58public_send("#{attribute}=", value)59end60self.prepended_creds ||= []61self.additional_privates ||= []62self.filter = nil63end6465# Adds a string as an additional private credential66# to be combined in the collection.67#68# @param [String] private_str The string to use as a private credential69# @return [void]70def add_private(private_str='')71additional_privates << private_str72end7374# Add {Credential credentials} that will be yielded by {#each}75#76# @see prepended_creds77# @param [Credential] cred78# @return [self]79def prepend_cred(cred)80prepended_creds.unshift cred81self82end8384# Combines all the provided credential sources into a stream of {Credential}85# objects, yielding them one at a time86#87# @yieldparam credential [Metasploit::Framework::Credential]88# @return [void]89def each_filtered90each_unfiltered do |credential|91next unless self.filter.nil? || self.filter.call(credential)9293yield credential94end95end9697# Combines all the provided credential sources into a stream of {Credential}98# objects, yielding them one at a time99#100# @yieldparam credential [Metasploit::Framework::Credential]101# @return [void]102def each_unfiltered103if pass_file.present?104pass_fd = File.open(pass_file, 'r:binary')105end106107prepended_creds.each { |c| yield c }108109if password.present?110yield Metasploit::Framework::Credential.new(private: password, realm: realm, private_type: private_type(password))111end112if blank_passwords113yield Metasploit::Framework::Credential.new(private: "", realm: realm, private_type: :password)114end115if nil_passwords116yield Metasploit::Framework::Credential.new(private: nil, realm: realm, private_type: :password)117end118if pass_fd119pass_fd.each_line do |pass_from_file|120pass_from_file.chomp!121yield Metasploit::Framework::Credential.new(private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))122end123pass_fd.seek(0)124end125additional_privates.each do |add_private|126yield Metasploit::Framework::Credential.new(private: add_private, realm: realm, private_type: private_type(add_private))127end128129ensure130pass_fd.close if pass_fd && !pass_fd.closed?131end132133# Returns true when #each will have no results to iterate134#135# @return [Boolean]136def empty?137prepended_creds.empty? && !has_privates?138end139140# Returns true when a filter is defined141#142# @return [Boolean]143def filtered?144!self.filter.nil?145end146147# Returns true when there are any private values set148#149# @return [Boolean]150def has_privates?151password.present? || pass_file.present? || !additional_privates.empty? || blank_passwords || nil_passwords152end153154alias each each_filtered155156protected157158# Analyze a private value to determine its type by checking it against a known list of regular expressions159#160# @param [String] private The string to analyze161# @return [Symbol]162def private_type(private)163if private =~ /[0-9a-f]{32}:[0-9a-f]{32}/164:ntlm_hash165elsif private =~ /^md5([a-f0-9]{32})$/166:postgres_md5167else168:password169end170end171end172173class CredentialCollection < PrivateCredentialCollection174# @!attribute password_spray175# Whether password spray is enabled. When true, each password is tried against each username first.176# Otherwise the default bruteforce logic will attempt all passwords against the first user, before177# continuing to the next user178#179# @return [Boolean]180attr_accessor :password_spray181182# @!attribute additional_publics183# Additional public values that should be tried184#185# @return [Array<String>]186attr_accessor :additional_publics187188# @!attribute user_as_pass189# Whether each username should be tried as a password for that user190# @return [Boolean]191attr_accessor :user_as_pass192193# @!attribute user_file194# Path to a file containing usernames, one per line195# @return [String]196attr_accessor :user_file197198# @!attribute username199# The username that should be tried200# @return [String]201attr_accessor :username202203# @!attribute userpass_file204# Path to a file containing usernames and passwords separated by a space,205# one pair per line206# @return [String]207attr_accessor :userpass_file208209# @!attribute anonymous_login210# Whether to attempt an anonymous login (blank user/pass)211# @return [Boolean]212attr_accessor :anonymous_login213214# @option opts [Boolean] :blank_passwords See {#blank_passwords}215# @option opts [String] :pass_file See {#pass_file}216# @option opts [String] :password See {#password}217# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}218# @option opts [Boolean] :user_as_pass See {#user_as_pass}219# @option opts [String] :user_file See {#user_file}220# @option opts [String] :username See {#username}221# @option opts [String] :userpass_file See {#userpass_file}222def initialize(opts = {})223super224self.additional_publics ||= []225end226227# Adds a string as an additional public credential228# to be combined in the collection.229#230# @param [String] public_str The string to use as a public credential231# @return [void]232def add_public(public_str='')233additional_publics << public_str234end235236# Combines all the provided credential sources into a stream of {Credential}237# objects, yielding them one at a time238#239# @yieldparam credential [Metasploit::Framework::Credential]240# @return [void]241def each_filtered242each_unfiltered do |credential|243next unless self.filter.nil? || self.filter.call(credential)244245yield credential246end247end248249alias each each_filtered250251def each_unfiltered(&block)252prepended_creds.each { |c| yield c }253254if anonymous_login255yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)256end257258if password_spray259each_unfiltered_password_first(&block)260else261each_unfiltered_username_first(&block)262end263end264265# When password spraying is enabled, do first passwords then usernames266# i.e.267# username1:password1268# username2:password1269# username3:password1270# ...271# username1:password2272# username2:password2273# username3:password2274# ...275# @yieldparam credential [Metasploit::Framework::Credential]276# @return [void]277def each_unfiltered_password_first278if nil_passwords279each_username do |username|280yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)281end282end283284if password.present?285each_username do |username|286yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))287end288end289290if user_as_pass291each_username do |username|292yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)293end294end295296if blank_passwords297each_username do |username|298yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)299end300end301302if pass_file.present?303File.open(pass_file, 'r:binary') do |pass_fd|304pass_fd.each_line do |pass_from_file|305pass_from_file.chomp!306307each_username do |username|308yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))309end310end311end312end313314each_user_pass_from_userpass_file do |user, pass|315yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)316end317318additional_privates.each do |add_private|319each_username do |username|320yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))321end322end323end324325# Iterates over all possible usernames326def each_username327if username.present?328yield username329end330331if user_file.present?332File.open(user_file, 'r:binary') do |user_fd|333user_fd.each_line do |user_from_file|334user_from_file.chomp!335yield user_from_file336end337user_fd.seek(0)338end339end340341additional_publics.each do |add_public|342yield add_public343end344end345346# When password spraying is not enabled, do first usernames then passwords347# i.e.348# username1:password1349# username1:password2350# username1:password3351# ...352# username2:password1353# username2:password2354# username2:password3355# @yieldparam credential [Metasploit::Framework::Credential]356# @return [void]357def each_unfiltered_username_first358if username.present?359each_password(username) do |password, private_type|360yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type)361end362end363364if user_file.present?365File.open(user_file, 'r:binary') do |user_fd|366user_fd.each_line do |user_from_file|367user_from_file.chomp!368each_password(user_from_file) do |password, private_type|369yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type)370end371end372end373end374375each_user_pass_from_userpass_file do |user, pass|376yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)377end378379additional_publics.each do |add_public|380each_password(add_public) do |password, private_type|381yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type)382end383end384end385386# Iterates over all possible passwords387def each_password(user)388if nil_passwords389yield [nil, :password]390end391392if password.present?393yield [password, private_type(password)]394end395396if user_as_pass397yield [user, :password]398end399400if blank_passwords401yield ["", :password]402end403404if pass_file405File.open(pass_file, 'r:binary') do |pass_fd|406pass_fd.each_line do |pass_from_file|407pass_from_file.chomp!408yield [pass_from_file, private_type(pass_from_file)]409end410pass_fd.seek(0)411end412end413414additional_privates.each do |add_private|415yield [add_private, private_type(add_private)]416end417end418419# Iterates on userpass file if present420def each_user_pass_from_userpass_file421return unless userpass_file.present?422423File.open(userpass_file, 'r:binary') do |userpass_fd|424userpass_fd.each_line do |line|425user, pass = line.split(" ", 2)426pass = pass.blank? ? '' : pass.chomp!427428yield [user, pass]429end430end431end432433# Returns true when #each will have no results to iterate434#435# @return [Boolean]436def empty?437prepended_creds.empty? && !has_users? && !anonymous_login || (has_users? && !has_privates?)438end439440# Returns true when there are any user values set441#442# @return [Boolean]443def has_users?444username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty?445end446447# Returns true when there are any private values set448#449# @return [Boolean]450def has_privates?451super || userpass_file.present? || user_as_pass452end453454end455end456457458