Академический Документы
Профессиональный Документы
Культура Документы
---------------------------------------------------------------
--
documentation.
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 {}
status, response
next_credential = self:get_next_credential()
retries = self.options.max_retries
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
"<empty>")
else
c = ("%s"):format(#password > 0 and password or "<empty>")
end
function(self, cvar )
condvar = nmap.condvar( cvar )
thread_data = self.threads[coroutine.running()]
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
if ( "function"
return false,
end
if ( "function"
return false,
end
~= type(usernames) ) then
"Invalid usernames iterator"
~= type(passwords) ) then
"Invalid passwords iterator"
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