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.
745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 745 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.
720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 720 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.
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 116 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 60 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.
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 421 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 386 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
785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 785 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
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 578 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 525 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
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 380 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 351 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’
126 127 128 129 130 131 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 126 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’
137 138 139 140 141 142 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 137 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’
148 149 150 151 152 153 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 148 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.
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 349 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 261 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’
159 160 161 162 163 164 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 159 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
598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 598 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
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 646 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 617 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
580 581 582 583 584 585 586 587 588 589 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 580 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
503 504 505 506 507 508 509 510 511 512 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 503 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
514 515 516 517 518 519 520 521 522 523 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 514 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
591 592 593 594 595 596 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 591 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 51 |
# 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]), OptString.new('SSLKeyLogFile', [ false, 'The SSL key log file', ENV['SSLKeyLogFile']]), 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.
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 425 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
491 492 493 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 491 def just_uniq_passwords(credentials) credentials.map{|x| ["",x[1]]}.uniq end |
#just_uniq_users(credentials) ⇒ Object
487 488 489 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 487 def just_uniq_users(credentials) credentials.map {|x| [x[0],""]}.uniq end |
#load_password_vars(credentials = nil) ⇒ Object
460 461 462 463 464 465 466 467 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 460 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
451 452 453 454 455 456 457 458 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 451 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
499 500 501 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 499 def prepend_chosen_password(pass,cred_array) cred_array.map {|pair| [pair[0],pass]} + cred_array end |
#prepend_chosen_username(user, cred_array) ⇒ Object
495 496 497 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 495 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
170 171 172 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 170 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.
180 181 182 183 184 185 186 187 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 180 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.
195 196 197 198 199 200 201 202 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 195 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.
210 211 212 213 214 215 216 217 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 210 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.
225 226 227 228 229 230 231 232 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 225 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.
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 696 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.
241 242 243 244 245 246 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 241 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.
780 781 782 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 780 def proto_from_fullname File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil end |
#setup ⇒ Object
118 119 120 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 118 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!
474 475 476 477 478 479 480 481 482 483 484 485 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 474 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.
768 769 770 771 772 773 774 775 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 768 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
648 649 650 651 652 653 654 655 656 657 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 648 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
659 660 661 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 659 def userpass_sleep_interval ::IO.select(nil,nil,nil,userpass_interval) unless userpass_interval == 0 end |
#vprint_brute(opts = {}) ⇒ Object
See #print_brute
664 665 666 667 668 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 664 def vprint_brute(opts={}) if datastore['VERBOSE'] print_brute(opts) end end |
#vprint_error(msg = '') ⇒ Object Also known as: vprint_bad
674 675 676 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 674 def vprint_error(msg='') print_brute :level => :verror, :msg => msg end |
#vprint_good(msg = '') ⇒ Object
680 681 682 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 680 def vprint_good(msg='') print_brute :level => :vgood, :msg => msg end |
#vprint_status(msg = '') ⇒ Object
670 671 672 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 670 def vprint_status(msg='') print_brute :level => :vstatus, :msg => msg end |