Library brute

The brute library is an attempt to create a common framework for performing password guessing against remote services.

The library currently attempts to parallelize the guessing by starting a number of working threads and increasing that number gradually until brute.threads limit is reached. The starting number of threads can be set with brute.start argument, it defaults to 5. The brute.threads argument defaults to 20. It is worth noticing that the number of working threads will grow exponentially until any error occurs, after that the engine will switch to linear growth.

The library contains the following classes:

  • Engine
** The actual engine doing the brute-forcing .
  • Error
** Class used to return errors back to the engine.
  • Options
** Stores any options that should be used during brute-forcing.

In order to make use of the framework a script needs to implement a Driver class. The Driver class is then to be passed as a parameter to the Engine constructor, which creates a new instance for each guess. The Driver class SHOULD implement the following four methods:

Driver:login = function( self, username, password )
Driver:check = function( self )
Driver:connect = function( self )
Driver:disconnect = function( self )

The login method does not need a lot of explanation. The login function should return two parameters. If the login was successful it should return true and a creds.Account. If the login was a failure it should return false and a brute.Error. The driver can signal the Engine to retry a set of credentials by calling the Error objects setRetry method. It may also signal the Engine to abort all password guessing by calling the Error objects setAbort method. Finally, the driver can notify the Engine about protocol related exception (like the ftp code 421 "Too many connections") by calling setReduce method. The latter will signal the Engine to reduce the number of running worker threads.

The following example code demonstrates how the Error object can be used.

-- After a number of incorrect attempts VNC blocks us, so we abort
if ( not(status) and x:match("Too many authentication failures") ) then
  local err = brute.Error:new( data )
  -- signal the engine to abort
  err:setAbort( true )
  return false, err
elseif ( not(status) ) then
  local err = brute.Error:new( "VNC handshake failed" )
  -- This might be temporary, signal the engine to retry
  err:setRetry( true )
  return false, err
end
.
.
.
-- Return a simple error, no retry needed
return false, brute.Error:new( "Incorrect password" )

The purpose of the check method is to be able to determine whether the script has all the information it needs, before starting the brute force. It's the method where you should check, e.g., if the correct database or repository URL was specified or not. On success, the check method returns true, on failure it returns false and the brute force engine aborts.

NOTE: The check method is deprecated and will be removed from all scripts in the future. Scripts should do this check in the action function instead.

The connect method provides the framework with the ability to ensure that the thread can run once it has been dispatched a set of credentials. As the sockets in NSE are limited we want to limit the risk of a thread blocking, due to insufficient free sockets, after it has acquired a username and password pair.

The following sample code illustrates how to implement a sample Driver that sends each username and password over a socket.

Driver = {
  new = function(self, host, port, options)
    local o = {}
    setmetatable(o, self)
    self.__index = self
    o.host = host
    o.port = port
    o.options = options
    return o
  end,
  connect = function( self )
    self.socket = nmap.new_socket()
    return self.socket:connect( self.host, self.port )
  end,
  disconnect = function( self )
    return self.socket:close()
  end,
  check = function( self )
    return true
  end,
  login = function( self, username, password )
    local status, err, data
    status, err = self.socket:send( username .. ":" .. password)
    status, data = self.socket:receive_bytes(1)

    if ( data:match("SUCCESS") ) then
      return true, creds.Account:new(username, password, creds.State.VALID)
    end
    return false, brute.Error:new( "login failed" )
  end,
}

The following sample code illustrates how to pass the Driver off to the brute engine.

action = function(host, port)
  local options = { key1 = val1, key2 = val2 }
  local status, accounts = brute.Engine:new(Driver, host, port, options):start()
  if( not(status) ) then
    return accounts
  end
  return stdnse.format_output( true, accounts )
end

