NSE LIB

Back to library
Official intrusive Brute

rlogin-brute

Performs brute force password auditing against the classic UNIX rlogin (remote login) service. This script must be run in privileged mode on UNIX because it must bind to a low source port number.

Ports

Any

Protocols

n/a

Attribution

Nmap Project

Usage

Copy the command and adjust the target or script arguments as needed.

nmap -p 513 --script rlogin-brute <ip>
Script Source Toggle

The full script source is stored with this entry and is hidden by default to keep the page easier to scan.

local brute = require "brute"
local creds = require "creds"
local math = require "math"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"

description=[[
Performs brute force password auditing against the classic UNIX rlogin (remote
login) service.  This script must be run in privileged mode on UNIX because it
must bind to a low source port number.
]]

---
-- @usage
-- nmap -p 513 --script rlogin-brute <ip>
--
-- @output
-- PORT    STATE SERVICE
-- 513/tcp open  login
-- | rlogin-brute:
-- |   Accounts
-- |     nmap:test - Valid credentials
-- |   Statistics
-- |_    Performed 4 guesses in 5 seconds, average tps: 0
--
-- @args rlogin-brute.timeout  socket timeout for connecting to rlogin (default 10s)

-- Version 0.1
-- Created 11/02/2011 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>


author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"brute", "intrusive"}

portrule = shortport.port_or_service(513, "login", "tcp")

-- The rlogin Driver, check the brute.lua documentation for more details
Driver = {

  -- creates a new Driver instance
  -- @param host table as received by the action function
  -- @param port table as received by the action function
  -- @return o instance of Driver
  new = function(self, host, port, options)
    local o = { host = host, port = port, timeout = options.timeout }
    setmetatable(o, self)
    self.__index = self
    return o
  end,

  -- connects to the rlogin service
  -- it sets the source port to a random value between 512 and 1023
  connect = function(self)
    local status, err
    self.socket = brute.new_socket()
    -- Let's make several attempts to bind to an unused well-known port
    for _ = 1, 10 do
      local srcport = math.random(512, 1023)
      status, err = self.socket:bind(nil, srcport)
      if status then
        self.socket:set_timeout(self.timeout)
        status, err = self.socket:connect(self.host, self.port)
        if status then
          -- socket:connect() succeeds even if mksock_bind_addr() fails.
          -- It just assigns an ephemeral port instead of our choice,
          -- so we need to check the actual source port afterwards.
          local lport
          status, err, lport = self.socket:get_info()
          if status then
            if lport == srcport then
              return status
            end
            status = false
            err = "Address already in use"
          end
        end
      end
      self.socket:close()
    end
    if not status then
      stdnse.debug2("Unable to bind to a well-known port (%s)", err)
    end
    return status
  end,

  login = function(self, username, password)
    local data = ("\0%s\0%s\0vt100/9600\0"):format(username, username)
    local status, err = self.socket:send(data)

    status, data = self.socket:receive()
    if (not(status)) then
      local err = brute.Error:new("Failed to read response from server")
      err:setRetry( true )
      return false, err
    end
    if ( data ~= "\0" ) then
      stdnse.debug2("ERROR: Expected null byte")
      local err = brute.Error:new( "Expected null byte" )
      err:setRetry( true )
      return false, err
    end

    status, data = self.socket:receive()
    if (not(status)) then
      local err = brute.Error:new("Failed to read response from server")
      err:setRetry( true )
      return false, err
    end
    if ( data ~= "Password: " ) then
      stdnse.debug2("ERROR: Expected password prompt")
      local err = brute.Error:new( "Expected password prompt" )
      err:setRetry( true )
      return false, err
    end

    status, err = self.socket:send(password .. "\r")
    status, data = self.socket:receive()
    if (not(status)) then
      local err = brute.Error:new("Failed to read response from server")
      err:setRetry( true )
      return false, err
    end

    status, data = self.socket:receive()
    if (not(status)) then
      local err = brute.Error:new("Failed to read response from server")
      err:setRetry( true )
      return false, err
    end

    if ( data:match("[Pp]assword") or data:match("[Ii]ncorrect") ) then
      return false, brute.Error:new( "Incorrect password" )
    end

    return true, creds.Account:new(username, password, creds.State.VALID)
  end,

  disconnect = function(self)
    return self.socket:close()
  end,
}

local arg_timeout = stdnse.parse_timespec(stdnse.get_script_args(SCRIPT_NAME .. ".timeout"))
arg_timeout = (arg_timeout or 10) * 1000

action = function(host, port)

  if ( not(nmap.is_privileged()) ) then
    stdnse.verbose1("Script must be run in privileged mode. Skipping.")
    return stdnse.format_output(false, "rlogin-brute needs Nmap to be run in privileged mode")
  end

  local options = {
    timeout = arg_timeout
  }

  local engine = brute.Engine:new(Driver, host, port, options)
  engine.options.script_name = SCRIPT_NAME
  local status, result = engine:start()
  return result
end

Overview

Performs brute force password auditing against the classic UNIX rlogin (remote login) service. This script must be run in privileged mode on UNIX because it must bind to a low source port number.