mirror of
https://github.com/nmap/nmap.git
synced 2025-12-21 23:19:03 +00:00
Changed the code to be more iterator oriented. Scripts can now add their own
iterators. Mutliple iterators may be added and the library provides a number of common ones. [Patrik]
This commit is contained in:
491
nselib/brute.lua
491
nselib/brute.lua
@@ -66,6 +66,10 @@
|
|||||||
-- <code>check</code> method returns true, on failure it returns false and the
|
-- <code>check</code> method returns true, on failure it returns false and the
|
||||||
-- brute force engine aborts.
|
-- brute force engine aborts.
|
||||||
--
|
--
|
||||||
|
-- NOTE: The <code>check</code> method is deprecated and will be removed from
|
||||||
|
-- all scripts in the future. Scripts should do this check in the action
|
||||||
|
-- function instead.
|
||||||
|
--
|
||||||
-- The <code>connect</code> method provides the framework with the ability to
|
-- The <code>connect</code> method provides the framework with the ability to
|
||||||
-- ensure that the thread can run once it has been dispatched a set of
|
-- 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
|
-- credentials. As the sockets in NSE are limited we want to limit the risk of
|
||||||
@@ -143,12 +147,24 @@
|
|||||||
-- @args brute.delay the number of seconds to wait between guesses (default: 0)
|
-- @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
|
-- @args brute.threads the number of initial worker threads, the number of
|
||||||
-- active threads will be automatically adjusted.
|
-- active threads will be automatically adjusted.
|
||||||
-- @args brute.mode can be user or pass and determines if passwords are guessed
|
-- @args brute.mode can be user, pass or creds and determines what mode to run
|
||||||
-- against users (user) or users against passwords (pass).
|
-- the engine in.
|
||||||
-- (default: pass)
|
-- * 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 '/'
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Version 0.5
|
-- Version 0.6
|
||||||
-- Created 06/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
-- Created 06/12/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
-- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver
|
-- Revised 07/13/2010 - v0.2 - added connect, disconnect methods to Driver
|
||||||
-- <patrik@cqure.net>
|
-- <patrik@cqure.net>
|
||||||
@@ -158,21 +174,21 @@
|
|||||||
-- found" message.
|
-- found" message.
|
||||||
-- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per
|
-- Revised 08/14/2010 - v0.5 - added some documentation and smaller changes per
|
||||||
-- David's request.
|
-- David's request.
|
||||||
|
-- Revised 08/30/2010 - v0.6 - added support for custom iterators and did some
|
||||||
|
-- needed cleanup.
|
||||||
|
|
||||||
module(... or "brute", package.seeall)
|
module(... or "brute", package.seeall)
|
||||||
require 'unpwdb'
|
require 'unpwdb'
|
||||||
|
require 'datafiles'
|
||||||
|
|
||||||
-- Options that can be set through --script-args
|
-- Options that can be set through --script-args
|
||||||
Options = {
|
Options = {
|
||||||
|
|
||||||
mode = "password",
|
|
||||||
|
|
||||||
new = function(self)
|
new = function(self)
|
||||||
local o = {}
|
local o = {}
|
||||||
setmetatable(o, self)
|
setmetatable(o, self)
|
||||||
self.__index = self
|
self.__index = self
|
||||||
o.user_as_password = self.checkBoolArg("brute.useraspass", true)
|
|
||||||
o.check_unique = self.checkBoolArg("brute.unique", true)
|
|
||||||
o.firstonly = self.checkBoolArg("brute.firstonly", false)
|
o.firstonly = self.checkBoolArg("brute.firstonly", false)
|
||||||
o.passonly = self.checkBoolArg("brute.passonly", false)
|
o.passonly = self.checkBoolArg("brute.passonly", false)
|
||||||
o.max_retries = tonumber( nmap.registry.args["brute.retries"] ) or 3
|
o.max_retries = tonumber( nmap.registry.args["brute.retries"] ) or 3
|
||||||
@@ -189,13 +205,8 @@ Options = {
|
|||||||
checkBoolArg = function( arg, default )
|
checkBoolArg = function( arg, default )
|
||||||
local val = nmap.registry.args[arg]
|
local val = nmap.registry.args[arg]
|
||||||
|
|
||||||
if ( not(val) ) then
|
if ( not(val) ) then return default end
|
||||||
return default
|
return ( val == "true" or val=="1" ) and true or false
|
||||||
elseif ( val == "true" or val=="1" ) then
|
|
||||||
return true
|
|
||||||
else
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Sets the brute mode to either iterate over users or passwords
|
--- Sets the brute mode to either iterate over users or passwords
|
||||||
@@ -218,9 +229,7 @@ Options = {
|
|||||||
--
|
--
|
||||||
-- @param param string containing the parameter name
|
-- @param param string containing the parameter name
|
||||||
-- @param value string containing the parameter value
|
-- @param value string containing the parameter value
|
||||||
setOption = function( self, param, value )
|
setOption = function( self, param, value ) self[param] = value end,
|
||||||
self[param] = value
|
|
||||||
end,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -290,37 +299,40 @@ Error =
|
|||||||
end,
|
end,
|
||||||
|
|
||||||
--- Is the error recoverable?
|
--- Is the error recoverable?
|
||||||
isRetry = function( self )
|
--
|
||||||
return self.retry
|
-- @return status true if the error is recoverable, false if not
|
||||||
end,
|
isRetry = function( self ) return self.retry end,
|
||||||
|
|
||||||
--- Set the error as recoverable
|
--- Set the error as recoverable
|
||||||
setRetry = function( self, r )
|
--
|
||||||
self.retry = r
|
-- @param r boolean true if the engine should attempt to retry the
|
||||||
end,
|
-- credentials, unset or false if not
|
||||||
|
setRetry = function( self, r ) self.retry = r end,
|
||||||
|
|
||||||
--- Set the error as abort all threads
|
--- Set the error as abort all threads
|
||||||
setAbort = function( self, b )
|
--
|
||||||
self.abort = b
|
-- @param b boolean true if the engine should abort guessing on all threads
|
||||||
end,
|
setAbort = function( self, b ) self.abort = b end,
|
||||||
|
|
||||||
--- Was the error abortable
|
--- Was the error abortable
|
||||||
isAbort = function( self )
|
--
|
||||||
return self.abort
|
-- @return status true if the driver flagged the engine to abort
|
||||||
end,
|
isAbort = function( self ) return self.abort end,
|
||||||
|
|
||||||
--- Get the error message reported
|
--- Get the error message reported
|
||||||
getMessage = function( self )
|
--
|
||||||
return self.msg
|
-- @return msg string containing the error message
|
||||||
end,
|
getMessage = function( self ) return self.msg end,
|
||||||
|
|
||||||
isThreadDone = function( self )
|
--- Is the thread done?
|
||||||
return self.done
|
--
|
||||||
end,
|
-- @return status true if done, false if not
|
||||||
|
isDone = function( self ) return self.done end,
|
||||||
|
|
||||||
setDone = function( self, b )
|
--- Signals the engine that the thread is done and should be terminated
|
||||||
self.done = b
|
--
|
||||||
end,
|
-- @param b boolean true if done, unset or false if not
|
||||||
|
setDone = function( self, b ) self.done = b end,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,17 +362,31 @@ Engine =
|
|||||||
o.threads = {}
|
o.threads = {}
|
||||||
o.counter = 0
|
o.counter = 0
|
||||||
o.max_threads = tonumber(nmap.registry.args["brute.threads"]) or 10
|
o.max_threads = tonumber(nmap.registry.args["brute.threads"]) or 10
|
||||||
|
o.iterators = {}
|
||||||
o.error = nil
|
o.error = nil
|
||||||
o.tps = {}
|
o.tps = {}
|
||||||
return o
|
return o
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
--- Adds an iterator to the list
|
||||||
|
--
|
||||||
|
-- @param iterator function to add to the list
|
||||||
|
addIterator = function( self, iterator )
|
||||||
|
table.insert( self.iterators, iterator )
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Sets the engine running mode
|
||||||
|
--
|
||||||
|
-- @param mode string, one of either "user", "creds" or "pass"
|
||||||
|
setMode = function( self, mode )
|
||||||
|
mode = ( mode == "user" or mode == "creds" or mode == "pass" ) and mode or nil
|
||||||
|
assert(mode, ("Unsupported mode: (%s)"):format(mode))
|
||||||
|
end,
|
||||||
|
|
||||||
--- Limit the number of worker threads
|
--- Limit the number of worker threads
|
||||||
--
|
--
|
||||||
-- @param max number containing the maximum number of allowed threads
|
-- @param max number containing the maximum number of allowed threads
|
||||||
setMaxThreads = function( self, max )
|
setMaxThreads = function( self, max ) self.max_threads = max end,
|
||||||
self.max_threads = max
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Returns the number of non-dead threads
|
--- Returns the number of non-dead threads
|
||||||
--
|
--
|
||||||
@@ -383,35 +409,53 @@ Engine =
|
|||||||
-- @return count number of threads performing activity
|
-- @return count number of threads performing activity
|
||||||
activeThreads = function( self )
|
activeThreads = function( self )
|
||||||
local count = 0
|
local count = 0
|
||||||
|
|
||||||
for thread, v in pairs(self.threads) do
|
for thread, v in pairs(self.threads) do
|
||||||
if ( v.guesses ~= nil ) then
|
if ( v.guesses ~= nil ) then count = count + 1 end
|
||||||
count = count + 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return count
|
return count
|
||||||
end,
|
end,
|
||||||
|
|
||||||
|
--- Iterator wrapper used to iterate over all registered iterators
|
||||||
|
--
|
||||||
|
-- @return iterator function
|
||||||
|
get_next_credential = function( self )
|
||||||
|
local function next_credential ()
|
||||||
|
local used_creds = {}
|
||||||
|
-- iterate over all credential iterators
|
||||||
|
for _, iter in ipairs( self.iterators ) do
|
||||||
|
for user, pass in iter do
|
||||||
|
-- makes sure the credentials have not been tested before
|
||||||
|
if ( not(used_creds[user..pass]) ) then
|
||||||
|
used_creds[user..pass] = true
|
||||||
|
coroutine.yield( user, pass )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
while true do coroutine.yield(nil, nil) end
|
||||||
|
end
|
||||||
|
return coroutine.wrap( next_credential )
|
||||||
|
end,
|
||||||
|
|
||||||
--- Does the actual authentication request
|
--- Does the actual authentication request
|
||||||
--
|
--
|
||||||
-- @return true on success, false on failure
|
-- @return true on success, false on failure
|
||||||
-- @return response Account on success, Error on failure
|
-- @return response Account on success, Error on failure
|
||||||
doAuthenticate = function( self )
|
doAuthenticate = function( self )
|
||||||
|
|
||||||
local driver, status, response, creds
|
local status, response
|
||||||
local username, password
|
local next_credential = self:get_next_credential()
|
||||||
local retries = self.options.max_retries
|
local retries = self.options.max_retries
|
||||||
local msg
|
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
driver = self.driver:new( self.host, self.port, self.driver_options )
|
local driver = self.driver:new( self.host, self.port, self.driver_options )
|
||||||
status = driver:connect()
|
status = driver:connect()
|
||||||
|
|
||||||
-- Did we succesfully connect?
|
-- Did we succesfully connect?
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
|
|
||||||
|
local username, password
|
||||||
if ( not(username) and not(password) ) then
|
if ( not(username) and not(password) ) then
|
||||||
username, password = self.iterator()
|
username, password = next_credential()
|
||||||
end
|
end
|
||||||
|
|
||||||
-- make sure that all threads locked in connect stat terminate quickly
|
-- make sure that all threads locked in connect stat terminate quickly
|
||||||
@@ -433,6 +477,7 @@ Engine =
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local creds
|
||||||
-- Do we have a username or not?
|
-- Do we have a username or not?
|
||||||
if ( username and #username > 0 ) then
|
if ( username and #username > 0 ) then
|
||||||
creds = ("%s/%s"):format(username, #password > 0 and password or "<empty>")
|
creds = ("%s/%s"):format(username, #password > 0 and password or "<empty>")
|
||||||
@@ -440,13 +485,7 @@ Engine =
|
|||||||
creds = ("%s"):format(#password > 0 and password or "<empty>")
|
creds = ("%s"):format(#password > 0 and password or "<empty>")
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Is this the first try?
|
local msg = ( retries ~= self.options.max_retries ) and "Re-trying" or "Trying"
|
||||||
if ( retries ~= self.options.max_retries ) then
|
|
||||||
msg = "Re-trying"
|
|
||||||
else
|
|
||||||
msg = "Trying"
|
|
||||||
end
|
|
||||||
|
|
||||||
stdnse.print_debug(2, "%s %s against %s:%d", msg, creds, self.host.ip, self.port.number )
|
stdnse.print_debug(2, "%s %s against %s:%d", msg, creds, self.host.ip, self.port.number )
|
||||||
status, response = driver:login( username, password )
|
status, response = driver:login( username, password )
|
||||||
|
|
||||||
@@ -467,25 +506,20 @@ Engine =
|
|||||||
Engine.terminate_all = true
|
Engine.terminate_all = true
|
||||||
self.error = "Too many retries, aborted ..."
|
self.error = "Too many retries, aborted ..."
|
||||||
end
|
end
|
||||||
|
|
||||||
return status, response
|
return status, response
|
||||||
end,
|
end,
|
||||||
|
|
||||||
login = function(self, valid_accounts )
|
login = function(self, valid_accounts )
|
||||||
local username, password, creds
|
|
||||||
local status, response, driver
|
|
||||||
local interval_start, timediff = os.time(), nil
|
|
||||||
local condvar = nmap.condvar( valid_accounts )
|
local condvar = nmap.condvar( valid_accounts )
|
||||||
local thread_data = self.threads[coroutine.running()]
|
local thread_data = self.threads[coroutine.running()]
|
||||||
|
local interval_start = os.time()
|
||||||
|
|
||||||
while( true ) do
|
while( true ) do
|
||||||
|
|
||||||
-- Should we terminate all threads?
|
-- Should we terminate all threads?
|
||||||
if ( Engine.terminate_all or thread_data.terminate ) then
|
if ( Engine.terminate_all or thread_data.terminate ) then break end
|
||||||
break
|
|
||||||
end
|
|
||||||
|
|
||||||
status, response = self:doAuthenticate()
|
local status, response = self:doAuthenticate()
|
||||||
|
|
||||||
if ( status ) then
|
if ( status ) then
|
||||||
-- Prevent locked accounts from appearing several times
|
-- Prevent locked accounts from appearing several times
|
||||||
@@ -497,24 +531,23 @@ Engine =
|
|||||||
end
|
end
|
||||||
table.insert( valid_accounts, response:toString() )
|
table.insert( valid_accounts, response:toString() )
|
||||||
self.found_accounts[response.username] = true
|
self.found_accounts[response.username] = true
|
||||||
|
|
||||||
-- Check if firstonly option was set, if so abort all threads
|
-- Check if firstonly option was set, if so abort all threads
|
||||||
if ( self.options.firstonly ) then
|
if ( self.options.firstonly ) then Engine.terminate_all = true end
|
||||||
Engine.terminate_all = true
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
if ( response and response:isAbort() ) then
|
if ( response and response:isAbort() ) then
|
||||||
Engine.terminate_all = true
|
Engine.terminate_all = true
|
||||||
self.error = response:getMessage()
|
self.error = response:getMessage()
|
||||||
break
|
break
|
||||||
elseif( response and response:isThreadDone() ) then
|
elseif( response and response:isDone() ) then
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Increase the amount of total guesses
|
-- Increase the amount of total guesses
|
||||||
self.counter = self.counter + 1
|
self.counter = self.counter + 1
|
||||||
timediff = (os.time() - interval_start)
|
local timediff = (os.time() - interval_start)
|
||||||
|
|
||||||
-- This thread made another guess
|
-- This thread made another guess
|
||||||
thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 )
|
thread_data.guesses = ( thread_data.guesses and thread_data.guesses + 1 or 1 )
|
||||||
@@ -528,10 +561,7 @@ Engine =
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- if delay was speciefied, do sleep
|
-- if delay was speciefied, do sleep
|
||||||
if ( self.options.delay > 0 ) then
|
if ( self.options.delay > 0 ) then stdnse.sleep( self.options.delay ) end
|
||||||
stdnse.sleep( self.options.delay )
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
end
|
||||||
condvar("broadcast")
|
condvar("broadcast")
|
||||||
end,
|
end,
|
||||||
@@ -541,45 +571,62 @@ Engine =
|
|||||||
-- @return status true on success, false on failure
|
-- @return status true on success, false on failure
|
||||||
-- @return err string containing error message on failure
|
-- @return err string containing error message on failure
|
||||||
start = function(self)
|
start = function(self)
|
||||||
local status, usernames, passwords, response
|
|
||||||
local result, valid_accounts, stats = {}, {}, {}
|
local result, valid_accounts, stats = {}, {}, {}
|
||||||
local condvar = nmap.condvar( valid_accounts )
|
local condvar = nmap.condvar( valid_accounts )
|
||||||
local sum, tps, time_diff = 0, 0, 0
|
|
||||||
|
|
||||||
|
-- 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 ).check ) then
|
||||||
-- check if the driver is ready!
|
-- check if the driver is ready!
|
||||||
status, response = self.driver:new( self.host, self.port ):check()
|
local status, response = self.driver:new( self.host, self.port ):check()
|
||||||
if( not(status) ) then
|
if( not(status) ) then return false, response end
|
||||||
return false, response
|
|
||||||
end
|
end
|
||||||
|
|
||||||
status, usernames = unpwdb.usernames()
|
local status, usernames = unpwdb.usernames()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then return false, "Failed to load usernames" end
|
||||||
return false, "Failed to load usernames"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- make sure we have a valid pw file
|
-- make sure we have a valid pw file
|
||||||
status, passwords = unpwdb.passwords()
|
local status, passwords = unpwdb.passwords()
|
||||||
if ( not(status) ) then
|
if ( not(status) ) then return false, "Failed to load passwords" end
|
||||||
return false, "Failed to load passwords"
|
|
||||||
end
|
local mode = stdnse.get_script_args("brute.mode")
|
||||||
|
|
||||||
-- Are we guessing against a service that has no username (eg. VNC)
|
-- Are we guessing against a service that has no username (eg. VNC)
|
||||||
if ( self.options.passonly ) then
|
if ( self.options.passonly ) then
|
||||||
local function single_user_iter(next)
|
local function single_user_iter(next)
|
||||||
local function next_user()
|
local function next_user() coroutine.yield( "" ) end
|
||||||
coroutine.yield( "" )
|
|
||||||
end
|
|
||||||
return coroutine.wrap(next_user)
|
return coroutine.wrap(next_user)
|
||||||
end
|
end
|
||||||
self.iterator = Engine.usrpwd_iterator( self, single_user_iter(), passwords )
|
table.insert( self.iterators, Iterators.user_pw_iterator( single_user_iter(), passwords ) )
|
||||||
elseif ( nmap.registry.args['brute.mode'] and nmap.registry.args['brute.mode'] == 'user' ) then
|
elseif ( mode == 'creds' ) then
|
||||||
self.iterator = Engine.usrpwd_iterator( self, usernames, passwords )
|
local credfile = stdnse.get_script_args("brute.credfile")
|
||||||
elseif( nmap.registry.args['brute.mode'] and nmap.registry.args['brute.mode'] == 'pass' ) then
|
if ( not(credfile) ) then
|
||||||
self.iterator = Engine.pwdusr_iterator( self, usernames, passwords )
|
return false, "No credential file specified"
|
||||||
elseif ( nmap.registry.args['brute.mode'] ) then
|
end
|
||||||
|
|
||||||
|
local f = io.open( credfile, "r" )
|
||||||
|
if ( not(f) ) then
|
||||||
|
return false, ("Failed to open credfile (%s)"):format(credfile)
|
||||||
|
end
|
||||||
|
local creds = {}
|
||||||
|
for line in f:lines() do
|
||||||
|
local trim = function(s) return s:match('^()%s*$') and '' or s:match('^%s*(.*%S)') end
|
||||||
|
line = trim(line)
|
||||||
|
local user, pass = line:match("^([^%/]*)%/(.*)$")
|
||||||
|
table.insert(creds, { [user]=pass } )
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert( self.iterators, Iterators.credential_iterator( creds ) )
|
||||||
|
elseif ( mode and mode == 'user' ) then
|
||||||
|
table.insert( self.iterators, Iterators.user_pw_iterator( usernames, passwords ) )
|
||||||
|
elseif( mode and mode == 'pass' ) then
|
||||||
|
table.insert( self.iterators, Iterators.pw_user_iterator( usernames, passwords ) )
|
||||||
|
elseif ( mode ) then
|
||||||
return false, ("Unsupported mode: %s"):format(nmap.registry.args['brute.mode'])
|
return false, ("Unsupported mode: %s"):format(nmap.registry.args['brute.mode'])
|
||||||
else
|
-- Default to the pw_user_iterator in case no iterator was specified
|
||||||
self.iterator = Engine.pwdusr_iterator( self, usernames, passwords )
|
elseif ( 0 == #self.iterators ) then
|
||||||
|
table.insert( self.iterators, Iterators.pw_user_iterator( usernames, passwords ) )
|
||||||
end
|
end
|
||||||
|
|
||||||
self.starttime = os.time()
|
self.starttime = os.time()
|
||||||
@@ -592,9 +639,7 @@ Engine =
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- wait for all threads to finnish running
|
-- wait for all threads to finnish running
|
||||||
while self:threadCount()>0 do
|
while self:threadCount()>0 do condvar("wait") end
|
||||||
condvar("wait")
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Did we find any accounts, if so, do formatting
|
-- Did we find any accounts, if so, do formatting
|
||||||
if ( #valid_accounts > 0 ) then
|
if ( #valid_accounts > 0 ) then
|
||||||
@@ -605,117 +650,163 @@ Engine =
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- calculate the average tps
|
-- calculate the average tps
|
||||||
for _, v in ipairs( self.tps ) do
|
local sum = 0
|
||||||
sum = sum + v
|
for _, v in ipairs( self.tps ) do sum = sum + v end
|
||||||
end
|
local time_diff = ( os.time() - self.starttime )
|
||||||
time_diff = ( os.time() - self.starttime )
|
time_diff = ( time_diff == 0 ) and 1 or time_diff
|
||||||
if ( time_diff == 0 ) then time_diff = 1 end
|
local tps = ( sum == 0 ) and ( self.counter / time_diff ) or ( sum / #self.tps )
|
||||||
if ( sum == 0 ) then
|
|
||||||
tps = self.counter / time_diff
|
|
||||||
else
|
|
||||||
tps = sum / #self.tps
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add the statistics to the result
|
-- Add the statistics to the result
|
||||||
table.insert(stats, ("Perfomed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) )
|
table.insert(stats, ("Perfomed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) )
|
||||||
stats.name = "Statistics"
|
stats.name = "Statistics"
|
||||||
table.insert( result, stats )
|
table.insert( result, stats )
|
||||||
|
|
||||||
if ( #result ) then
|
result = ( #result ) and stdnse.format_output( true, result ) or ""
|
||||||
result = stdnse.format_output( true, result )
|
|
||||||
else
|
|
||||||
result = ""
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Did any error occure? If so add this to the result.
|
-- Did any error occure? If so add this to the result.
|
||||||
if ( self.error ) then
|
if ( self.error ) then
|
||||||
result = result .. (" \n\n ERROR: %s"):format( self.error )
|
result = result .. (" \n\n ERROR: %s"):format( self.error )
|
||||||
return false, result
|
return false, result
|
||||||
end
|
end
|
||||||
|
|
||||||
return true, result
|
return true, result
|
||||||
end,
|
end,
|
||||||
|
|
||||||
--- Credential iterator, tries every user for each password
|
|
||||||
--
|
|
||||||
-- @param usernames iterator from unpwdb
|
|
||||||
-- @param passwords iterator from unpwdb
|
|
||||||
-- @return username string
|
|
||||||
-- @return password string
|
|
||||||
pwdusr_iterator = function(self, usernames, passwords)
|
|
||||||
local function next_password_username ()
|
|
||||||
local tested_creds = {}
|
|
||||||
|
|
||||||
-- should we check for same password as username
|
|
||||||
if ( self.options.user_as_password ) then
|
|
||||||
for username in usernames do
|
|
||||||
if ( not( tested_creds[username] ) ) then
|
|
||||||
tested_creds[username] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
tested_creds[username][username] = true
|
|
||||||
if ( not(self.found_accounts) or not(self.found_accounts[username]) ) then
|
|
||||||
coroutine.yield(username, username)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
usernames("reset")
|
|
||||||
|
|
||||||
for password in passwords do
|
|
||||||
for username in usernames do
|
|
||||||
if ( not(tested_creds[username]) ) then
|
|
||||||
tested_creds[username] = {}
|
|
||||||
end
|
|
||||||
if ( self.options.check_unique and not(tested_creds[username][password]) ) then
|
|
||||||
tested_creds[username][password] = true
|
|
||||||
if ( not(self.found_accounts) or not(self.found_accounts[username]) ) then
|
|
||||||
coroutine.yield(username, password)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
usernames("reset")
|
|
||||||
end
|
|
||||||
while true do coroutine.yield(nil, nil) end
|
|
||||||
end
|
|
||||||
return coroutine.wrap(next_password_username)
|
|
||||||
end,
|
|
||||||
|
|
||||||
--- Credential iterator, tries every password for each user
|
|
||||||
--
|
|
||||||
-- @param usernames iterator from unpwdb
|
|
||||||
-- @param passwords iterator from unpwdb
|
|
||||||
-- @return username string
|
|
||||||
-- @return password string
|
|
||||||
usrpwd_iterator = function(self, usernames, passwords)
|
|
||||||
local function next_username_password ()
|
|
||||||
local tested_creds = {}
|
|
||||||
|
|
||||||
for username in usernames do
|
|
||||||
-- set's up a table to track tested credentials
|
|
||||||
tested_creds[username] = {}
|
|
||||||
|
|
||||||
-- should we check for same password as username
|
|
||||||
if ( self.options.user_as_password and not(self.options.passonly) ) then
|
|
||||||
tested_creds[username][username:lower()] = true
|
|
||||||
if ( not(self.found_accounts) or not(self.found_accounts[username]) ) then
|
|
||||||
coroutine.yield(username, username:lower())
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for password in passwords do
|
|
||||||
if ( self.options.check_unique and not(tested_creds[username][password]) ) then
|
|
||||||
tested_creds[username][password] = true
|
|
||||||
if ( not(self.found_accounts) or not(self.found_accounts[username]) ) then
|
|
||||||
coroutine.yield(username, password)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
passwords("reset")
|
|
||||||
end
|
|
||||||
while true do coroutine.yield(nil, nil) end
|
|
||||||
end
|
|
||||||
return coroutine.wrap(next_username_password)
|
|
||||||
end,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Iterators = {
|
||||||
|
|
||||||
|
--- Iterates over each user and 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
|
||||||
|
account_iterator = function(users, pass, mode)
|
||||||
|
local function next_credential ()
|
||||||
|
local outer, inner
|
||||||
|
|
||||||
|
if ( mode == 'pass' ) then
|
||||||
|
outer, inner = pass, users
|
||||||
|
elseif ( mode == 'user' ) then
|
||||||
|
outer, inner = users, pass
|
||||||
|
else
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( 'table' == type(users) and 'table' == type(pass) ) then
|
||||||
|
for _, o in ipairs(outer) do
|
||||||
|
for _, i in ipairs(inner) do
|
||||||
|
if ( mode == 'pass' ) then
|
||||||
|
coroutine.yield( i, o )
|
||||||
|
else
|
||||||
|
coroutine.yield( o, i )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif ( 'function' == type(users) and 'function' == type(pass) ) then
|
||||||
|
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
|
||||||
|
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 containing list of users
|
||||||
|
-- @param pass table 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 containing list of users
|
||||||
|
-- @param pass table 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 table containing list of users
|
||||||
|
-- @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 ipairs(users) do
|
||||||
|
if ( case == 'upper' ) then
|
||||||
|
coroutine.yield( user:upper(), user:upper() )
|
||||||
|
elseif( case == 'lower' ) then
|
||||||
|
coroutine.yield( user:lower(), user:lower() )
|
||||||
|
else
|
||||||
|
coroutine.yield( user, user )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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 creds table containing username/pass combinations
|
||||||
|
-- the table should be of the following format
|
||||||
|
-- { ["user"] = "pass", ["user2"] = "pass2" }
|
||||||
|
-- @return function iterator
|
||||||
|
credential_iterator = function( creds )
|
||||||
|
local function next_credential ()
|
||||||
|
for _, item in ipairs(creds) do
|
||||||
|
for user, pass in pairs(item) do
|
||||||
|
coroutine.yield( user, pass )
|
||||||
|
end
|
||||||
|
end
|
||||||
|
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,
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user