The Engine is written with performance and reasonable resource usage in mind and requires minimum extra work from a script developer. A trivial approach is to spawn as many working threads as possible regardless of network conditions, other scripts' needs, and protocol response. This indeed works well, but only in ideal conditions. In reality there might be several scripts running or only limited number of threads are allowed to use sockets at any given moment (as it is in Nmap). A more intelligent approach is to automate the management of Engine's running threads, so that performance of other scripts does not suffer because of exhaustive brute force work. This can be done on three levels: protocol, network, and resource level.

On the protocol level the developer should notify the Engine about connection restrictions imposed by a server that can be learned during a protocol communication. Like code 421 "To many connections" is used in FTP. Reasonably in such cases we would like to reduce the number of connections to this service, hence saving resources for other work and reducing the load on the target server. This can be done by returning an Error object with called setReduce method on it. The error will make the Engine reduce the number of running threads.

Following is an example how it can be done for FTP brute.

local line = <response from the server>

if(string.match(line, "^230")) then
  stdnse.debug1("Successful login: %s/%s", user, pass)
  return true, creds.Account:new( user, pass, creds.State.VALID)
elseif(string.match(line, "^530")) then
  return false, brute.Error:new( "Incorrect password" )
elseif(string.match(line, "^421")) then
  local err = brute.Error:new("Too many connections")
  err:setReduce(true)
  return false, err
elseif(string.match(line, "^220")) then
elseif(string.match(line, "^331")) then
else
  stdnse.debug1("WARNING: Unhandled response: %s", line)
  local err = brute.Error:new("Unhandled response")
  err:setRetry(true)
  return false, err
end

On the network level we want to catch errors that can occur because of network congestion or target machine specifics, say firewalled. These errors can be caught as return results of operations on sockets, like local status, err = socket.receive(). Asking a developer to relay such errors to the Engine is counterproductive, and it would lead to bloated scripts with lots of repetitive code. The Engine takes care of that with a little help from the developer. The only thing that needs to be done is to use brute.new_socket() instead of nmap.new_socket() when creating a socket in a script.

NOTE: A socket created with brute.new_socket() will behave as a regular socket when used without the brute library. The returned object is a BruteSocket instance, which can be treated as a regular socket object.

Example on creating "brute" socket.

connect = function( self )
  self.socket = brute.new_socket()
  local status, err = self.socket:connect(self.host, self.port)
  self.socket:set_timeout(arg_timeout)
  if(not(status)) then
    return false, brute.Error:new( "Couldn't connect to host: " .. err )
  end
  return true
end

On the resource level the Engine can query the current status of the NSE. As of the time of writing, the only parameter used is a number of threads waiting for connection (as was said before the NSE has a constraint on the number of concurrent connections due to performance reasons). With a running brute script the limit can be hit pretty fast, which can affect performance of other scripts. To mitigate this situation resource management strategy is used, and the Engine will reduce the number of working threads if there are any threads waiting for connection. As a result the preference for connection will be given to non brute scripts and if there are many brute scripts running simultaneously, then they will not exhaust resources unnecessarily. This feature is enabled by default and does not require any additional work from the developer.

Stagnation avoidance mechanism is implemented to alert users about services that might have failed during bruteforcing. The Engine will abort if all working threads have been experiencing connection errors during 100 consequentive iterations of the main thread loop. If brute.killstagnated is set to false the Engine will continue after issuing a warning.

For a complete example of a brute implementation consult the svn-brute.nse or vnc-brute.nse scripts

Author:

  • Patrik Karlsson <patrik@cqure.net>

Copyright © Same as Nmap--See https://nmap.org/book/man-legal.html

Source: https://svn.nmap.org/nmap/nselib/brute.lua

Script Arguments

brute.mode

can be user, pass or creds and determines what mode to run the engine in. * user - the unpwdb library is used to guess passwords, every password password is tried for each user. (The user iterator is in the outer loop) * pass - the unpwdb library is used to guess passwords, each password is tried for every user. (The password iterator is in the outer loop) * creds- a set of credentials (username and password pairs) are guessed against the service. This allows for lists of known or common username and password combinations to be tested. If no mode is specified and the script has not added any custom iterator the pass mode will be enabled.

