lib/msf/core/auxiliary/login.rb
# -*- coding: binary -*-
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 negotiation module.
#
def initialize(info = {})
super
create_login_ivars
end
def create_login_ivars
# 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 |
^http | html |
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|
not\ allowed\ to\ log\ in
)/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)
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?(username=nil)
return true if(@recvd =~ @password_regex)
if username
return true if !(username.empty?) and @recvd.to_s.include?("#{username}'s")
end
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