Module: Msf::Auxiliary::AuthBrute
- Included in:
- CNPILOT, EPMP, Exploit::Remote::Kerberos::AuthBrute
- Defined in:
- lib/msf/core/auxiliary/auth_brute.rb
Overview
This module provides methods for brute forcing authentication
Instance Method Summary collapse
-
#adjust_credentials_by_max_user(credentials) ⇒ Object
Takes a credentials array, and returns just the first X involving a particular user.
-
#build_brute_message(host_ip, host_port, proto, msg) ⇒ Object
Depending on the non-nil elements, build up a standardized auth_brute message.
-
#build_credential_collection(opts) ⇒ Metasploit::Framework::CredentialCollection
Build a new CredentialCollection instance configured based on the datastore options.
-
#build_credentials_array ⇒ Object
If the user passed a memory location for credential gen, assume that that's precisely what's desired ā no other transforms or additions or uniqueness should be done.
-
#cleanup_files ⇒ Object
This method deletes the dictionary files if requested.
- #combine_users_and_passwords(user_array, pass_array) ⇒ Object
- #counters_expired?(this_service, credentials) ⇒ Boolean
-
#each_ntlm_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'ntlm_hash'.
-
#each_password_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'password'.
-
#each_ssh_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'ssh_key'.
-
#each_user_pass(noconn = false, &block) ⇒ Object
Checks all three files for usernames and passwords, and combines them into one credential list to apply against the supplied block.
-
#each_username_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'nil'.
- #extract_word_pair(wordfile) ⇒ Object
- #extract_word_pair_from_memory(memloc) ⇒ Object
- #extract_words(wordfile) ⇒ Object
- #gen_blank_passwords(user_array, cred_array) ⇒ Object
- #gen_user_as_password(user_array, cred_array) ⇒ Object
- #get_object_from_memory_location(memloc) ⇒ Object
- #initialize(info = {}) ⇒ Object
-
#initialize_class_variables(this_service, credentials) ⇒ Object
Class variables to track credential use.
- #just_uniq_passwords(credentials) ⇒ Object
- #just_uniq_users(credentials) ⇒ Object
- #load_password_vars(credentials = nil) ⇒ Object
- #load_user_vars(credentials = nil) ⇒ Object
- #prepend_chosen_password(pass, cred_array) ⇒ Object
- #prepend_chosen_username(user, cred_array) ⇒ Object
-
#prepend_db_creds? ⇒ TrueClass, FalseClass
Checks whether we should be adding creds from the DB to a CredCollection.
-
#prepend_db_hashes(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing NTLMHashes from the database.
-
#prepend_db_keys(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing SSHKeys from the database.
-
#prepend_db_passwords(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing Password Credentials from the database.
-
#prepend_db_usernames(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing Usernames from the database.
-
#print_brute(opts = {}) ⇒ Object
Provides a consistent way to display messages about AuthBrute-mixed modules.
-
#process_cred_for_collection(cred_collection, cred) ⇒ Object
Takes a Metasploit::Credential::Core and converts it into a Metasploit::Framework::Credential and processes it into the Metasploit::Framework::CredentialCollection as dictated by the selected datastore options.
-
#proto_from_fullname ⇒ Object
Protocols can nearly always be automatically determined from the name of the module, assuming the name is sensible like ssh_login or smb_auth.
- #setup ⇒ Object
-
#translate_proto_datastores ⇒ Object
Takes protocol-specific username and password fields, and, if present, prefer those over any given USERNAME or PASSWORD.
-
#tried_over_total(ip, port) ⇒ Object
Fun trick: Only prints if we're already in each_user_pass, since only then is @@max_per_service defined.
- #userpass_interval ⇒ Object
- #userpass_sleep_interval ⇒ Object
-
#vprint_brute(opts = {}) ⇒ Object
See #print_brute.
- #vprint_error(msg = '') ⇒ Object (also: #vprint_bad)
- #vprint_good(msg = '') ⇒ Object
- #vprint_status(msg = '') ⇒ Object
Instance Method Details
#adjust_credentials_by_max_user(credentials) ⇒ Object
Takes a credentials array, and returns just the first X involving a particular user.
741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 741 def adjust_credentials_by_max_user(credentials) max = datastore['MaxGuessesPerUser'].to_i.abs if max == 0 new_credentials = credentials else print_brute( :level => :vstatus, :msg => "Adjusting credentials by MaxGuessesPerUser (#{max})" ) user_count = {} new_credentials = [] credentials.each do |u,p| user_count[u] ||= 0 user_count[u] += 1 next if user_count[u] > max new_credentials << [u,p] end end return new_credentials end |
#build_brute_message(host_ip, host_port, proto, msg) ⇒ Object
Depending on the non-nil elements, build up a standardized auth_brute message.
716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 716 def (host_ip,host_port,proto,msg) ip = host_ip.to_s.strip if host_ip port = host_port.to_s.strip if host_port = nil old_msg = msg.to_s.strip msg_regex = /(#{ip})(:#{port})?(\s*-?\s*)(#{proto.to_s})?(\s*-?\s*)(.*)/i if old_msg.match(msg_regex) = msg.to_s.strip else = '' unless ip.blank? && port.blank? << "#{ip}:#{port}" else << proto || 'Bruteforce' end << " - " progress = tried_over_total(ip,port) << progress if progress << msg.to_s.strip end end |
#build_credential_collection(opts) ⇒ Metasploit::Framework::CredentialCollection
Build a new CredentialCollection instance configured based on the datastore options. Any options passed in will take precedence over the datastore. Usernames and passwords will be prepended to the credential collection if their respective datastore options are configured appropriately. Finally the resulting CredentialCollection will be configured to perform any necessary filtering per the DB_SKIP_EXISTING option.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 57 def build_credential_collection(opts) cred_collection = Metasploit::Framework::CredentialCollection.new({ blank_passwords: datastore['BLANK_PASSWORDS'], pass_file: datastore['PASS_FILE'], user_file: datastore['USER_FILE'], userpass_file: datastore['USERPASS_FILE'], user_as_pass: datastore['USER_AS_PASS'], }.merge(opts)) if framework.db.active cred_collection = prepend_db_usernames(cred_collection) cred_collection = prepend_db_passwords(cred_collection) else ignored = %w{ DB_ALL_CREDS DB_ALL_PASS DB_ALL_USERS }.select { |option| datastore[option] } ignored << 'DB_SKIP_EXISTING' unless datastore['DB_SKIP_EXISTING'].blank? || datastore['DB_SKIP_EXISTING'] == 'none' unless ignored.empty? print_warning("No active DB -- The following option#{ ignored.length == 1 ? '' : 's'} will be ignored: #{ ignored.join(', ') }") end end # only define the filter if any filtering needs to take place unless datastore['DB_SKIP_EXISTING'].blank? || datastore['DB_SKIP_EXISTING'] == 'none' cred_collection.filter = -> (cred) do return true unless datastore['DB_SKIP_EXISTING'] return true unless framework.db.active opts = { workspace: myworkspace.name } opts[:type] = case cred.private_type when :ntlm_hash 'Metasploit::Credential::NTLMHash' when :password 'Metasploit::Credential::Password' when :ssh_key 'Metasploit::Credential::SSHKey' else return true # not a private type that we can filter on end case datastore['DB_SKIP_EXISTING'] when 'user' opts[:user] = cred.public when 'user&realm' opts[:user] = cred.public opts[:realm] = cred.realm else return true end # cred[@public, @private, @private_type[:password], @realm] framework.db.creds(opts).length == 0 end end cred_collection end |
#build_credentials_array ⇒ Object
If the user passed a memory location for credential gen, assume that that's precisely what's desired ā no other transforms or additions or uniqueness should be done. Otherwise, perform the usual alterations.
382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 382 def build_credentials_array credentials = extract_word_pair(datastore['USERPASS_FILE']) translate_proto_datastores() return credentials if datastore['USERPASS_FILE'] =~ /^memory:/ users = load_user_vars(credentials) passwords = load_password_vars(credentials) cleanup_files() if datastore['USER_AS_PASS'] credentials = gen_user_as_password(users, credentials) end if datastore['BLANK_PASSWORDS'] credentials = gen_blank_passwords(users, credentials) end if framework.db.active if datastore['DB_ALL_CREDS'] framework.db.creds(workspace: myworkspace.name).each do |o| credentials << [o.public.username, o.private.data] if o.private && o.private.type =~ /password/i end end if datastore['DB_ALL_USERS'] framework.db.creds(workspace: myworkspace.name).each do |o| users << o.public.username if o.public end end if datastore['DB_ALL_PASS'] framework.db.creds(workspace: myworkspace.name).each do |o| passwords << o.private.data if o.private && o.private.type =~ /password/i end end end credentials.concat(combine_users_and_passwords(users, passwords)) credentials.uniq! credentials = just_uniq_users(credentials) if @strip_passwords credentials = just_uniq_passwords(credentials) if @strip_usernames return credentials end |
#cleanup_files ⇒ Object
This method deletes the dictionary files if requested
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 781 def cleanup_files path = datastore['USERPASS_FILE'] if path and datastore['REMOVE_USERPASS_FILE'] ::File.unlink(path) rescue nil end path = datastore['USER_FILE'] if path and datastore['REMOVE_USER_FILE'] ::File.unlink(path) rescue nil end path = datastore['PASS_FILE'] if path and datastore['REMOVE_PASS_FILE'] ::File.unlink(path) rescue nil end end |
#combine_users_and_passwords(user_array, pass_array) ⇒ Object
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 521 def combine_users_and_passwords(user_array,pass_array) if (user_array.length + pass_array.length) < 1 return [] end combined_array = [] if pass_array.empty? combined_array = user_array.map {|u| [u,""] } elsif user_array.empty? combined_array = pass_array.map {|p| ["",p] } else if datastore['PASSWORD_SPRAY'] pass_array.each do |p| user_array.each do |u| combined_array << [u,p] end end else user_array.each do |u| pass_array.each do |p| combined_array << [u,p] end end end end creds = [ [], [], [], [] ] # userpass, pass, user, rest remaining_pairs = combined_array.length # counter for our occasional output interval = 60 # seconds between each remaining pair message reported to user = Time.now + interval # initial timing interval for user message # Move datastore['USERNAME'] and datastore['PASSWORD'] to the front of the list. # Note that we cannot tell the user intention if USERNAME or PASSWORD is blank -- # maybe (and it's often) they wanted a blank. One more credential won't kill # anyone, and hey, won't they be lucky if blank user/blank pass actually works! combined_array.each do |pair| if pair == [datastore['USERNAME'],datastore['PASSWORD']] creds[0] << pair elsif pair[1] == datastore['PASSWORD'] creds[1] << pair elsif pair[0] == datastore['USERNAME'] creds[2] << pair else creds[3] << pair end if Time.now > print_brute( :level => :vstatus, :msg => "Pair list is still building with #{remaining_pairs} pairs left to process" ) = Time.now + interval end remaining_pairs -= 1 end return creds[0] + creds[1] + creds[2] + creds[3] end |
#counters_expired?(this_service, credentials) ⇒ Boolean
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 347 def counters_expired?(this_service,credentials) expired_cred = false expired_time = false # Workaround for cases where multiple auth_brute modules are running concurrently and # someone stomps on the @max_per_service class variable during setup. current_max_per_service = self.class.class_variable_get("@@max_per_service") rescue nil return false unless current_max_per_service if @@guesses_per_service[this_service] >= (@@max_per_service) if @@max_per_service < credentials.size print_brute( :level => :vstatus, :ip => datastore['RHOST'], :port => datastore['RPORT'], :msg => "Hit maximum guesses for this service (#{@@max_per_service}).") expired_cred = true end end seconds_to_run = datastore['MaxMinutesPerService'].to_i.abs * 60 if seconds_to_run > 0 if Time.now.utc.to_i > @@brute_start_time.to_i + seconds_to_run print_brute( :level => :vstatus, :ip => datastore['RHOST'], :port => datastore['RPORT'], :msg => "Hit timeout for this service at #{seconds_to_run / 60}m.") expired_time = true end end expired_cred || expired_time end |
#each_ntlm_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'ntlm_hash'
122 123 124 125 126 127 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 122 def each_ntlm_cred creds = framework.db.creds(type: 'Metasploit::Credential::NTLMHash', workspace: myworkspace.name) creds.each do |cred| yield cred end end |
#each_password_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'password'
133 134 135 136 137 138 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 133 def each_password_cred creds = framework.db.creds(type: 'Metasploit::Credential::Password', workspace: myworkspace.name) creds.each do |cred| yield cred end end |
#each_ssh_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'ssh_key'
144 145 146 147 148 149 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 144 def each_ssh_cred creds = framework.db.creds(type: 'Metasploit::Credential::SSHKey', workspace: myworkspace.name) creds.each do |cred| yield cred end end |
#each_user_pass(noconn = false, &block) ⇒ Object
Checks all three files for usernames and passwords, and combines them into one credential list to apply against the supplied block. The block (usually something like do_login(user,pass) ) is responsible for actually recording success and failure in its own way; each_user_pass() will only respond to a return value of :done (which will signal to end all processing) and to :next_user (which will cause that username to be skipped for subsequent password guesses). Other return values won't affect the processing of the list.
The 'noconn' argument should be set to true if each_user_pass is merely iterating over the usernames and passwords and should not respect bruteforce_speed as a delaying factor.
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 257 def each_user_pass(noconn=false,&block) this_service = [datastore['RHOST'],datastore['RPORT']].join(":") fq_rest = [this_service,"all remaining users"].join(":") # This should kinda halfway be in setup, halfway in run... need to # revisit this. unless credentials ||= false # Assignment and comparison! credentials ||= build_credentials_array() credentials = adjust_credentials_by_max_user(credentials) this_service = [datastore['RHOST'],datastore['RPORT']].join(":") initialize_class_variables(this_service,credentials) end prev_iterator = nil credentials.each do |u, p| # Explicitly be able to set a blank (zero-byte) username by setting the # username to <BLANK>. It's up to the caller to handle this if it's not # allowed or if there's any special handling needed (such as smb_login). u = "" if u =~ /^<BLANK>$/i break if @@credentials_skipped[fq_rest] fq_user = [this_service,u].join(":") # Set noconn to indicate that in this case, each_user_pass # is not actually kicking off a connection, so the # bruteforce_speed datastore should be ignored. if not noconn userpass_sleep_interval unless @@credentials_tried.empty? end next if @@credentials_skipped[fq_user] next if @@credentials_tried[fq_user] == p # Used for tracking if we should TRANSITION_DELAY # If the current user/password values don't match the previous iteration we know # we've made it through all of the records for that iteration and should start the delay. if ![u,p].include?(prev_iterator) unless prev_iterator.nil? # Prevents a delay on the first run through if datastore['TRANSITION_DELAY'] > 0 vprint_status("Delaying #{datastore['TRANSITION_DELAY']} minutes before attempting next iteration.") sleep datastore['TRANSITION_DELAY'] * 60 end end prev_iterator = datastore['PASSWORD_SPRAY'] ? p : u # Update the iterator end ret = block.call(u, p) case ret when :abort # Skip the current host entirely. abort_msg = { :level => :error, :ip => datastore['RHOST'], :port => datastore['RPORT'], :msg => "Bruteforce cancelled against this service." } unless datastore['VERBOSE'] abort_msg[:msg] << " Enable verbose output for service-specific details." end print_brute abort_msg break when :next_user # This means success for that user. @@credentials_skipped[fq_user] = p if datastore['STOP_ON_SUCCESS'] # See? @@credentials_skipped[fq_rest] = true end when :skip_user # Skip the user in non-success cases. @@credentials_skipped[fq_user] = p when :connection_error # Report an error, skip this cred, but don't neccisarily abort. print_brute( :level => :verror, :ip => datastore['RHOST'], :port => datastore['RPORT'], :msg => "Connection error, skipping '#{u}':'#{p}'") end @@guesses_per_service[this_service] ||= 1 @@credentials_tried[fq_user] = p if counters_expired? this_service,credentials break else @@guesses_per_service[this_service] += 1 end end end |
#each_username_cred {|| ... } ⇒ Object
Yields each Metasploit::Credential::Core in the Mdm::Workspace with a private type of 'nil'
155 156 157 158 159 160 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 155 def each_username_cred creds = framework.db.creds(type: nil, workspace: myworkspace.name) creds.each do |cred| yield cred end end |
#extract_word_pair(wordfile) ⇒ Object
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 594 def extract_word_pair(wordfile) creds = [] if wordfile.to_s =~ /^memory:/ return extract_word_pair_from_memory(wordfile.to_s) else return [] unless wordfile && File.readable?(wordfile) begin upfile_contents = File.open(wordfile) {|f| f.read(f.stat.size)} rescue return [] end upfile_contents.split(/\n/).each do |line| user,pass = line.split(/\s+/,2).map { |x| x.strip } creds << [user.to_s, pass.to_s] end return creds end end |
#extract_word_pair_from_memory(memloc) ⇒ Object
613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 613 def extract_word_pair_from_memory(memloc) begin creds = [] obj = get_object_from_memory_location(memloc) unless obj.all_creds.empty? these_creds = obj.all_creds else these_creds = obj.builders.select {|x| x.respond_to? :imported_users}.map {|b| b.imported_users}.flatten end these_creds.each do |cred| if @strip_passwords user = cred.split(/\s+/,2).map {|x| x.strip}[0] pass = "" elsif @strip_usernames user = "" pass = cred.split(/\s+/,2).map {|x| x.strip}[1] else user,pass = cred.split(/\s+/,2).map {|x| x.strip} end creds << [Rex::Text.dehex(user.to_s), Rex::Text.dehex(pass.to_s)] end if @strip_passwords || @strip_usernames return creds.uniq else return creds end rescue => e raise ArgumentError, "Could not read credentials from memory, raised: #{e.class}: #{e.}" end end |
#extract_words(wordfile) ⇒ Object
576 577 578 579 580 581 582 583 584 585 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 576 def extract_words(wordfile) return [] unless wordfile && File.readable?(wordfile) begin File.readlines(wordfile, chomp: true) rescue ::StandardError => e elog(e) [] end end |
#gen_blank_passwords(user_array, cred_array) ⇒ Object
499 500 501 502 503 504 505 506 507 508 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 499 def gen_blank_passwords(user_array,cred_array) blank_passwords = [] unless user_array.empty? blank_passwords.concat(user_array.map {|u| [u,""]}) end unless cred_array.empty? cred_array.each {|u,p| blank_passwords << [u,""]} end return(blank_passwords + cred_array) end |
#gen_user_as_password(user_array, cred_array) ⇒ Object
510 511 512 513 514 515 516 517 518 519 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 510 def gen_user_as_password(user_array,cred_array) user_as_passwords = [] unless user_array.empty? user_as_passwords.concat(user_array.map {|u| [u,u]}) end unless cred_array.empty? cred_array.each {|u,p| user_as_passwords << [u,u]} end return(user_as_passwords + cred_array) end |
#get_object_from_memory_location(memloc) ⇒ Object
587 588 589 590 591 592 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 587 def get_object_from_memory_location(memloc) if memloc.to_s =~ /^memory:\s*([0-9]+)/ id = $1 ObjectSpace._id2ref(id.to_s.to_i) end end |
#initialize(info = {}) ⇒ Object
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 12 def initialize(info = {}) super ([ OptString.new('USERNAME', [ false, 'A specific username to authenticate as' ]), OptString.new('PASSWORD', [ false, 'A specific password to authenticate with' ]), OptPath.new('USER_FILE', [ false, "File containing usernames, one per line" ]), OptPath.new('PASS_FILE', [ false, "File containing passwords, one per line" ]), OptPath.new('USERPASS_FILE', [ false, "File containing users and passwords separated by space, one pair per line" ]), OptInt.new('BRUTEFORCE_SPEED', [ true, "How fast to bruteforce, from 0 to 5", 5]), OptBool.new('VERBOSE', [ true, "Whether to print output for all attempts", true]), OptBool.new('BLANK_PASSWORDS', [ false, "Try blank passwords for all users", false]), OptBool.new('USER_AS_PASS', [ false, "Try the username as the password for all users", false]), OptBool.new('DB_ALL_CREDS', [false,"Try each user/password couple stored in the current database",false]), OptBool.new('DB_ALL_USERS', [false,"Add all users in the current database to the list",false]), OptBool.new('DB_ALL_PASS', [false,"Add all passwords in the current database to the list",false]), OptEnum.new('DB_SKIP_EXISTING', [false,"Skip existing credentials stored in the current database", 'none', %w[ none user user&realm ]]), OptBool.new('STOP_ON_SUCCESS', [ true, "Stop guessing when a credential works for a host", false]), OptBool.new('ANONYMOUS_LOGIN', [ true, "Attempt to login with a blank username and password", false]) ], Auxiliary::AuthBrute) ([ OptBool.new('REMOVE_USER_FILE', [ true, "Automatically delete the USER_FILE on module completion", false]), OptBool.new('REMOVE_PASS_FILE', [ true, "Automatically delete the PASS_FILE on module completion", false]), OptBool.new('REMOVE_USERPASS_FILE', [ true, "Automatically delete the USERPASS_FILE on module completion", false]), OptBool.new('PASSWORD_SPRAY', [true, "Reverse the credential pairing order. For each password, attempt every possible user.", false]), OptInt.new('TRANSITION_DELAY', [false, "Amount of time (in minutes) to delay before transitioning to the next user in the array (or password when PASSWORD_SPRAY=true)", 0]), OptInt.new('MaxGuessesPerService', [ false, "Maximum number of credentials to try per service instance. If set to zero or a non-number, this option will not be used.", 0]), # Tracked in @@guesses_per_service OptInt.new('MaxMinutesPerService', [ false, "Maximum time in minutes to bruteforce the service instance. If set to zero or a non-number, this option will not be used.", 0]), # Tracked in @@brute_start_time OptInt.new('MaxGuessesPerUser', [ false, %q{ Maximum guesses for a particular username for the service instance. Note that users are considered unique among different services, so a user at 10.1.1.1:22 is different from one at 10.2.2.2:22, and both will be tried up to the MaxGuessesPerUser limit. If set to zero or a non-number, this option will not be used.}.gsub(/[\t\r\n\s]+/nm,"\s"), 0]) # Tracked in @@brute_start_time ], Auxiliary::AuthBrute) end |
#initialize_class_variables(this_service, credentials) ⇒ Object
Class variables to track credential use. They need to be class variables due to threading.
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 421 def initialize_class_variables(this_service,credentials) @@guesses_per_service ||= {} @@guesses_per_service[this_service] = nil @@credentials_skipped = {} @@credentials_tried = {} @@guesses_per_service = {} if datastore['MaxGuessesPerService'].to_i.abs == 0 @@max_per_service = credentials.size else if datastore['MaxGuessesPerService'].to_i.abs >= credentials.size @@max_per_service = credentials.size print_brute( :level => :vstatus, :ip => datastore['RHOST'], :port => datastore['RPORT'], :msg => "Adjusting MaxGuessesPerService to the actual total number of credentials") else @@max_per_service = datastore['MaxGuessesPerService'].to_i.abs end end unless datastore['MaxMinutesPerService'].to_i.abs == 0 @@brute_start_time = Time.now.utc end end |
#just_uniq_passwords(credentials) ⇒ Object
487 488 489 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 487 def just_uniq_passwords(credentials) credentials.map{|x| ["",x[1]]}.uniq end |
#just_uniq_users(credentials) ⇒ Object
483 484 485 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 483 def just_uniq_users(credentials) credentials.map {|x| [x[0],""]}.uniq end |
#load_password_vars(credentials = nil) ⇒ Object
456 457 458 459 460 461 462 463 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 456 def load_password_vars(credentials = nil) passwords = extract_words(datastore['PASS_FILE']) if datastore['PASSWORD'] passwords.unshift datastore['PASSWORD'] credentials = prepend_chosen_password(datastore['PASSWORD'], credentials) if credentials end passwords end |
#load_user_vars(credentials = nil) ⇒ Object
447 448 449 450 451 452 453 454 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 447 def load_user_vars(credentials = nil) users = extract_words(datastore['USER_FILE']) if datastore['USERNAME'] users.unshift datastore['USERNAME'] credentials = prepend_chosen_username(datastore['USERNAME'], credentials) if credentials end users end |
#prepend_chosen_password(pass, cred_array) ⇒ Object
495 496 497 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 495 def prepend_chosen_password(pass,cred_array) cred_array.map {|pair| [pair[0],pass]} + cred_array end |
#prepend_chosen_username(user, cred_array) ⇒ Object
491 492 493 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 491 def prepend_chosen_username(user,cred_array) cred_array.map {|pair| [user,pair[1]]} + cred_array end |
#prepend_db_creds? ⇒ TrueClass, FalseClass
Checks whether we should be adding creds from the DB to a CredCollection
166 167 168 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 166 def prepend_db_creds? (datastore['DB_ALL_CREDS'] || datastore['DB_ALL_PASS'] || datastore['DB_ALL_USERS']) && framework.db.active end |
#prepend_db_hashes(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing NTLMHashes from the database. This allows the users to use the DB_ALL_CREDS option.
176 177 178 179 180 181 182 183 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 176 def prepend_db_hashes(cred_collection) if prepend_db_creds? each_ntlm_cred do |cred| process_cred_for_collection(cred_collection,cred) end end cred_collection end |
#prepend_db_keys(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing SSHKeys from the database. This allows the users to use the DB_ALL_CREDS option.
191 192 193 194 195 196 197 198 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 191 def prepend_db_keys(cred_collection) if prepend_db_creds? each_ssh_cred do |cred| process_cred_for_collection(cred_collection,cred) end end cred_collection end |
#prepend_db_passwords(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing Password Credentials from the database. This allows the users to use the DB_ALL_CREDS option.
206 207 208 209 210 211 212 213 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 206 def prepend_db_passwords(cred_collection) if prepend_db_creds? each_password_cred do |cred| process_cred_for_collection(cred_collection,cred) end end cred_collection end |
#prepend_db_usernames(cred_collection) ⇒ Metasploit::Framework::CredentialCollection
This method takes a Metasploit::Framework::CredentialCollection and prepends existing Usernames from the database. This allows the users to use the DB_ALL_USERS option.
221 222 223 224 225 226 227 228 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 221 def prepend_db_usernames(cred_collection) if prepend_db_creds? each_username_cred do |cred| process_cred_for_collection(cred_collection,cred) end end cred_collection end |
#print_brute(opts = {}) ⇒ Object
Provides a consistent way to display messages about AuthBrute-mixed modules. Acceptable opts are fairly self-explanatory, but :level can be tricky.
It can be one of status, good, error, or line (and corresponds to the usual print_status, print_good, etc. methods).
If it's preceded by a āvā (ie, vgood, verror, etc), only print if datastore is set to true.
If :level would make the method nonsense, default to print_status.
TODO: This needs to be simpler to be useful.
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 692 def print_brute(opts={}) if opts[:level] and opts[:level].to_s[/^v/] return unless datastore["VERBOSE"] level = opts[:level].to_s[1,16].strip else level = opts[:level].to_s.strip end host_ip = opts[:ip] || opts[:rhost] || opts[:host] || (rhost rescue nil) || datastore['RHOST'] host_port = opts[:port] || opts[:rport] || (rport rescue nil) || datastore['RPORT'] msg = opts[:msg] || opts[:message] proto = opts[:proto] || opts[:protocol] || proto_from_fullname = (host_ip,host_port,proto,msg) print_method = "print_#{level}" if self.respond_to? print_method self.send print_method, else print_status end end |
#process_cred_for_collection(cred_collection, cred) ⇒ Object
Takes a Metasploit::Credential::Core and converts it into a Metasploit::Framework::Credential and processes it into the Metasploit::Framework::CredentialCollection as dictated by the selected datastore options.
237 238 239 240 241 242 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 237 def process_cred_for_collection(cred_collection, cred) msf_cred = cred.to_credential cred_collection.prepend_cred(msf_cred) if datastore['DB_ALL_CREDS'] cred_collection.add_private(msf_cred.private) if datastore['DB_ALL_PASS'] cred_collection.add_public(msf_cred.public) if datastore['DB_ALL_USERS'] end |
#proto_from_fullname ⇒ Object
Protocols can nearly always be automatically determined from the name of the module, assuming the name is sensible like ssh_login or smb_auth.
776 777 778 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 776 def proto_from_fullname File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil end |
#setup ⇒ Object
114 115 116 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 114 def setup @@max_per_service = nil end |
#translate_proto_datastores ⇒ Object
Takes protocol-specific username and password fields, and, if present, prefer those over any given USERNAME or PASSWORD. Note, these special username/passwords should get deprecated some day. Note2: Don't use with SMB and FTP at the same time!
470 471 472 473 474 475 476 477 478 479 480 481 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 470 def translate_proto_datastores ['SMBUser','FTPUSER'].each do |u| if datastore[u] and !datastore[u].empty? datastore['USERNAME'] = datastore[u] end end ['SMBPass','FTPPASS'].each do |p| if datastore[p] and !datastore[p].empty? datastore['PASSWORD'] = datastore[p] end end end |
#tried_over_total(ip, port) ⇒ Object
Fun trick: Only prints if we're already in each_user_pass, since only then is @@max_per_service defined.
764 765 766 767 768 769 770 771 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 764 def tried_over_total(ip,port) total = self.class.class_variable_get("@@max_per_service") rescue nil return unless total total = total.to_i current_try = (@@guesses_per_service["#{ip}:#{port}"] || 1).to_i pad = total.to_s.size "[%0#{pad}d/%0#{pad}d] - " % [current_try, total] end |
#userpass_interval ⇒ Object
644 645 646 647 648 649 650 651 652 653 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 644 def userpass_interval case datastore['BRUTEFORCE_SPEED'].to_i when 0; 60 * 5 when 1; 15 when 2; 1 when 3; 0.5 when 4; 0.1 else; 0 end end |
#userpass_sleep_interval ⇒ Object
655 656 657 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 655 def userpass_sleep_interval ::IO.select(nil,nil,nil,userpass_interval) unless userpass_interval == 0 end |
#vprint_brute(opts = {}) ⇒ Object
See #print_brute
660 661 662 663 664 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 660 def vprint_brute(opts={}) if datastore['VERBOSE'] print_brute(opts) end end |
#vprint_error(msg = '') ⇒ Object Also known as: vprint_bad
670 671 672 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 670 def vprint_error(msg='') print_brute :level => :verror, :msg => msg end |
#vprint_good(msg = '') ⇒ Object
676 677 678 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 676 def vprint_good(msg='') print_brute :level => :vgood, :msg => msg end |
#vprint_status(msg = '') ⇒ Object
666 667 668 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 666 def vprint_status(msg='') print_brute :level => :vstatus, :msg => msg end |