mirror of
https://github.com/rapid7/metasploit-framework
synced 2024-11-12 11:52:01 +01:00
add the ability to check for a prompt before sending user/pass; now works with cisco, aix, solaris, linux, and windows telnetds
git-svn-id: file:///home/svn/framework3/trunk@8434 4d416f70-5f16-0410-b530-b9f4589650da
This commit is contained in:
parent
23901c83ea
commit
e2d70519d7
@ -106,6 +106,26 @@ module Exploit::Remote::Telnet
|
||||
|
||||
register_autofilter_ports([ 23 ])
|
||||
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 = ''
|
||||
|
||||
#
|
||||
# These regexes taken directly from NeXpose.
|
||||
#
|
||||
@login_regex = /(?:[lL]ogin|[uU]sername) *\:/
|
||||
@password_regex = /(?:[pP]assword|[pP]asswd) *\:/
|
||||
@false_failure_regex = /(?:[lL]ast [lL]ogin *\:|allows only .* Telnet Client License)/
|
||||
@failure_regex = /(?:
|
||||
[iI]ncorrect | [uU]nknown | [fF]ail | [iI]nvalid |
|
||||
[lL]ogin | [pP]assword | [pP]asswd | [uU]sername |
|
||||
[uU]nable | [eE]rror | [dD]enied | [rR]eject |
|
||||
[rR]efuse | [cC]lose | [cC]losing | % Bad |
|
||||
Not on system console
|
||||
)/x
|
||||
end
|
||||
|
||||
#
|
||||
@ -115,6 +135,7 @@ module Exploit::Remote::Telnet
|
||||
# benefit of handling telnet option negotiation.
|
||||
#
|
||||
def connect(global = true, verbose = true)
|
||||
@recvd = ''
|
||||
fd = super(global)
|
||||
|
||||
self.banner = ''
|
||||
@ -124,7 +145,7 @@ module Exploit::Remote::Telnet
|
||||
while(true)
|
||||
buff = recv_telnet(fd)
|
||||
self.banner << buff if buff
|
||||
if(self.banner =~ /login|user|welcome|pass|\$|\#/i)
|
||||
if(self.banner =~ @login_regex or self.banner =~ @password_regex)
|
||||
break
|
||||
end
|
||||
end
|
||||
@ -142,10 +163,13 @@ module Exploit::Remote::Telnet
|
||||
#
|
||||
# Handle telnet option negotiation
|
||||
#
|
||||
# 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_telnet(fd=self.sock, timeout=datastore['TelnetTimeout'])
|
||||
|
||||
data = fd.get_once(-1, timeout.to_i)
|
||||
return nil if not data
|
||||
return nil if not data or data.length == 0
|
||||
|
||||
# combine CR+NULL into CR
|
||||
data.gsub!(/#{CR}#{NULL}/no, CR)
|
||||
@ -153,7 +177,7 @@ module Exploit::Remote::Telnet
|
||||
# combine EOL into "\n"
|
||||
data.gsub!(/#{EOL}/no, "\n")
|
||||
|
||||
data.gsub(/#{IAC}(
|
||||
data.gsub!(/#{IAC}(
|
||||
[#{IAC}#{AO}#{AYT}#{DM}#{IP}#{NOP}]|
|
||||
[#{DO}#{DONT}#{WILL}#{WONT}]
|
||||
[#{OPT_BINARY}-#{OPT_NEW_ENVIRON}#{OPT_EXOPL}]|
|
||||
@ -165,6 +189,7 @@ module Exploit::Remote::Telnet
|
||||
IAC
|
||||
elsif m == AYT
|
||||
fd.write("YES" + EOL)
|
||||
''
|
||||
elsif m[0,1] == DO
|
||||
if(m[1,1] == OPT_BINARY)
|
||||
fd.write(IAC + WILL + OPT_BINARY)
|
||||
@ -195,6 +220,39 @@ module Exploit::Remote::Telnet
|
||||
''
|
||||
end
|
||||
end
|
||||
@recvd << data
|
||||
fd.flush
|
||||
data
|
||||
end
|
||||
|
||||
def login_prompt?
|
||||
return true if @recvd =~ @login_regex
|
||||
return 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.
|
||||
#
|
||||
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
|
||||
@ -204,78 +262,69 @@ module Exploit::Remote::Telnet
|
||||
datastore["PASSWORD"]
|
||||
end
|
||||
|
||||
#
|
||||
# Connect and login to the remote telnet server using the credentials
|
||||
# that have been supplied in the exploit options.
|
||||
#
|
||||
def connect_login(global = true, verbose = true)
|
||||
telsock = connect(global, verbose)
|
||||
|
||||
if !(user and pass)
|
||||
print_status("No username and password were supplied, unable to login")
|
||||
return false
|
||||
end
|
||||
|
||||
print_status("Authenticating as #{user} with password #{pass}...") if verbose
|
||||
res = send_user(user, telsock)
|
||||
|
||||
if (pass)
|
||||
print_status("Sending password...") if verbose
|
||||
res = send_pass(pass, telsock)
|
||||
if (res =~ /fail/i)
|
||||
print_status("The server rejected our password") if verbose
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return telsock
|
||||
end
|
||||
|
||||
#
|
||||
# This method logs in as the supplied user by transmitting the username
|
||||
#
|
||||
def send_user(user, nsock = self.sock)
|
||||
raw_send("#{user}\r\n", nsock)
|
||||
recv_telnet(nsock)
|
||||
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)
|
||||
raw_send("#{pass}\r\n", nsock)
|
||||
# Some servers send options after a successful login, so we have to
|
||||
# recv all of those before we see the initial prompt. Some servers
|
||||
# send a single newline before the next login prompt after a failed
|
||||
# login, so strip the response to check for that.
|
||||
resp = recv_telnet(nsock)
|
||||
while (resp and resp.strip.length == 0)
|
||||
resp = recv_telnet(nsock)
|
||||
got_prompt = wait_for(@password_regex)
|
||||
if not got_prompt
|
||||
print_error("#{rhost} - Something is wrong, didn't get a password prompt")
|
||||
end
|
||||
# Server didn't like our credentials if they give us nothing or a
|
||||
# failure message. Otherwise, assume login succeeded
|
||||
return (not resp or resp !~ /fail|incorrect/i)
|
||||
return send_recv("#{pass}\r\n")
|
||||
end
|
||||
|
||||
#
|
||||
# This method sends one command with zero or more parameters
|
||||
#
|
||||
def send_cmd(args, recv = true, nsock = self.sock)
|
||||
cmd = args.join(" ") + "\r\n"
|
||||
ret = raw_send(cmd, nsock)
|
||||
if (recv)
|
||||
return recv_telnet(nsock)
|
||||
end
|
||||
return ret
|
||||
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 = ''
|
||||
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
|
||||
|
@ -55,7 +55,7 @@ class Metasploit3 < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
|
||||
print_status("Starting host #{ip}")
|
||||
begin
|
||||
each_user_pass { |user, pass|
|
||||
try_user_pass(user, pass)
|
||||
@ -75,8 +75,23 @@ class Metasploit3 < Msf::Auxiliary
|
||||
disconnect
|
||||
connect
|
||||
end
|
||||
send_user(user)
|
||||
if (send_pass(pass))
|
||||
|
||||
if password_prompt?
|
||||
send_pass(pass)
|
||||
elsif login_prompt?
|
||||
send_user(user)
|
||||
# Ubuntu's telnetd gives a failure right away when trying to login
|
||||
# as root. Skipping this user in that instance saves a lot of time
|
||||
# but might unnecessarily skip users, especially on high-latency
|
||||
# networks.
|
||||
#return :next_user if not password_prompt?
|
||||
send_pass(pass)
|
||||
end
|
||||
|
||||
# if we don't have a pass or a login prompt, maybe it's just an
|
||||
# unauthenticated shell, so check for success anyway.
|
||||
|
||||
if (login_succeeded?)
|
||||
print_good("#{rhost} - SUCCESSFUL LOGIN #{user} : #{pass}")
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
|
Loading…
Reference in New Issue
Block a user