15
--- -- 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. The number of threads can be defined using -- the brute.threads argument, it defaults to 10. -- -- The library contains the following classes: -- * <code>Engine</code> -- ** The actual engine doing the brute-forcing . -- * <code>Error</code> -- ** Class used to return errors back to the engine. -- * <code>Options</code> -- ** 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: -- -- <code> -- Driver:login = function( self, username, password ) -- Driver:check = function( self ) -- Driver:connect = function( self ) -- Driver:disconnect = function( self ) -- </code> -- -- The <code>login</code> 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 <code>creds.Account</code>. If the login was a failure it -- should return false and an <code>Error</code>. The driver can signal the -- Engine to retry a set of credentials by calling the Error objects -- <code>setRetry</code> method. It may also signal the Engine to abort all -- password guessing by calling the Error objects <code>setAbort</code> method. -- -- The following example code demonstrates how the Error object can be used. -- -- <code> -- -- 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" ) -- </code> -- -- The purpose of the <code>check</code> 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 -- <code>check</code> method returns true, on failure it returns false and the

Nmap Http_brute Lua

Embed Size (px)

DESCRIPTION

Nmap Http_brute Lua

Citation preview

  • ---

    -- 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. The number of threads can be defined using

    -- the brute.threads argument, it defaults to 10.

    --

    -- 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 an 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.

    --

    -- 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

    --

  • --

    -- For a complete example of a brute implementation consult the

    -- svn-brute.nse or vnc-brute.nse scripts

    --

    -- @args brute.useraspass guess the username as password for each user

    -- (default: true)

    -- @args brute.emptypass guess an empty password for each user

    -- (default: false)

    -- @args brute.unique make sure that each password is only guessed once

    -- (default: true)

    -- @args brute.firstonly stop guessing after first password is found

    -- (default: false)

    -- @args brute.passonly iterate over passwords only for services that provide

    -- only a password for authentication. (default: false)

    -- @args brute.retries the number of times to retry if recoverable failures

    -- occur. (default: 3)

    -- @args brute.delay the number of seconds to wait between guesses (default: 0)

    -- @args brute.threads the number of initial worker threads, the number of

    -- active threads will be automatically adjusted.

    -- @args 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.

    -- @args brute.credfile a file containing username and password pairs delimited

    -- by '/'

    -- @args brute.guesses the number of guesses to perform against each account.

    -- (default: 0 (unlimited)). The argument can be used to prevent account

    -- lockouts.

    --

    -- @author "Patrik Karlsson "

    -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html

    --

    -- Version 0.73

    -- Created 06/12/2010 - v0.1 - created by Patrik Karlsson

    -- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver

    --

    -- Revised 07/21/2010 - v0.3 - documented missing argument brute.mode

    -- Revised 07/23/2010 - v0.4 - fixed incorrect statistics and changed output to

    -- include statistics, and to display "no accounts

    -- found" message.

    -- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per

    -- David's request.

    -- Revised 08/30/2010 - v0.6 - added support for custom iterators and did some

    -- needed cleanup.

    -- Revised 06/19/2011 - v0.7 - added support for creds library [Patrik]

    -- Revised 07/07/2011 - v0.71- fixed some minor bugs, and changed credential

    -- iterator to use a file handle instead of table

    -- Revised 07/21/2011 - v0.72- added code to allow script reporting invalid

    -- (non existing) accounts using setInvalidAccount

    -- Revised 11/12/2011 - v0.73- added support for max guesses per account to

    -- prevent account lockouts.

    -- bugfix: added support for guessing the username

    -- as password per default, as suggested by the

  • -- documentation.

    local coroutine = require "coroutine"

    local creds = require "creds"

    local io = require "io"

    local nmap = require "nmap"

    local os = require "os"

    local stdnse = require "stdnse"

    local table = require "table"

    local unpwdb = require "unpwdb"

    _ENV = stdnse.module("brute", stdnse.seeall)

    -- Engine options that can be set by scripts

    -- Supported options are:

    -- * firstonly - stop after finding the first correct password

    -- (can be set using script-arg brute.firstonly)

    -- * passonly - guess passwords only, don't supply a username

    -- (can be set using script-arg brute.passonly)

    -- * max_retries - the amount of retries to do before aborting

    -- (can be set using script-arg brute.retries)

    -- * delay - sets the delay between attempts

    -- (can be set using script-arg brute.delay)

    -- * mode - can be set to either cred, user or pass and controls

    -- whether the engine should iterate over users, passwords

    -- or fetch a list of credentials from a single file.

    -- (can be set using script-arg brute.mode)

    -- * title - changes the title of the result table where the

    -- passwords are returned.

    -- * nostore - don't store the results in the credential library

    -- * max_guesses - the maximum amount of guesses to perform for each

    -- account.

    -- * useraspass - guesses the username as password (default: true)

    -- * emptypass - guesses an empty string as password (default: false)

    --

    Options = {

    new = function(self)

    local o = {}

    setmetatable(o, self)

    self.__index = self

    o.emptypass = self.checkBoolArg("brute.emptypass", false)

    o.useraspass = self.checkBoolArg("brute.useraspass", true)

    o.firstonly = self.checkBoolArg("brute.firstonly", false)

    o.passonly = self.checkBoolArg("brute.passonly", false)

    o.max_retries = tonumber( nmap.registry.args["brute.retries"] ) or 3

    o.delay = tonumber( nmap.registry.args["brute.delay"] ) or 0

    o.max_guesses = tonumber( nmap.registry.args["brute.guesses"] ) or 0

    return o

    end,

    --- Checks if a script argument is boolean true or false

    --

    -- @param arg string containing the name of the argument to check

    -- @param default boolean containing the default value

    -- @return boolean, true if argument evaluates to 1 or true, else false

    checkBoolArg = function( arg, default )

    local val = stdnse.get_script_args(arg) or default

    return (val == "true" or val==true or tonumber(val)==1)

    end,

    --- Sets the brute mode to either iterate over users or passwords

  • -- @see description for more information.

    --

    -- @param mode string containing either "user" or "password"

    -- @return status true on success else false

    -- @return err string containing the error message on failure

    setMode = function( self, mode )

    local modes = { "password", "user", "creds" }

    local supported = false

    for _, m in ipairs(modes) do

    if ( mode == m ) then supported = true end

    end

    if ( not(supported) ) then

    stdnse.debug1("ERROR: brute.options.setMode: mode %s not supported", mode)

    return false, "Unsupported mode"

    else

    self.mode = mode

    end

    return true

    end,

    --- Sets an option parameter

    --

    -- @param param string containing the parameter name

    -- @param value string containing the parameter value

    setOption = function( self, param, value ) self[param] = value end,

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

    --

    -- @param title string containing the title value

    setTitle = function(self, title) self.title = title end,

    }

    -- The account object which is to be reported back from each driver

    -- The Error class, is currently only used to flag for retries

    -- It also contains the error message, if one was returned from the driver.

    Error =

    {

    retry = false,

    new = function(self, msg)

    local o = { msg = msg, done = false }

    setmetatable(o, self)

    self.__index = self

    return o

    end,

    --- Is the error recoverable?

    --

    -- @return status true if the error is recoverable, false if not

    isRetry = function( self ) return self.retry end,

    --- Set the error as recoverable

    --

    -- @param r boolean true if the engine should attempt to retry the

    -- credentials, unset or false if not

    setRetry = function( self, r ) self.retry = r end,

    --- Set the error as abort all threads

    --

    -- @param b boolean true if the engine should abort guessing on all threads

  • setAbort = function( self, b ) self.abort = b end,

    --- Was the error abortable

    --

    -- @return status true if the driver flagged the engine to abort

    isAbort = function( self ) return self.abort end,

    --- Get the error message reported

    --

    -- @return msg string containing the error message

    getMessage = function( self ) return self.msg end,

    --- Is the thread done?

    --

    -- @return status true if done, false if not

    isDone = function( self ) return self.done end,

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

    --

    -- @param b boolean true if done, unset or false if not

    setDone = function( self, b ) self.done = b end,

    -- Marks the username as invalid, aborting further guessing.

    -- @param username

    setInvalidAccount = function(self, username)

    self.invalid_account = username

    end,

    -- Checks if the error reported the account as invalid.

    -- @return username string containing the invalid account

    isInvalidAccount = function(self)

    return self.invalid_account

    end,

    }

    -- The brute engine, doing all the nasty work

    Engine =

    {

    STAT_INTERVAL = 20,

    --- Creates a new Engine instance

    --

    -- @param driver, the driver class that should be instantiated

    -- @param host table as passed to the action method of the script

    -- @param port table as passed to the action method of the script

    -- @param options table containing any script specific options

    -- @return o new Engine instance

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

    local o = {

    driver = driver,

    host = host,

    port = port,

    driver_options = options,

    terminate_all = false,

    error = nil,

    counter = 0,

    threads = {},

    tps = {},

    iterator = nil ,

    usernames = usernames_iterator(),

    passwords = passwords_iterator(),

    found_accounts = {},

  • account_guesses = {},

    options = Options:new(),

    }

    setmetatable(o, self)

    self.__index = self

    o.max_threads = stdnse.get_script_args("brute.threads") or 10

    return o

    end,

    --- Sets the username iterator

    --

    -- @param usernameIterator function to set as a username iterator

    setUsernameIterator = function(self,usernameIterator)

    self.usernames = usernameIterator

    end,

    --- Sets the password iterator

    --

    -- @param passwordIterator function to set as a password iterator

    setPasswordIterator = function(self,passwordIterator)

    self.passwords = passwordIterator

    end,

    --- Limit the number of worker threads

    --

    -- @param max number containing the maximum number of allowed threads

    setMaxThreads = function( self, max ) self.max_threads = max end,

    --- Returns the number of non-dead threads

    --

    -- @return count number of non-dead threads

    threadCount = function( self )

    local count = 0

    for thread in pairs(self.threads) do

    if ( coroutine.status(thread) == "dead" ) then

    self.threads[thread] = nil

    else

    count = count + 1

    end

    end

    return count

    end,

    --- Calculates the number of threads that are actually doing any work

    --

    -- @return count number of threads performing activity

    activeThreads = function( self )

    local count = 0

    for thread, v in pairs(self.threads) do

    if ( v.guesses ~= nil ) then count = count + 1 end

    end

    return count

    end,

    --- Iterator wrapper used to iterate over all registered iterators

    --

    -- @return iterator function

    get_next_credential = function( self )

    local function next_credential ()

    for user, pass in self.iterator do

    -- makes sure the credentials have not been tested before

    self.used_creds = self.used_creds or {}

  • pass = pass or "nil"

    if ( not(self.used_creds[user..pass]) ) then

    self.used_creds[user..pass] = true

    coroutine.yield( user, pass )

    end

    end

    while true do coroutine.yield(nil, nil) end

    end

    return coroutine.wrap( next_credential )

    end,

    --- Does the actual authentication request

    --

    -- @return true on success, false on failure

    -- @return response Account on success, Error on failure

    doAuthenticate = function( self )

    local status, response

    local next_credential = self:get_next_credential()

    local retries = self.options.max_retries

    local username, password

    repeat

    local driver = self.driver:new( self.host, self.port, self.driver_options

    )

    status = driver:connect()

    -- Did we successfully connect?

    if ( status ) then

    if ( not(username) and not(password) ) then

    repeat

    username, password = next_credential()

    if ( not(username) and not(password) ) then

    driver:disconnect()

    self.threads[coroutine.running()].terminate = true

    return false

    end

    until ( ( not(self.found_accounts) or

    not(self.found_accounts[username]) ) and

    ( self.options.max_guesses == 0 or

    not(self.account_guesses[username]) or

    self.options.max_guesses > self.account_guesses[username] ) )

    -- increases the number of guesses for an account

    self.account_guesses[username] = self.account_guesses[username] and

    self.account_guesses[username] + 1 or 1

    end

    -- make sure that all threads locked in connect stat terminate quickly

    if ( Engine.terminate_all ) then

    driver:disconnect()

    return false

    end

    local c

    -- Do we have a username or not?

    if ( username and #username > 0 ) then

    c = ("%s/%s"):format(username, #password > 0 and password or

    "")

    else

    c = ("%s"):format(#password > 0 and password or "")

    end

  • local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or

    "Trying"

    stdnse.debug2("%s %s against %s:%d", msg, c, self.host.ip,

    self.port.number )

    status, response = driver:login( username, password )

    driver:disconnect()

    driver = nil

    end

    retries = retries - 1

    -- End if:

    -- * The guess was successful

    -- * The response was not set to retry

    -- * We've reached the maximum retry attempts

    until( status or ( response and not( response:isRetry() ) ) or retries == 0)

    -- Increase the amount of total guesses

    self.counter = self.counter + 1

    -- did we exhaust all retries, terminate and report?

    if ( retries == 0 ) then

    Engine.terminate_all = true

    self.error = "Too many retries, aborted ..."

    response = Error:new("Too many retries, aborted ...")

    response.abort = true

    end

    return status, response

    end,

    login = function(self, cvar )

    local condvar = nmap.condvar( cvar )

    local thread_data = self.threads[coroutine.running()]

    local interval_start = os.time()

    while( true ) do

    -- Should we terminate all threads?

    if ( self.terminate_all or thread_data.terminate ) then break end

    local status, response = self:doAuthenticate()

    if ( status ) then

    -- Prevent locked accounts from appearing several times

    if ( not(self.found_accounts) or self.found_accounts[response.username]

    == nil ) then

    if ( not(self.options.nostore) ) then

    creds.Credentials:new( self.options.script_name, self.host,

    self.port ):add(response.username, response.password, response.state )

    else

    self.credstore = self.credstore or {}

    table.insert(self.credstore, tostring(response) )

    end

    stdnse.debug1("Discovered account: %s", tostring(response))

    -- if we're running in passonly mode, and want to continue guessing

    -- we will have a problem as the username is always the same.

    -- in this case we don't log the account as found.

    if ( not(self.options.passonly) ) then

    self.found_accounts[response.username] = true

    end

  • -- Check if firstonly option was set, if so abort all threads

    if ( self.options.firstonly ) then self.terminate_all = true end

    end

    else

    if ( response and response:isAbort() ) then

    self.terminate_all = true

    self.error = response:getMessage()

    break

    elseif( response and response:isDone() ) then

    break

    elseif ( response and response:isInvalidAccount() ) then

    self.found_accounts[response:isInvalidAccount()] = true

    end

    end

    local timediff = (os.time() - interval_start)

    -- This thread made another guess

    thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or

    1 )

    -- Dump statistics at regular intervals

    if ( timediff > Engine.STAT_INTERVAL ) then

    interval_start = os.time()

    local tps = self.counter / ( os.time() - self.starttime )

    table.insert(self.tps, tps )

    stdnse.debug2("threads=%d,tps=%d", self:activeThreads(), tps )

    end

    -- if delay was specified, do sleep

    if ( self.options.delay > 0 ) then stdnse.sleep( self.options.delay ) end

    end

    condvar "signal"

    end,

    --- Starts the brute-force

    --

    -- @return status true on success, false on failure

    -- @return err string containing error message on failure

    start = function(self)

    local cvar = {}

    local condvar = nmap.condvar( cvar )

    assert(self.options.script_name, "SCRIPT_NAME was not set in

    options.script_name")

    assert(self.port.number and self.port.protocol, "Invalid port table

    detected")

    self.port.service = self.port.service or "unknown"

    -- Only run the check method if it exist. We should phase this out

    -- in favor of a check in the action function of the script

    if ( self.driver:new( self.host, self.port, self.driver_options ).check )

    then

    -- check if the driver is ready!

    local status, response = self.driver:new( self.host, self.port,

    self.driver_options ):check()

    if( not(status) ) then return false, response end

    end

    local usernames = self.usernames

    local passwords = self.passwords

  • if ( "function" ~= type(usernames) ) then

    return false, "Invalid usernames iterator"

    end

    if ( "function" ~= type(passwords) ) then

    return false, "Invalid passwords iterator"

    end

    local mode = self.options.mode or stdnse.get_script_args("brute.mode")

    -- if no mode was given, but a credfile is present, assume creds mode

    if ( not(mode) and stdnse.get_script_args("brute.credfile") ) then

    if ( stdnse.get_script_args("userdb") or

    stdnse.get_script_args("passdb") ) then

    return false, "\n ERROR: brute.credfile can't be used in combination

    with userdb/passdb"

    end

    mode = 'creds'

    end

    -- Are we guessing against a service that has no username (eg. VNC)

    if ( self.options.passonly ) then

    local function single_user_iter(next)

    local function next_user() coroutine.yield( "" ) end

    return coroutine.wrap(next_user)

    end

    -- only add this iterator if no other iterator was specified

    if self.iterator == nil then

    self.iterator = Iterators.user_pw_iterator( single_user_iter(),

    passwords )

    end

    elseif ( mode == 'creds' ) then

    local credfile = stdnse.get_script_args("brute.credfile")

    if ( not(credfile) ) then

    return false, "No credential file specified (see brute.credfile)"

    end

    local f = io.open( credfile, "r" )

    if ( not(f) ) then

    return false, ("Failed to open credfile (%s)"):format(credfile)

    end

    self.iterator = Iterators.credential_iterator( f )

    elseif ( mode and mode == 'user' ) then

    self.iterator = self.iterator or Iterators.user_pw_iterator( usernames,

    passwords )

    elseif( mode and mode == 'pass' ) then

    self.iterator = self.iterator or Iterators.pw_user_iterator( usernames,

    passwords )

    elseif ( mode ) then

    return false, ("Unsupported mode: %s"):format(mode)

    -- Default to the pw_user_iterator in case no iterator was specified

    elseif ( self.iterator == nil ) then

    self.iterator = Iterators.pw_user_iterator( usernames, passwords )

    end

    if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and

    self.options.useraspass ) then

    -- if we're only guessing passwords, this doesn't make sense

    if ( not(self.options.passonly) ) then

    self.iterator =

    unpwdb.concat_iterators(Iterators.pw_same_as_user_iterator(usernames,

    "lower"),self.iterator)

    end

  • end

    if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and

    self.options.emptypass ) then

    local function empty_pass_iter()

    local function next_pass()

    coroutine.yield( "" )

    end

    return coroutine.wrap(next_pass)

    end

    self.iterator = Iterators.account_iterator(usernames, empty_pass_iter(),

    mode or "pass")

    end

    self.starttime = os.time()

    -- Startup all worker threads

    for i=1, self.max_threads do

    local co = stdnse.new_thread( self.login, self, cvar )

    self.threads[co] = {}

    self.threads[co].running = true

    end

    -- wait for all threads to finish running

    while self:threadCount()>0 do condvar "wait" end

    local valid_accounts

    if ( not(self.options.nostore) ) then

    valid_accounts = creds.Credentials:new(self.options.script_name,

    self.host, self.port):getTable()

    else

    valid_accounts = self.credstore

    end

    local result = stdnse.output_table()

    -- Did we find any accounts, if so, do formatting

    if ( valid_accounts and #valid_accounts > 0 ) then

    result[self.options.title or "Accounts"] = valid_accounts

    else

    result.Accounts = "No valid accounts found"

    end

    -- calculate the average tps

    local sum = 0

    for _, v in ipairs( self.tps ) do sum = sum + v end

    local time_diff = ( os.time() - self.starttime )

    time_diff = ( time_diff == 0 ) and 1 or time_diff

    local tps = ( sum == 0 ) and ( self.counter / time_diff ) or ( sum /

    #self.tps )

    -- Add the statistics to the result

    result.Statistics = ("Performed %d guesses in %d seconds, average tps:

    %d"):format( self.counter, time_diff, tps )

    if ( self.options.max_guesses > 0 ) then

    -- we only display a warning if the guesses are equal to max_guesses

    for user, guesses in pairs(self.account_guesses) do

    if ( guesses == self.options.max_guesses ) then

    result.Information = ("Guesses restricted to %d tries per account to

    avoid lockout"):format(self.options.max_guesses)

    break

  • end

    end

    end

    -- Did any error occur? If so add this to the result.

    if ( self.error ) then

    result.ERROR = self.error

    return false, result

    end

    return true, result

    end,

    }

    --- Default username iterator that uses unpwdb

    --

    usernames_iterator = function()

    local status, usernames = unpwdb.usernames()

    if ( not(status) ) then return "Failed to load usernames" end

    return usernames

    end

    --- Default password iterator that uses unpwdb

    --

    passwords_iterator = function()

    local status, passwords = unpwdb.passwords()

    if ( not(status) ) then return "Failed to load passwords" end

    return passwords

    end

    Iterators = {

    --- Iterates over each user and password

    --

    -- @param users table/function containing list of users

    -- @param pass table/function containing list of passwords

    -- @param mode string, should be either 'user' or 'pass' and controls

    -- whether the users or passwords are in the 'outer' loop

    -- @return function iterator

    account_iterator = function(users, pass, mode)

    local function next_credential ()

    local outer, inner

    if "table" == type(users) then

    users = unpwdb.table_iterator(users)

    end

    if "table" == type(pass) then

    pass = unpwdb.table_iterator(pass)

    end

    if ( mode == 'pass' ) then

    outer, inner = pass, users

    elseif ( mode == 'user' ) then

    outer, inner = users, pass

    else

    return

    end

    for o in outer do

    for i in inner do

    if ( mode == 'pass' ) then

    coroutine.yield( i, o )

    else

    coroutine.yield( o, i )

  • end

    end

    inner("reset")

    end

    while true do coroutine.yield(nil, nil) end

    end

    return coroutine.wrap( next_credential )

    end,

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

    --

    -- @param users table/function containing list of users

    -- @param pass table/function containing list of passwords

    -- @return function iterator

    user_pw_iterator = function( users, pass )

    return Iterators.account_iterator( users, pass, "user" )

    end,

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

    --

    -- @param users table/function containing list of users

    -- @param pass table/function containing list of passwords

    -- @return function iterator

    pw_user_iterator = function( users, pass )

    return Iterators.account_iterator( users, pass, "pass" )

    end,

    --- An iterator that returns the username as password

    --

    -- @param users function returning the next user

    -- @param case string [optional] 'upper' or 'lower', specifies if user

    -- and password pairs should be case converted.

    -- @return function iterator

    pw_same_as_user_iterator = function( users, case )

    local function next_credential ()

    for user in users do

    if ( case == 'upper' ) then

    coroutine.yield(user, user:upper())

    elseif( case == 'lower' ) then

    coroutine.yield(user, user:lower())

    else

    coroutine.yield(user, user)

    end

    end

    users("reset")

    while true do coroutine.yield(nil, nil) end

    end

    return coroutine.wrap( next_credential )

    end,

    --- An iterator that returns the username and uppercase password

    --

    -- @param users table containing list of users

    -- @param pass table containing list of passwords

    -- @param mode string, should be either 'user' or 'pass' and controls

    -- whether the users or passwords are in the 'outer' loop

    -- @return function iterator

    pw_ucase_iterator = function( users, passwords, mode )

    local function next_credential ()

    for user, pass in Iterators.account_iterator(users, passwords, mode) do

    coroutine.yield( user, pass:upper() )

    end

  • while true do coroutine.yield(nil, nil) end

    end

    return coroutine.wrap( next_credential )

    end,

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

    --

    -- @param f file handle to file containing credentials separated by '/'

    -- @return function iterator

    credential_iterator = function( f )

    local function next_credential ()

    local c = {}

    for line in f:lines() do

    if ( not(line:match("^#!comment:")) ) then

    local trim = function(s) return s:match('^()%s*$') and '' or

    s:match('^%s*(.*%S)') end

    line = trim(line)

    local user, pass = line:match("^([^%/]*)%/(.*)$")

    coroutine.yield( user, pass )

    end

    end

    f:close()

    while true do coroutine.yield( nil, nil ) end

    end

    return coroutine.wrap( next_credential )

    end,

    unpwdb_iterator = function( mode )

    local status, users, passwords

    status, users = unpwdb.usernames()

    if ( not(status) ) then return end

    status, passwords = unpwdb.passwords()

    if ( not(status) ) then return end

    return Iterators.account_iterator( users, passwords, mode )

    end,

    }

    return _ENV;