brute.unique

make sure that each password is only guessed once (default: true)

brute.retries

the number of times to retry if recoverable failures occur. (default: 2)

brute.useraspass

guess the username as password for each user (default: true)

brute.start

the number of threads the engine will start with. (default: 5).

brute.threads

the number of initial worker threads, the number of active threads will be automatically adjusted.

brute.credfile

a file containing username and password pairs delimited by '/'

brute.emptypass

guess an empty password for each user (default: false)

brute.guesses

the number of guesses to perform against each account. (default: 0 (unlimited)). The argument can be used to prevent account lockouts.

brute.firstonly

stop guessing after first password is found (default: false)

brute.delay

the number of seconds to wait between guesses (default: 0)

brute.passonly

iterate over passwords only for services that provide only a password for authentication. (default: false)

Functions

account_iterator (users, pass, mode)

Iterates over each user and password

activeThreads (self)

Calculates the number of threads that are actually doing any work

add (self, obj)

Adds new item to the vault (if possible)

addWorker (self, cvar)

Adds new worker thread using start function

checkBoolArg (arg, default)

Checks if a script argument is boolean true or false

credential_iterator (f)

Credential iterator (for default or known user/pass combinations)

doAuthenticate (self)

Does the actual authentication request

get_next_credential (self)

Iterator wrapper used to iterate over all registered iterators

getMessage (self)

Get the error message reported

isAbort (self)

Was the error abortable

isDone (self)

Is the thread done?

isReduce (self)

Checks if the error signals to reduce the number of running threads

isRetry (self)

Is the error recoverable?

new (self, driver, host, port, options)

Creates a new Engine instance

passwords_iterator ()

Default password iterator that uses unpwdb

pw_same_as_user_iterator (users, case)

An iterator that returns the username as password

pw_ucase_iterator (users, passwords, mode, pass)

An iterator that returns the username and uppercase password

pw_user_iterator (users, pass)

Try each user for each password (password in outer loop)

setAbort (self, b)

Set the error as abort all threads

setDone (self, b)

Signals the engine that the thread is done and should be terminated

setMaxThreads (self, max)

Limit the number of worker threads

setMode (self, mode)

Sets the brute mode to either iterate over users or passwords

setOption (self, param, value)

Sets an option parameter

setPasswordIterator (self, passwordIterator)

Sets the password iterator

setReduce (self, r)

Set the error as reduce the number of running threads

setRetry (self, r)

Set the error as recoverable

setTitle (self, title)

Set an alternate title for the result output (default: Accounts)

setUsernameIterator (self, usernameIterator)

Sets the username iterator

start (self)

Starts the brute-force

threadCount (self)

Returns the number of non-dead threads

user_pw_iterator (users, pass)

Try each password for each user (user in outer loop)

usernames_iterator ()

Default username iterator that uses unpwdb

Functions

account_iterator (users, pass, mode)

Iterates over each user and password

Parameters

users
table/function containing list of users
pass
table/function containing list of passwords
mode
string, should be either 'user' or 'pass' and controls whether the users or passwords are in the 'outer' loop

Return value:

function iterator
activeThreads (self)

Calculates the number of threads that are actually doing any work

Parameters

self
 

Return value:

count number of threads performing activity
add (self, obj)

Adds new item to the vault (if possible)

Parameters

self
 
obj
 

Return value:

true if insert is successful, false if the vault is full
addWorker (self, cvar)

Adds new worker thread using start function

Parameters

self
 
cvar
 

Return value:

new thread object
checkBoolArg (arg, default)

Checks if a script argument is boolean true or false

Parameters

arg
string containing the name of the argument to check
default
boolean containing the default value

Return value:

boolean, true if argument evaluates to 1 or true, else false
credential_iterator (f)

