1
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:
James Lee 2010-02-09 19:07:02 +00:00
parent 23901c83ea
commit e2d70519d7
2 changed files with 120 additions and 56 deletions

View File

@ -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

View File

@ -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,