See #2034. This adds a pretty decent reconnection handler -- keep trying until you're dropped, so this is handy for sites that greylist the banner display.

Also ensures that anonymous:somebrowserID is always checked.



git-svn-id: file:///home/svn/framework3/trunk@9447 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Tod Beardsley 2010-06-07 21:37:29 +00:00
parent f13417be08
commit a0e252ad75
2 changed files with 53 additions and 40 deletions

View File

@ -60,6 +60,7 @@ def each_user_pass(&block)
credentials.concat(combine_users_and_passwords(users,passwords))
credentials = just_uniq_passwords(credentials) if @strip_usernames
fq_rest = "%s:%s:%s" % [datastore['RHOST'], datastore['RPORT'], "all remaining users"]
vprint_status "#{datastore['RHOST']}:#{datastore['RPORT']} - Attempting #{credentials.size} username and password combinations"
credentials.each do |u,p|
@ -77,6 +78,8 @@ def each_user_pass(&block)
if datastore['STOP_ON_SUCCESS']
@@credentials_skipped[fq_rest] = true
end
when :connection_error
vprint_error "#{datastore['RHOST']}:#{datastore['RPORT']} - Connection error, skipping '#{u}':'#{p}'"
end
@@credentials_tried[fq_user] = p
end

View File

@ -42,20 +42,33 @@ class Metasploit3 < Msf::Auxiliary
], self.class)
deregister_options('FTPUSER','FTPPASS') # Can use these, but should use 'username' and 'password'
end
def run_host(ip)
print_status("#{ip}:#{rport} - Starting FTP login sweep")
begin
check_banner
if check_banner
each_user_pass { |user, pass|
next if user.nil? || user.empty?
do_login(user,pass)
}
rescue ::Rex::ConnectionError, ::EOFError, ::IOError
check_anonymous
else
return
end
disconnect
@ftp_sock = nil
end
# Always check for anonymous access by pretending to be a browser.
def check_anonymous
browser_passwords = {}
browser_passwords['IE6'] = "IEUser@"
browser_passwords['IE8'] = "User@"
browser_passwords['Firefox'] = 'mozilla@example.com'
browser_passwords['Chrome'] = 'chrome@example.com'
unless @@credentials_tried.keys.include? "#{rhost}:#{rport}:anonymous"
do_login("anonymous",browser_passwords.values[rand(browser_passwords.size)])
end
end
def check_banner
@ -64,49 +77,46 @@ class Metasploit3 < Msf::Auxiliary
banner_sanitized = self.banner.to_s.gsub(/[\x00-\x19\x7f-\xff]/) { |s| "\\x%02x" % s[0].ord}
print_status("#{rhost}:#{rport} - FTP Banner: '#{banner_sanitized}'")
report_service(:host => rhost, :port => rport, :name => "ftp", :info => banner_sanitized)
return true
else
print_error("#{rhost}:#{rport} - Could not connect to host.")
return false
end
end
def do_login(user=nil,pass=nil)
if !@ftp_sock || @ftp_sock.closed?
@ftp_sock = connect(true,false)
end
vprint_status("#{rhost}:#{rport} - Attempting FTP login for '#{user}':'#{pass}'")
if(self.banner)
user_res = send_user(user, @ftp_sock)
if user_res !~ /^(331|2)/
vprint_error("#{rhost}:#{rport} - The server rejected username: '#{user}'")
send_quit
disconnect
return :next_user
this_attempt ||= {}
this_attempt[[user,pass]] ||= 0
while this_attempt[[user,pass]] <= 3
@ftp_sock = connect(true,false) unless @ftp_sock
begin
user_response = send_user(user, @ftp_sock)
if user_response !~ /^(331|2)/
vprint_error("#{rhost}:#{rport} - The server rejected username: '#{user}'")
return :next_user
end
pass_response = send_pass(pass, @ftp_sock)
if pass_response =~ /^2/
print_good("#{rhost}:#{rport} - Successful FTP login for '#{user}':'#{pass}'")
access = test_ftp_access(user)
report_ftp_creds(user,pass,access)
send_quit
disconnect
@ftp_sock = nil
return :next_user
else
vprint_status("#{rhost}:#{rport} - Failed FTP login for '#{user}':'#{pass}'")
return :fail
end
rescue ::Rex::ConnectionError, EOFError, ::Errno::ECONNRESET => e
this_attempt[[user,pass]] += 1
vprint_status "#{rhost}:#{rport} - Caught #{e.class}, reconnecting and retrying"
disconnect if @ftp_sock
@ftp_sock = nil
end
pass_res = send_pass(pass, @ftp_sock)
if pass_res =~ /^2/
print_good("#{rhost}:#{rport} - Successful FTP login for '#{user}':'#{pass}'")
access = test_ftp_access(user)
report_ftp_creds(user,pass,access)
send_quit
disconnect
return :next_user
else
vprint_status("#{rhost}:#{rport} - Failed FTP login for '#{user}':'#{pass}'")
# TODO: Shouldn't have to disconnect every time, but sadly, some FTP servers
# behave erratically in the face of failed logins -- they will sometimes drop
# in the middle of the user/pass sequence, which means we need to reconnect
# and retry that user/pass combination. For now, we always
# disconnect after a failed attempt. In many cases, 3 retries per session is
# safe, but it's difficult to pin down the corner cases. IOW, set the retry
# behavior as a user option, so we can better handle FTP servers that employ
# greylisting.
send_quit
disconnect
return :fail
end
else
print_error("#{rhost}:#{rport} - The server did not provide a banner; aborting (FTPTimeout is currently set to #{datastore['FTPTimeout']})")
disconnect
return :abort
end
return :connection_error
end
def test_ftp_access(user)