brute NSE Library — Nmap Scripting Engine documentation (original) (raw)

Script Arguments Functions

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:

** The actual engine doing the brute-forcing .

** Class used to return errors back to the engine.

** 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 objectssetRetry 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 callingsetReduce 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, thecheck 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 sampleDriver 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 Driveroff 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 calledsetReduce 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 =

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, likelocal 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 ofnmap.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.killstagnatedis set to false the Engine will continue after issuing a warning.

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

Author:

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. See description for more information.

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. See description for more information.

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

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