Nmap Http_brute Lua

March 27, 2018 | Author: paolo the best(ia) | Category: Parameter (Computer Programming), Password, User (Computing), Control Flow, Computer Security


Comments



Description

---- 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 port.credentials.port ) -end. accounts = brute. password ) -local status. --.The <code>connect</code> method provides the framework with the ability to -. username. data -status. options) -local o = {} -setmetatable(o. ":" .. --. after it has acquired a -. Scripts should do this check in the action -. host.Error:new( "login failed" ) -end.socket:send( username .Account:new(username.end -.socket:receive_bytes(1) --if ( data:match("SUCCESS") ) then -return true.brute force engine aborts. self. err = self.username and password pair.} -.<code> -. brute. --. port.host.<code> -. -. -connect = function( self ) -self.socket = nmap.__index = self -o.</code> --.options = options -return o -end.</code> .off to the brute engine.format_output( true.socket:close() -end.host = host -o.action = function(host.Engine:new(Driver.Driver = { -new = function(self. password.NOTE: The <code>check</code> method is deprecated and will be removed from -. key2 = val2 } -local status.<code>Driver</code> that sends each username and password over a socket. --. options):start() -if( not(status) ) then -return accounts -end -return stdnse. -login = function( self. err. As the sockets in NSE are limited we want to limit the risk of -. creds. accounts ) -. -check = function( self ) -return true -end. creds.VALID) -end -return false.The following sample code illustrates how to pass the <code>Driver</code> -. -disconnect = function( self ) -return self.State.port = port -o. self) -self.a thread blocking. port) -local options = { key1 = val1. --.-.socket:connect( self.ensure that the thread can run once it has been dispatched a set of -. host. due to insufficient free sockets. data = self.all scripts in the future.. password) -status.new_socket() -return self.function instead.The following sample code illustrates how to implement a sample -. the unpwdb library is used to guess passwords.nse</code> or <code>vnc-brute.1 .firstonly stop guessing after first password is found (default: false) @args brute.delay the number of seconds to wait between guesses (default: 0) @args brute. and changed credential iterator to use a file handle instead of table Revised 07/21/2011 .2 .retries the number of times to retry if recoverable failures occur.passonly iterate over passwords only for services that provide only a password for authentication.added connect. bugfix: added support for guessing the username as password per default.org/book/man-legal.--------------------------------------------------------------- For a complete example of a brute implementation consult the <code>svn-brute.v0. disconnect methods to Driver <[email protected] make sure that each password is only guessed once (default: true) @args brute. as suggested by the .v0. Revised 08/14/2010 . This allows for lists of known or common username and password combinations to be tested.created by Patrik Karlsson <[email protected] a file containing username and password pairs delimited by '/' @args brute.fixed some minor bugs.added support for max guesses per account to prevent account lockouts.73.5 . the number of active threads will be automatically adjusted. every password password is tried for each user. each password is tried for every user.mode can be user.net> Revised 07/21/2010 . If no mode is specified and the script has not added any custom iterator the pass mode will be enabled.added code to allow script reporting invalid (non existing) accounts using setInvalidAccount Revised 11/12/2011 .71.7 . (default: false) @args brute. @args brute. @author "Patrik Karlsson <[email protected] guess the username as password for each user (default: true) @args brute.3 .v0.added support for custom iterators and did some needed cleanup. (default: 0 (unlimited)).a set of credentials (username and password pairs) are guessed against the service.6 . (default: 3) @args brute.net> Revised 07/13/2010 .v0.v0.emptypass guess an empty password for each user (default: false) @args brute.html Version 0. Revised 08/30/2010 .fixed incorrect statistics and changed output to include statistics. Revised 06/19/2011 .v0.documented missing argument brute.72.net>" @copyright Same as Nmap--See http://nmap. * user .4 .v0. @args brute.threads the number of initial worker threads.73 Created 06/12/2010 . and to display "no accounts found" message.v0. (The user iterator is in the outer loop) * pass . The argument can be used to prevent account lockouts.nse</code> scripts @args brute. pass or creds and determines what mode to run the engine in.v0. (The password iterator is in the outer loop) * creds.added support for creds library [Patrik] Revised 07/07/2011 .guesses the number of guesses to perform against each account.added some documentation and smaller changes per David's request.the unpwdb library is used to guess passwords.mode Revised 07/23/2010 . Supported options are: -* firstonly .guesses an empty string as password (default: false) -Options = { new = function(self) local o = {} setmetatable(o.checkBoolArg("brute.seeall) -.__index = self o. self) self. default ) local val = stdnse.sets the delay between attempts -(can be set using script-arg brute.-- documentation. -(can be set using script-arg brute.@param arg string containing the name of the argument to check -.passonly) -* max_retries . stdnse.checkBoolArg("brute.args["brute.delay = tonumber( nmap.@return boolean.max_guesses = tonumber( nmap.@param default boolean containing the default value -. true if argument evaluates to 1 or true.emptypass".Checks if a script argument is boolean true or false --. false) o.Sets the brute mode to either iterate over users or passwords .stop after finding the first correct password -(can be set using script-arg brute. passwords -or fetch a list of credentials from a single file. 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.mode) -* title .firstonly = self.checkBoolArg("brute.emptypass = self.args["brute.registry. -* useraspass .registry. false) o. true) o.guess passwords only.useraspass".guesses the username as password (default: true) -* emptypass .Engine options that can be set by scripts -.passonly = self.module("brute".retries"] ) or 3 o. --.firstonly) -* passonly .don't store the results in the credential library -* max_guesses .useraspass = self.get_script_args(arg) or default return (val == "true" or val==true or tonumber(val)==1) end.firstonly". user or pass and controls -whether the engine should iterate over users. -* nostore . --.changes the title of the result table where the -passwords are returned.retries) -* delay .checkBoolArg("brute.passonly".the maximum amount of guesses to perform for each -account.delay) -* mode . don't supply a username -(can be set using script-arg brute.the amount of retries to do before aborting -(can be set using script-arg brute.delay"] ) or 0 o.guesses"] ) or 0 return o end.can be set to either cred.registry.args["brute. else false checkBoolArg = function( arg. false) o.max_retries = tonumber( nmap. @see description for more information. unset or false if not setRetry = function( self.The account object which is to be reported back from each driver -. "user". --.@return status true on success else false -.@return status true if the error is recoverable. mode ) local modes = { "password". m in ipairs(modes) do if ( mode == m ) then supported = true end end if ( not(supported) ) then stdnse. value ) self[param] = value end. Error = { retry = false. --. --. --.retry = r end.title = title end. param.Set the error as abort all threads --.setMode: mode %s not supported".-.Sets an option parameter --.options. mode) return false. is currently only used to flag for retries -.mode = mode end return true end.debug1("ERROR: brute.@param title string containing the title value setTitle = function(self. --. done = false } setmetatable(o.It also contains the error message.@param b boolean true if the engine should abort guessing on all threads .@return err string containing the error message on failure setMode = function( self. if one was returned from the driver.@param mode string containing either "user" or "password" -.Set the error as recoverable --. "Unsupported mode" else self. self) self.retry end.__index = self return o end. new = function(self.@param r boolean true if the engine should attempt to retry the -credentials.@param param string containing the parameter name -. msg) local o = { msg = msg.The Error class. } -.@param value string containing the parameter value setOption = function( self.Is the error recoverable? --. false if not isRetry = function( self ) return self. --. title) self. r ) self. "creds" } local supported = false for _.Set an alternate title for the result output (default: Accounts) --. @param driver. -. found_accounts = {}.@return status true if the driver flagged the engine to abort isAbort = function( self ) return self.Marks the username as invalid.abort = b end. driver_options = options.@param b boolean true if done. host.@return msg string containing the error message getMessage = function( self ) return self.msg end.@param host table as passed to the action method of the script -.Was the error abortable --. } -. b ) self. terminate_all = false.Get the error message reported --. -. -. port. doing all the nasty work Engine = { STAT_INTERVAL = 20. passwords = passwords_iterator().setAbort = function( self. options) local o = { driver = driver. --.done = b end.done end.Signals the engine that the thread is done and should be terminated --. unset or false if not setDone = function( self. port = port.invalid_account end. .@param options table containing any script specific options -.@return status true if done.@param username setInvalidAccount = function(self.The brute engine.@param port table as passed to the action method of the script -. iterator = nil . counter = 0.@return username string containing the invalid account isInvalidAccount = function(self) return self.Checks if the error reported the account as invalid. error = nil. the driver class that should be instantiated -. threads = {}.invalid_account = username end. --. username) self.@return o new Engine instance new = function(self. -. tps = {}.abort end.Is the thread done? --. false if not isDone = function( self ) return self. driver. usernames = usernames_iterator(). host = host.Creates a new Engine instance --. b ) self. --. aborting further guessing. --. --. --.get_script_args("brute.Iterator wrapper used to iterate over all registered iterators --. } setmetatable(o.max_threads = stdnse.passwords = passwordIterator end.account_guesses = {}.Limit the number of worker threads --.threads[thread] = nil else count = count + 1 end end return count end. pass in self.threads) do if ( v.used_creds or {} .used_creds = self.Sets the password iterator --.guesses ~= nil ) then count = count + 1 end end return count end.threads) do if ( coroutine.iterator do -. --.@return count number of non-dead threads threadCount = function( self ) local count = 0 for thread in pairs(self.__index = self o.@param max number containing the maximum number of allowed threads setMaxThreads = function( self.threads") or 10 return o end. self) self. v in pairs(self.@return count number of threads performing activity activeThreads = function( self ) local count = 0 for thread.Returns the number of non-dead threads --. --.@param usernameIterator function to set as a username iterator setUsernameIterator = function(self. --.usernameIterator) self.usernames = usernameIterator end.Calculates the number of threads that are actually doing any work --. max ) self.@param passwordIterator function to set as a password iterator setPasswordIterator = function(self. options = Options:new(). --.makes sure the credentials have not been tested before self.@return iterator function get_next_credential = function( self ) local function next_credential () for user.passwordIterator) self.Sets the username iterator --.max_threads = max end. --.status(thread) == "dead" ) then self. --.Does the actual authentication request --.account_guesses[username] and self. password = next_credential() if ( not(username) and not(password) ) then driver:disconnect() self.wrap( next_credential ) end. false on failure -..max_guesses == 0 or not(self. Error on failure doAuthenticate = function( self ) local local local local status. pass ) end end while true do coroutine.used_creds[user. nil) end end return coroutine.Did we successfully connect? if ( status ) then if ( not(username) and not(password) ) then repeat username.yield(nil.max_retries username.driver:new( self.pass] = true coroutine.found_accounts) or not(self.host.options.account_guesses[username] + 1 or 1 end -.make sure that all threads locked in connect stat terminate quickly if ( Engine.threads[coroutine.options.max_guesses > self. response next_credential = self:get_next_credential() retries = self.running()].pass]) ) then self.terminate = true return false end until ( ( not(self.account_guesses[username]) or self..driver_options ) status = driver:connect() -.port.options. password repeat local driver = self.terminate_all ) then driver:disconnect() return false end local c -.account_guesses[username] = self.found_accounts[username]) ) and ( self.account_guesses[username] ) ) -. self.used_creds[user.yield( user.increases the number of guesses for an account self. self.pass = pass or "nil" if ( not(self.Do we have a username or not? if ( username and #username > 0 ) then c = ("%s/%s"):format(username.@return true on success. #password > 0 and password or "<empty>") else c = ("%s"):format(#password > 0 and password or "<empty>") end .@return response Account on success. running()] interval_start = os.credstore = self.threads[coroutine.script_name.insert(self. self.credstore or {} table.time() while( true ) do -.abort = true end return status.* We've reached the maximum retry attempts until( status or ( response and not( response:isRetry() ) ) or retries == 0) -.max_retries ) and "Re-trying" or "Trying" stdnse. in this case we don't log the account as found.host. aborted .username] == nil ) then if ( not(self.port ):add(response.* The response was not set to retry -. ( not(self.counter + 1 -. response = self:doAuthenticate() if ( status ) then -.port.found_accounts) or self. response end.options.End if: -.* The guess was successful -.ip.Prevent locked accounts from appearing several times if ( not(self. login = local local local function(self.1 -..options. aborted . password ) driver:disconnect() driver = nil end retries = retries . tostring(response) ) end stdnse.terminate_all = true self.error = "Too many retries.state ) else self. msg.nostore) ) then creds. response.password.local msg = ( retries ~= self.number ) status.host. c..terminate_all or thread_data. cvar ) condvar = nmap.options.Increase the amount of total guesses self.Credentials:new( self.. self.") response.options.found_accounts[response.passonly) ) then self.debug2("%s %s against %s:%d".debug1("Discovered account: %s".did we exhaust all retries.Should we terminate all threads? if ( self.counter = self.terminate ) then break end local status..credstore. terminate and report? if ( retries == 0 ) then Engine. self.username. response = driver:login( username.username] = true end ." response = Error:new("Too many retries. self. tostring(response)) ---if if we're running in passonly mode.condvar( cvar ) thread_data = self.found_accounts[response. response. and want to continue guessing we will have a problem as the username is always the same. script_name.host.time() . tps ) stdnse.debug2("threads=%d.options.options.port.guesses = ( thread_data.passwords .check ) then -.Dump statistics at regular intervals if ( timediff > Engine.port.service or "unknown" -. self.time() local tps = self. We should phase this out -.port. response = self.tps.driver_options ):check() if( not(status) ) then return false.driver:new( self. response end end local usernames = self. self:activeThreads(). do sleep if ( self.Check if firstonly option was set.check if the driver is ready! local status. self.driver:new( self.time() .tps=%d".guesses and thread_data.condvar( cvar ) assert(self. tps ) end -. "Invalid port table detected") self.Starts the brute-force --.terminate_all = true end end else if ( response and response:isAbort() ) then self.host. if so abort all threads if ( self.firstonly ) then self.Only run the check method if it exist. self.options.-.script_name") assert(self.self.STAT_INTERVAL ) then interval_start = os.number and self. false on failure -.delay ) end end condvar "signal" end.sleep( self.counter / ( os.protocol.if delay was specified.terminate_all = true self.insert(self.guesses + 1 or 1 ) -.error = response:getMessage() break elseif( response and response:isDone() ) then break elseif ( response and response:isInvalidAccount() ) then self.starttime ) table.interval_start) -.options.delay > 0 ) then stdnse.port.usernames local passwords = self.port.in favor of a check in the action function of the script if ( self. self. "SCRIPT_NAME was not set in options.service = self.port.driver_options ).@return status true on success.found_accounts[response:isInvalidAccount()] = true end end local timediff = (os. --.This thread made another guess thread_data.@return err string containing error message on failure start = function(self) local cvar = {} local condvar = nmap. "No credential file specified (see brute. passwords ) end if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options.user_pw_iterator( single_user_iter().passonly) ) then self.wrap(next_user) end -.if no mode was given.iterator = Iterators.only add this iterator if no other iterator was specified if self.passonly ) then local function single_user_iter(next) local function next_user() coroutine.user_pw_iterator( usernames. end if ( "function" return false.if ( "function" return false.iterator or Iterators. ("Failed to open credfile (%s)"):format(credfile) end self.iterator = self. assume creds mode if ( not(mode) and stdnse.get_script_args("brute. passwords ) elseif ( mode ) then return false.pw_user_iterator( usernames.Are we guessing against a service that has no username (eg. end ~= type(usernames) ) then "Invalid usernames iterator" ~= type(passwords) ) then "Invalid passwords iterator" local mode = self.iterator or Iterators.mode or stdnse.iterator = Iterators.options.iterator = unpwdb.concat_iterators(Iterators.Default to the pw_user_iterator in case no iterator was specified elseif ( self. VNC) if ( self.pw_user_iterator( usernames.get_script_args("userdb") or stdnse.iterator == nil then self.get_script_args("passdb") ) then return false.credential_iterator( f ) elseif ( mode and mode == 'user' ) then self.iterator = self.yield( "" ) end return coroutine. but a credfile is present.mode") -. passwords ) elseif( mode and mode == 'pass' ) then self.get_script_args("brute. "r" ) if ( not(f) ) then return false.useraspass ) then -.get_script_args("brute.options.if we're only guessing passwords. "\n ERROR: brute. passwords ) end elseif ( mode == 'creds' ) then local credfile = stdnse.credfile") ) then if ( stdnse.credfile)" end local f = io. "lower").credfile can't be used in combination with userdb/passdb" end mode = 'creds' end -.options. this doesn't make sense if ( not(self.self. ("Unsupported mode: %s"):format(mode) -.iterator) end .iterator == nil ) then self.iterator = Iterators.credfile") if ( not(credfile) ) then return false.open( credfile.pw_same_as_user_iterator(usernames. if so.port):getTable() else valid_accounts = self. tps ) if ( self.options.script_name.Add the statistics to the result result. self.credstore end local result = stdnse.Credentials:new(self.calculate the average tps local sum = 0 for _. empty_pass_iter(). v in ipairs( self.title or "Accounts"] = valid_accounts else result.new_thread( self.counter.host.output_table() -. self.options.Statistics = ("Performed %d guesses in %d seconds.options.time() -.yield( "" ) end return coroutine.max_guesses) break .Did we find any accounts. mode or "pass") end self. guesses in pairs(self.emptypass ) then local function empty_pass_iter() local function next_pass() coroutine.options.wrap(next_pass) end self.end if ( ( not(mode) or mode == 'user' or mode == 'pass' ) and self.options. self.login.account_iterator(usernames.max_guesses > 0 ) then -.starttime ) time_diff = ( time_diff == 0 ) and 1 or time_diff local tps = ( sum == 0 ) and ( self. do formatting if ( valid_accounts and #valid_accounts > 0 ) then result[self.options.max_guesses ) then result.options.wait for all threads to finish running while self:threadCount()>0 do condvar "wait" end local valid_accounts if ( not(self.tps ) -.iterator = Iterators.Information = ("Guesses restricted to %d tries per account to avoid lockout"):format(self.running = true end -.account_guesses) do if ( guesses == self.max_threads do local co = stdnse.nostore) ) then valid_accounts = creds.time() . time_diff. cvar ) self. average tps: %d"):format( self.threads[co] = {} self.Startup all worker threads for i=1.starttime = os.self.tps ) do sum = sum + v end local time_diff = ( os.we only display a warning if the guesses are equal to max_guesses for user.Accounts = "No valid accounts found" end -.counter / time_diff ) or ( sum / #self.threads[co]. self. mode) local function next_credential () local outer.Did any error occur? If so add this to the result.end end end -. should be either 'user' or 'pass' and controls -whether the users or passwords are in the 'outer' loop -. pass.passwords() if ( not(status) ) then return "Failed to load passwords" end return passwords end Iterators = { --.usernames() if ( not(status) ) then return "Failed to load usernames" end return usernames end --. o ) else coroutine.yield( o.Iterates over each user and password --.Default username iterator that uses unpwdb -usernames_iterator = function() local status. users elseif ( mode == 'user' ) then outer. } --. if ( self. passwords = unpwdb.table_iterator(pass) end if ( mode == 'pass' ) then outer. usernames = unpwdb. inner = users. inner if "table" == type(users) then users = unpwdb.error return false. i ) .table_iterator(users) end if "table" == type(pass) then pass = unpwdb.yield( i. pass else return end for o in outer do for i in inner do if ( mode == 'pass' ) then coroutine.@param mode string. result end return true. inner = pass.ERROR = self. result end.error ) then result.@return function iterator account_iterator = function(users.@param pass table/function containing list of passwords -.@param users table/function containing list of users -.Default password iterator that uses unpwdb -passwords_iterator = function() local status. @param users table/function containing list of users -.@return function iterator pw_ucase_iterator = function( users. --.An iterator that returns the username as password --. passwords.yield( user. case ) local function next_credential () for user in users do if ( case == 'upper' ) then coroutine.wrap( next_credential ) end. user:lower()) else coroutine.@return function iterator pw_user_iterator = function( users.@param users table/function containing list of users -. nil) end end return coroutine.@return function iterator pw_same_as_user_iterator = function( users.yield(nil.Try each user for each password (password in outer loop) --. -. --.@param case string [optional] 'upper' or 'lower'.@return function iterator user_pw_iterator = function( users. specifies if user -and password pairs should be case converted.account_iterator( users. pass in Iterators. --. should be either 'user' or 'pass' and controls -whether the users or passwords are in the 'outer' loop -.@param pass table containing list of passwords -.@param mode string.Try each password for each user (user in outer loop) --. mode ) local function next_credential () for user. pass.wrap( next_credential ) end. nil) end end return coroutine.@param users function returning the next user -.end end inner("reset") end while true do coroutine. --.@param pass table/function containing list of passwords -. pass:upper() ) end .@param pass table/function containing list of passwords -.yield(nil.@param users table containing list of users -.account_iterator( users.yield(user. mode) do coroutine. "user" ) end.yield(user. passwords. user:upper()) elseif( case == 'lower' ) then coroutine. pass ) return Iterators.An iterator that returns the username and uppercase password --. pass ) return Iterators.yield(user. pass. user) end end users("reset") while true do coroutine.account_iterator(users. "pass" ) end. passwords = unpwdb.yield( user. passwords status. nil) end end return coroutine.account_iterator( users. pass = line:match("^([^%/]*)%/(.*%S)') end line = trim(line) local user. --. users = unpwdb.usernames() if ( not(status) ) then return end status.@param f file handle to file containing credentials separated by '/' -. mode ) end.wrap( next_credential ) end. unpwdb_iterator = function( mode ) local status.*)$") coroutine.wrap( next_credential ) end.yield( nil. .while true do coroutine. nil ) end end return coroutine. passwords. users.@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*(. pass ) end end f:close() while true do coroutine.Credential iterator (for default or known user/pass combinations) --.passwords() if ( not(status) ) then return end return Iterators.yield(nil. } return _ENV.
Copyright © 2024 DOKUMEN.SITE Inc.