Credential iterator (for default or known user/pass combinations)

Parameters

f
file handle to file containing credentials separated by '/'

Return value:

function iterator
doAuthenticate (self)

Does the actual authentication request

Parameters

self
 

Return values:

  1. true on success, false on failure
  2. response Account on success, Error on failure
get_next_credential (self)

Iterator wrapper used to iterate over all registered iterators

Parameters

self
 

Return value:

iterator function
getMessage (self)

Get the error message reported

Parameters

self
 

Return value:

msg string containing the error message
isAbort (self)

Was the error abortable

Parameters

self
 

Return value:

status true if the driver flagged the engine to abort
isDone (self)

Is the thread done?

Parameters

self
 

Return value:

status true if done, false if not
isReduce (self)

Checks if the error signals to reduce the number of running threads

Parameters

self
 

Return value:

status true if reduce, false otherwise
isRetry (self)

Is the error recoverable?

Parameters

self
 

Return value:

status true if the error is recoverable, false if not
new (self, driver, host, port, options)

Creates a new Engine instance

Parameters

self
 
driver
 
host
table as passed to the action method of the script
port
table as passed to the action method of the script
options
table containing any script specific options

Return value:

o new Engine instance
passwords_iterator ()

Default password iterator that uses unpwdb

pw_same_as_user_iterator (users, case)

An iterator that returns the username as password

Parameters

users
function returning the next user
case
string [optional] 'upper' or 'lower', specifies if user and password pairs should be case converted.

Return value:

function iterator
pw_ucase_iterator (users, passwords, mode, pass)

An iterator that returns the username and uppercase password

Parameters

users
table containing list of users
passwords
 
mode
string, should be either 'user' or 'pass' and controls whether the users or passwords are in the 'outer' loop
pass
table containing list of passwords

Return value:

function iterator
pw_user_iterator (users, pass)

Try each user for each password (password in outer loop)

Parameters

users
table/function containing list of users
pass
table/function containing list of passwords

Return value:

function iterator
setAbort (self, b)

Set the error as abort all threads

Parameters

self
 
b
boolean true if the engine should abort guessing on all threads
setDone (self, b)

Signals the engine that the thread is done and should be terminated

Parameters

self
 
b
boolean true if done, unset or false if not
setMaxThreads (self, max)

Limit the number of worker threads

Parameters

self
 
max
number containing the maximum number of allowed threads
setMode (self, mode)

Sets the brute mode to either iterate over users or passwords

Parameters

self
 
mode
string containing either "user" or "password"

Return values:

  1. status true on success else false
  2. err string containing the error message on failure

See also:

setOption (self, param, value)

Sets an option parameter

Parameters

self
 
param
string containing the parameter name
value
string containing the parameter value
setPasswordIterator (self, passwordIterator)

Sets the password iterator

Parameters

self
 
passwordIterator
function to set as a password iterator
setReduce (self, r)

Set the error as reduce the number of running threads

Parameters

self
 
r
boolean true if should reduce, unset or false if not
setRetry (self, r)

Set the error as recoverable

Parameters

self
 
r
boolean true if the engine should attempt to retry the credentials, unset or false if not
setTitle (self, title)

Set an alternate title for the result output (default: Accounts)

Parameters

self
 
title
string containing the title value
setUsernameIterator (self, usernameIterator)

Sets the username iterator

Parameters

self
 
usernameIterator
function to set as a username iterator
start (self)

Starts the brute-force

Parameters

self
 

Return values:

  1. status true on success, false on failure
  2. err string containing error message on failure
threadCount (self)

Returns the number of non-dead threads

Parameters

self
 

Return value:

count number of non-dead threads
user_pw_iterator (users, pass)

Try each password for each user (user in outer loop)

Parameters

users
table/function containing list of users
pass
table/function containing list of passwords

Return value:

function iterator
usernames_iterator ()

Default username iterator that uses unpwdb