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.
742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 742 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.
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 717 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 113 |
# 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'], 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.
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 418 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 383 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
782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 782 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
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 575 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 522 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
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 377 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 348 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'
123 124 125 126 127 128 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 123 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'
134 135 136 137 138 139 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 134 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'
145 146 147 148 149 150 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 145 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.
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 346 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 258 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'
156 157 158 159 160 161 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 156 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
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 595 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
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 643 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 614 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
577 578 579 580 581 582 583 584 585 586 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 577 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
500 501 502 503 504 505 506 507 508 509 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 500 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
511 512 513 514 515 516 517 518 519 520 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 511 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
588 589 590 591 592 593 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 588 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.
422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 422 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
488 489 490 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 488 def just_uniq_passwords(credentials) credentials.map{|x| ["",x[1]]}.uniq end |
#just_uniq_users(credentials) ⇒ Object
484 485 486 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 484 def just_uniq_users(credentials) credentials.map {|x| [x[0],""]}.uniq end |
#load_password_vars(credentials = nil) ⇒ Object
457 458 459 460 461 462 463 464 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 457 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
448 449 450 451 452 453 454 455 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 448 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
496 497 498 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 496 def prepend_chosen_password(pass,cred_array) cred_array.map {|pair| [pair[0],pass]} + cred_array end |
#prepend_chosen_username(user, cred_array) ⇒ Object
492 493 494 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 492 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
167 168 169 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 167 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.
177 178 179 180 181 182 183 184 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 177 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.
192 193 194 195 196 197 198 199 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 192 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.
207 208 209 210 211 212 213 214 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 207 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.
222 223 224 225 226 227 228 229 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 222 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.
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 693 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.
238 239 240 241 242 243 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 238 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.
777 778 779 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 777 def proto_from_fullname File.split(self.fullname).last.match(/^(.*)_(login|auth|identify)/)[1].upcase rescue nil end |
#setup ⇒ Object
115 116 117 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 115 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!
471 472 473 474 475 476 477 478 479 480 481 482 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 471 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.
765 766 767 768 769 770 771 772 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 765 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
645 646 647 648 649 650 651 652 653 654 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 645 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
656 657 658 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 656 def userpass_sleep_interval ::IO.select(nil,nil,nil,userpass_interval) unless userpass_interval == 0 end |
#vprint_brute(opts = {}) ⇒ Object
See #print_brute
661 662 663 664 665 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 661 def vprint_brute(opts={}) if datastore['VERBOSE'] print_brute(opts) end end |
#vprint_error(msg = '') ⇒ Object Also known as: vprint_bad
671 672 673 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 671 def vprint_error(msg='') print_brute :level => :verror, :msg => msg end |
#vprint_good(msg = '') ⇒ Object
677 678 679 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 677 def vprint_good(msg='') print_brute :level => :vgood, :msg => msg end |
#vprint_status(msg = '') ⇒ Object
667 668 669 |
# File 'lib/msf/core/auxiliary/auth_brute.rb', line 667 def vprint_status(msg='') print_brute :level => :vstatus, :msg => msg end |