brute NSE Library — Nmap Scripting Engine documentation (original) (raw)
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 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 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 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.killstagnated
is 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 (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
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)
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:
- true on success, false on failure
- 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:
- status true on success else false
- 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:
- status true on success, false on failure
- 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