Rejiggers the max credentials limiter a little, and adds a max time limiter per service.

git-svn-id: file:///home/svn/framework3/trunk@12967 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Tod Beardsley 2011-06-17 22:40:25 +00:00
parent e30252df02
commit db1619d035
2 changed files with 74 additions and 33 deletions

View File

@ -28,7 +28,8 @@ module Auxiliary::AuthBrute
OptBool.new('REMOVE_USER_FILE', [ true, "Automatically delete the USER_FILE on module completion", false]), 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_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('REMOVE_USERPASS_FILE', [ true, "Automatically delete the USERPASS_FILE on module completion", false]),
OptInt.new('MaxGuessesPerService', [ false, "Maximum number of credentials to try per service instance.", 0]) OptInt.new('MaxGuessesPerService', [ false, "Maximum number of credentials to try per service instance.", 0]), # Tracked in @@guesses_per_service
OptInt.new('MaxMinutesPerService', [ false, "Maximum time in minutes to bruteforce the service instance.", 0]) # Tracked in @@brute_start_time
], Auxiliary::AuthBrute) ], Auxiliary::AuthBrute)
end end
@ -49,8 +50,10 @@ module Auxiliary::AuthBrute
this_service = [datastore['RHOST'],datastore['RPORT']].join(":") this_service = [datastore['RHOST'],datastore['RPORT']].join(":")
fq_rest = [this_service,"all remaining users"].join(":") fq_rest = [this_service,"all remaining users"].join(":")
unless credentials ||= false # Assignment and comparison!
credentials = build_credentials_array() credentials = build_credentials_array()
initialize_class_variables(credentials) initialize_class_variables(this_service,credentials)
end
credentials.each do |u, p| credentials.each do |u, p|
# Explicitly be able to set a blank (zero-byte) username by setting the # Explicitly be able to set a blank (zero-byte) username by setting the
@ -96,23 +99,45 @@ module Auxiliary::AuthBrute
@@guesses_per_service[this_service] ||= 1 @@guesses_per_service[this_service] ||= 1
@@credentials_tried[fq_user] = p @@credentials_tried[fq_user] = p
if counters_expired? this_service,credentials
break
else
@@guesses_per_service[this_service] += 1
end
end
end
def counters_expired?(this_service,credentials)
expired_cred = false
expired_time = false
if @@guesses_per_service[this_service] >= (@@max_per_service) if @@guesses_per_service[this_service] >= (@@max_per_service)
if @@max_per_service < credentials.size if @@max_per_service < credentials.size
print_brute( print_brute(
:level => :vstatus, :level => :vstatus,
:ip => datastore['RHOST'], :ip => datastore['RHOST'],
:port => datastore['RPORT'], :port => datastore['RPORT'],
:msg => "Hit maximum guesses for this service.") :msg => "Hit maximum guesses for this service (#{@@max_per_service}).")
break expired_cred = true
end
else
@@guesses_per_service[this_service] += 1
end end
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 end
def unset_attempt_counters(ip,port) def reset_attempt_counters(*args)
ip = args[0]
port = args[1]
@@guesses_per_service ||= {} @@guesses_per_service ||= {}
@@guesses_per_service["#{ip}:#{port}"] = nil @@guesses_per_service["#{ip}:#{port}"] = nil
@@max_per_service = nil @@max_per_service = nil
@ -144,15 +169,16 @@ module Auxiliary::AuthBrute
# Class variables to track credential use. They need # Class variables to track credential use. They need
# to be class variables due to threading. # to be class variables due to threading.
def initialize_class_variables(credentials) def initialize_class_variables(this_service,credentials)
reset_attempt_counters(this_service.split(":"))
@@credentials_skipped = {} @@credentials_skipped = {}
@@credentials_tried = {} @@credentials_tried = {}
@@guesses_per_service = {} @@guesses_per_service = {}
if datastore['MaxGuessesPerService'].to_i == 0 if datastore['MaxGuessesPerService'].to_i.abs == 0
@@max_per_service = credentials.size @@max_per_service = credentials.size
else else
if datastore['MaxGuessesPerService'].to_i >= credentials.size if datastore['MaxGuessesPerService'].to_i.abs >= credentials.size
@@max_per_service = credentials.size @@max_per_service = credentials.size
print_brute( print_brute(
:level => :vstatus, :level => :vstatus,
@ -160,9 +186,12 @@ module Auxiliary::AuthBrute
:port => datastore['RPORT'], :port => datastore['RPORT'],
:msg => "Adjusting MaxGuessesPerService to the actual total number of credentials") :msg => "Adjusting MaxGuessesPerService to the actual total number of credentials")
else else
@@max_per_service = datastore['MaxGuessesPerService'].to_i @@max_per_service = datastore['MaxGuessesPerService'].to_i.abs
end end
end end
unless datastore['MaxMinutesPerService'].to_i.abs == 0
@@brute_start_time = Time.now.utc
end
end end
def load_user_vars(credentials = nil) def load_user_vars(credentials = nil)
@ -325,12 +354,12 @@ module Auxiliary::AuthBrute
level = opts[:level].to_s.strip level = opts[:level].to_s.strip
end end
host_ip = opts[:ip] || opts[:rhost] || opts[:host] host_ip = opts[:ip] || opts[:rhost] || opts[:host] || (rhost rescue nil) || datastore['RHOST']
host_port = opts[:port] || opts[:rport] || (rport rescue nil) host_port = opts[:port] || opts[:rport] || (rport rescue nil) || datastore['RPORT']
msg = opts[:msg] || opts[:message] msg = opts[:msg] || opts[:message] || opts[:legacy_msg]
proto = opts[:proto] || opts[:protocol] || proto_from_fullname proto = opts[:proto] || opts[:protocol] || proto_from_fullname
complete_message = build_brute_message(host_ip,host_port,proto,msg) complete_message = build_brute_message(host_ip,host_port,proto,msg,!!opts[:legacy_msg])
print_method = "print_#{level}" print_method = "print_#{level}"
if self.respond_to? print_method if self.respond_to? print_method
@ -343,15 +372,28 @@ module Auxiliary::AuthBrute
# Depending on the non-nil elements, build up a standardized # Depending on the non-nil elements, build up a standardized
# auth_brute message, but support the old style used by # auth_brute message, but support the old style used by
# vprint_status and friends as well. # vprint_status and friends as well.
def build_brute_message(host_ip,host_port,proto,msg) def build_brute_message(host_ip,host_port,proto,msg,legacy)
ip = host_ip.to_s.strip if host_ip ip = host_ip.to_s.strip if host_ip
port = host_port.to_s.strip if host_port port = host_port.to_s.strip if host_port
complete_message = "" complete_message = nil
unless ip && port extracted_message = nil
complete_message = msg.to_s.strip if legacy # TODO: This is all a workaround until I get a chance to get rid of the legacy messages
old_msg = msg.to_s.strip
msg_regex = /(#{ip})(:#{port})?(\s*-?\s*)(#{proto.to_s})?(\s*-?\s*)(.*)/ni
if old_msg.match(msg_regex)
complete_message = [ip,port].join(":")
(complete_message << " ") if ip
complete_message << (old_msg.match(msg_regex)[4] || proto).to_s
complete_message << " - "
progress = tried_over_total(ip,port)
complete_message << progress if progress
complete_message << old_msg.match(msg_regex)[6].to_s.strip
else else
complete_message << [ip,port].join(":") complete_message = msg.to_s.strip
(complete_message << " - ") if ip end
else
complete_message = [ip,port].join(":")
(complete_message << " ") if ip
complete_message << "#{proto.to_s.strip} - " if proto complete_message << "#{proto.to_s.strip} - " if proto
progress = tried_over_total(ip,port) progress = tried_over_total(ip,port)
complete_message << progress if progress complete_message << progress if progress
@ -379,17 +421,17 @@ module Auxiliary::AuthBrute
# Legacy vprint # Legacy vprint
def vprint_status(msg='') def vprint_status(msg='')
print_brute :level => :vstatus, :msg => msg print_brute :level => :vstatus, :legacy_msg => msg
end end
# Legacy vprint # Legacy vprint
def vprint_error(msg='') def vprint_error(msg='')
print_brute :level => :verror, :msg => msg print_brute :level => :verror, :legacy_msg => msg
end end
# Legacy vprint # Legacy vprint
def vprint_good(msg='') def vprint_good(msg='')
print_brute :level => :vgood, :msg => msg print_brute :level => :vgood, :legacy_msg => msg
end end
# This method deletes the dictionary files if requested # This method deletes the dictionary files if requested

View File

@ -129,7 +129,6 @@ class Metasploit3 < Msf::Auxiliary
end end
def run_host(ip) def run_host(ip)
unset_attempt_counters(ip,rport)
print_brute :ip => ip, :msg => "Starting bruteforce" print_brute :ip => ip, :msg => "Starting bruteforce"
each_user_pass do |user, pass| each_user_pass do |user, pass|
print_brute :level => :vstatus, print_brute :level => :vstatus,