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-b9f4589650daunstable
parent
aa827488cd
commit
225bf0738e
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue