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-b9f4589650daunstable
parent
f13417be08
commit
a0e252ad75
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue