Module: Msf::Auxiliary::AuthBrute
- Includes:
- LoginScanner
- 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
Methods included from LoginScanner
Instance Method Details
#adjust_credentials_by_max_user(credentials) ⇒ Object
Takes a credentials array, and returns just the first X involving a particular user.
744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 744 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.
719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 719 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.
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 113 114 115 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 59 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'], password_spray: datastore['PASSWORD_SPRAY'] }.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.
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 418 419 420 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 385 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
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 784 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
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 575 576 577 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 524 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
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 377 378 379 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 350 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’
125 126 127 128 129 130 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 125 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’
136 137 138 139 140 141 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 136 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’
147 148 149 150 151 152 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 147 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.
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 346 347 348 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 260 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’
158 159 160 161 162 163 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 158 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
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 597 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
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 643 644 645 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 616 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
579 580 581 582 583 584 585 586 587 588 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 579 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
502 503 504 505 506 507 508 509 510 511 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 502 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
513 514 515 516 517 518 519 520 521 522 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 513 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
590 591 592 593 594 595 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 590 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
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 49 50 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 14 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.
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 424 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
490 491 492 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 490 def just_uniq_passwords(credentials) credentials.map{|x| ["",x[1]]}.uniq end |
#just_uniq_users(credentials) ⇒ Object
486 487 488 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 486 def just_uniq_users(credentials) credentials.map {|x| [x[0],""]}.uniq end |
#load_password_vars(credentials = nil) ⇒ Object
459 460 461 462 463 464 465 466 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 459 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
450 451 452 453 454 455 456 457 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 450 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
498 499 500 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 498 def prepend_chosen_password(pass,cred_array) cred_array.map {|pair| [pair[0],pass]} + cred_array end |
#prepend_chosen_username(user, cred_array) ⇒ Object
494 495 496 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 494 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
169 170 171 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 169 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.
179 180 181 182 183 184 185 186 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 179 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.
194 195 196 197 198 199 200 201 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 194 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.
209 210 211 212 213 214 215 216 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 209 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.
224 225 226 227 228 229 230 231 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 224 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.
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 695 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.
240 241 242 243 244 245 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 240 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.
779 780 781 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 779 def proto_from_fullname File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil end |
#setup ⇒ Object
117 118 119 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 117 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!
473 474 475 476 477 478 479 480 481 482 483 484 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 473 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.
767 768 769 770 771 772 773 774 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 767 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
647 648 649 650 651 652 653 654 655 656 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 647 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
658 659 660 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 658 def userpass_sleep_interval ::IO.select(nil,nil,nil,userpass_interval) unless userpass_interval == 0 end |
#vprint_brute(opts = {}) ⇒ Object
See #print_brute
663 664 665 666 667 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 663 def vprint_brute(opts={}) if datastore['VERBOSE'] print_brute(opts) end end |
#vprint_error(msg = '') ⇒ Object Also known as: vprint_bad
673 674 675 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 673 def vprint_error(msg='') print_brute :level => :verror, :msg => msg end |
#vprint_good(msg = '') ⇒ Object
679 680 681 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 679 def vprint_good(msg='') print_brute :level => :vgood, :msg => msg end |
#vprint_status(msg = '') ⇒ Object
669 670 671 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 669 def vprint_status(msg='') print_brute :level => :vstatus, :msg => msg end |