Path: blob/master/lib/metasploit/framework/credential_collection.rb
19592 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# @!attribute ignore_private215# Whether to ignore private (password). This is usually set when Kerberos216# or Schannel authentication is requested and the credentials are217# retrieved from cache or from a file. This attribute should be true in218# these scenarios, otherwise validation will fail since the password is not219# provided.220# @return [Boolean]221attr_accessor :ignore_private222223# @!attribute ignore_public224# Whether to ignore public (username). This is usually set when Schannel225# authentication is requested and the credentials are retrieved from a226# file (certificate). This attribute should be true in this case,227# otherwise validation will fail since the password is not provided.228# @return [Boolean]229attr_accessor :ignore_public230231# @option opts [Boolean] :blank_passwords See {#blank_passwords}232# @option opts [String] :pass_file See {#pass_file}233# @option opts [String] :password See {#password}234# @option opts [Array<Credential>] :prepended_creds ([]) See {#prepended_creds}235# @option opts [Boolean] :user_as_pass See {#user_as_pass}236# @option opts [String] :user_file See {#user_file}237# @option opts [String] :username See {#username}238# @option opts [String] :userpass_file See {#userpass_file}239def initialize(opts = {})240super241self.additional_publics ||= []242end243244# Adds a string as an additional public credential245# to be combined in the collection.246#247# @param [String] public_str The string to use as a public credential248# @return [void]249def add_public(public_str='')250additional_publics << public_str251end252253# Combines all the provided credential sources into a stream of {Credential}254# objects, yielding them one at a time255#256# @yieldparam credential [Metasploit::Framework::Credential]257# @return [void]258def each_filtered259if ignore_private260if ignore_public261yield Metasploit::Framework::Credential.new(public: nil, private: nil, realm: realm)262else263yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm)264end265elsif password_spray266each_unfiltered_password_first do |credential|267next unless self.filter.nil? || self.filter.call(credential)268269yield credential270end271else272each_unfiltered_username_first do |credential|273next unless self.filter.nil? || self.filter.call(credential)274275yield credential276end277end278end279280alias each each_filtered281282# When password spraying is enabled, do first passwords then usernames283# i.e.284# username1:password1285# username2:password1286# username3:password1287# ...288# username1:password2289# username2:password2290# username3:password2291# ...292# @yieldparam credential [Metasploit::Framework::Credential]293# @return [void]294def each_unfiltered_password_first295if user_file.present?296user_fd = File.open(user_file, 'r:binary')297end298299prepended_creds.each { |c| yield c }300301if anonymous_login302yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)303end304305if user_as_pass306if user_fd307user_fd.each_line do |user_from_file|308user_from_file.chomp!309yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: private_type(password))310end311user_fd.seek(0)312end313end314315if password.present?316if nil_passwords317yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)318end319if username.present?320yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))321end322if user_as_pass323yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)324end325if blank_passwords326yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)327end328if user_fd329user_fd.each_line do |user_from_file|330user_from_file.chomp!331yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password))332end333user_fd.seek(0)334end335end336337if pass_file.present?338File.open(pass_file, 'r:binary') do |pass_fd|339pass_fd.each_line do |pass_from_file|340pass_from_file.chomp!341if username.present?342yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: :password)343end344next unless user_fd345346user_fd.each_line do |user_from_file|347user_from_file.chomp!348yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))349end350user_fd.seek(0)351end352end353end354355if userpass_file.present?356File.open(userpass_file, 'r:binary') do |userpass_fd|357userpass_fd.each_line do |line|358user, pass = line.split(" ", 2)359if pass.blank?360pass = ''361else362pass.chomp!363end364yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)365end366end367end368369additional_privates.each do |add_private|370if username.present?371yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))372end373user_fd.each_line do |user_from_file|374user_from_file.chomp!375yield Metasploit::Framework::Credential.new(public: user_from_file, private: add_private, realm: realm, private_type: private_type(add_private))376end377user_fd.seek(0)378end379380additional_publics.each do |add_public|381if password.present?382yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type(password) )383end384if user_as_pass385yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: :password)386end387if blank_passwords388yield Metasploit::Framework::Credential.new(public: add_public, private: "", realm: realm, private_type: :password)389end390if nil_passwords391yield Metasploit::Framework::Credential.new(public: add_public, private: nil, realm: realm, private_type: :password)392end393if user_fd394user_fd.each_line do |user_from_file|395user_from_file.chomp!396yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: private_type(user_from_file))397end398user_fd.seek(0)399end400additional_privates.each do |add_private|401yield Metasploit::Framework::Credential.new(public: add_public, private: add_private, realm: realm, private_type: private_type(add_private))402end403end404ensure405user_fd.close if user_fd && !user_fd.closed?406end407408# When password spraying is not enabled, do first usernames then passwords409# i.e.410# username1:password1411# username1:password2412# username1:password3413# ...414# username2:password1415# username2:password2416# username2:password3417# @yieldparam credential [Metasploit::Framework::Credential]418# @return [void]419def each_unfiltered_username_first420if pass_file.present?421pass_fd = File.open(pass_file, 'r:binary')422end423424prepended_creds.each { |c| yield c }425426if anonymous_login427yield Metasploit::Framework::Credential.new(public: '', private: '', realm: realm, private_type: :password)428end429430if username.present?431if nil_passwords432yield Metasploit::Framework::Credential.new(public: username, private: nil, realm: realm, private_type: :password)433end434if password.present?435yield Metasploit::Framework::Credential.new(public: username, private: password, realm: realm, private_type: private_type(password))436end437if user_as_pass438yield Metasploit::Framework::Credential.new(public: username, private: username, realm: realm, private_type: :password)439end440if blank_passwords441yield Metasploit::Framework::Credential.new(public: username, private: "", realm: realm, private_type: :password)442end443if pass_fd444pass_fd.each_line do |pass_from_file|445pass_from_file.chomp!446yield Metasploit::Framework::Credential.new(public: username, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))447end448pass_fd.seek(0)449end450additional_privates.each do |add_private|451yield Metasploit::Framework::Credential.new(public: username, private: add_private, realm: realm, private_type: private_type(add_private))452end453end454455if user_file.present?456File.open(user_file, 'r:binary') do |user_fd|457user_fd.each_line do |user_from_file|458user_from_file.chomp!459if nil_passwords460yield Metasploit::Framework::Credential.new(public: user_from_file, private: nil, realm: realm, private_type: :password)461end462if password.present?463yield Metasploit::Framework::Credential.new(public: user_from_file, private: password, realm: realm, private_type: private_type(password) )464end465if user_as_pass466yield Metasploit::Framework::Credential.new(public: user_from_file, private: user_from_file, realm: realm, private_type: :password)467end468if blank_passwords469yield Metasploit::Framework::Credential.new(public: user_from_file, private: "", realm: realm, private_type: :password)470end471if pass_fd472pass_fd.each_line do |pass_from_file|473pass_from_file.chomp!474yield Metasploit::Framework::Credential.new(public: user_from_file, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))475end476pass_fd.seek(0)477end478additional_privates.each do |add_private|479yield Metasploit::Framework::Credential.new(public: user_from_file, private: add_private, realm: realm, private_type: private_type(add_private))480end481end482end483end484485if userpass_file.present?486File.open(userpass_file, 'r:binary') do |userpass_fd|487userpass_fd.each_line do |line|488user, pass = line.split(" ", 2)489if pass.blank?490pass = ''491else492pass.chomp!493end494yield Metasploit::Framework::Credential.new(public: user, private: pass, realm: realm)495end496end497end498499additional_publics.each do |add_public|500if password.present?501yield Metasploit::Framework::Credential.new(public: add_public, private: password, realm: realm, private_type: private_type(password) )502end503if user_as_pass504yield Metasploit::Framework::Credential.new(public: add_public, private: user_from_file, realm: realm, private_type: :password)505end506if blank_passwords507yield Metasploit::Framework::Credential.new(public: add_public, private: "", realm: realm, private_type: :password)508end509if pass_fd510pass_fd.each_line do |pass_from_file|511pass_from_file.chomp!512yield Metasploit::Framework::Credential.new(public: add_public, private: pass_from_file, realm: realm, private_type: private_type(pass_from_file))513end514pass_fd.seek(0)515end516additional_privates.each do |add_private|517yield Metasploit::Framework::Credential.new(public: add_public, private: add_private, realm: realm, private_type: private_type(add_private))518end519end520ensure521pass_fd.close if pass_fd && !pass_fd.closed?522end523524# Returns true when #each will have no results to iterate525#526# @return [Boolean]527def empty?528prepended_creds.empty? && !has_users? && !anonymous_login || (has_users? && !has_privates?)529end530531# Returns true when there are any user values set532#533# @return [Boolean]534def has_users?535username.present? || user_file.present? || userpass_file.present? || !additional_publics.empty? || !!ignore_public536end537538# Returns true when there are any private values set539#540# @return [Boolean]541def has_privates?542super || userpass_file.present? || user_as_pass || !!ignore_private543end544545end546end547548549