From c6ed7023c41cc8e5deba6eafac327fd52d8ba0ef Mon Sep 17 00:00:00 2001 From: aca Date: Tue, 10 Jul 2012 16:35:48 +0000 Subject: [PATCH] Brute and unpwdb lib improvements that allow more flexible iterator specifications. Merge from my dev branch. --- CHANGELOG | 3 + nselib/brute.lua | 112 +++++++++++++++++-------------- nselib/unpwdb.lua | 43 +++++++++++- scripts/cvs-brute-repository.nse | 10 +-- scripts/oracle-brute.nse | 4 +- 5 files changed, 114 insertions(+), 58 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 65eada8eb..4453cd180 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added changes to brute and unpwdb libraries to allow more flexible iterator + specification and control. [Aleksandar Nikolic] + o [NSE] Added ms-sql-dac script which queries the Microsoft SQL Browser service for the DAC (Dedicated Admin Connection) port. [Patrik Karlsson] diff --git a/nselib/brute.lua b/nselib/brute.lua index b2d3062e2..8694989c0 100644 --- a/nselib/brute.lua +++ b/nselib/brute.lua @@ -409,7 +409,9 @@ Engine = counter = 0, threads = {}, tps = {}, - iterators = {}, + iterator = nil , + usernames = usernames_iterator(), + passwords = passwords_iterator(), found_accounts = {}, account_guesses = {}, options = Options:new(), @@ -420,11 +422,18 @@ Engine = return o end, - --- Adds an iterator to the list - -- - -- @param iterator function to add to the list - addIterator = function( self, iterator ) - table.insert( self.iterators, iterator ) + --- 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 @@ -464,15 +473,12 @@ Engine = -- @return iterator function get_next_credential = function( self ) local function next_credential () - -- 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 - self.used_creds = self.used_creds or {} - if ( not(self.used_creds[user..pass]) ) then - self.used_creds[user..pass] = true - coroutine.yield( user, pass ) - end + for user, pass in self.iterator do + -- makes sure the credentials have not been tested before + self.used_creds = self.used_creds or {} + 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 @@ -641,13 +647,9 @@ Engine = if( not(status) ) then return false, response end end - local status, usernames = unpwdb.usernames() - if ( not(status) ) then return false, "Failed to load usernames" end + local usernames = self.usernames + local passwords = self.passwords - -- make sure we have a valid pw file - local status, passwords = unpwdb.passwords() - if ( not(status) ) then return false, "Failed to load passwords" 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 @@ -666,8 +668,8 @@ Engine = return coroutine.wrap(next_user) end -- only add this iterator if no other iterator was specified - if ( #self.iterators == 0 ) then - table.insert( self.iterators, Iterators.user_pw_iterator( single_user_iter(), passwords ) ) + 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") @@ -680,22 +682,22 @@ Engine = return false, ("Failed to open credfile (%s)"):format(credfile) end - table.insert( self.iterators, Iterators.credential_iterator( f ) ) + self.iterator = Iterators.credential_iterator( f ) elseif ( mode and mode == 'user' ) then - table.insert( self.iterators, Iterators.user_pw_iterator( usernames, passwords ) ) + self.iterator = Iterators.user_pw_iterator( usernames, passwords ) elseif( mode and mode == 'pass' ) then - table.insert( self.iterators, Iterators.pw_user_iterator( usernames, passwords ) ) + self.iterator = 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 ( 0 == #self.iterators ) then - table.insert( self.iterators, Iterators.pw_user_iterator( usernames, passwords ) ) + 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 - table.insert( self.iterators, 1, Iterators.pw_same_as_user_iterator(usernames, "lower")) + self.iterator = unpwdb.concat_iterators(Iterators.pw_same_as_user_iterator(usernames, "lower"),self.iterator) end end @@ -706,7 +708,7 @@ Engine = end return coroutine.wrap(next_pass) end - table.insert( self.iterators, 1, Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass")) + self.iterator = Iterators.account_iterator(usernames, empty_pass_iter(), mode or "pass") end @@ -775,6 +777,22 @@ Engine = } +--- 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 @@ -787,7 +805,13 @@ Iterators = { 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 @@ -796,27 +820,15 @@ Iterators = { 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 + 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 - 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 + inner("reset") end while true do coroutine.yield(nil, nil) end end diff --git a/nselib/unpwdb.lua b/nselib/unpwdb.lua index 9a54a6b1f..98dfb36b5 100644 --- a/nselib/unpwdb.lua +++ b/nselib/unpwdb.lua @@ -120,7 +120,7 @@ local filltable = function(filename, table) return true end -local closure = function(table) +table_iterator = function(table) local i = 1 return function(cmd) @@ -187,7 +187,7 @@ local usernames_raw = function() return false, "Error parsing username list" end - return true, closure(usertable) + return true, table_iterator(usertable) end --- Returns a function closure which returns a new password with every call @@ -206,7 +206,7 @@ local passwords_raw = function() return false, "Error parsing password list" end - return true, closure(passtable) + return true, table_iterator(passtable) end --- Wraps time and count limits around an iterator. When either limit expires, @@ -286,4 +286,41 @@ passwords = function(time_limit, count_limit) return true, limited_iterator(iterator, time_limit, count_limit) end +--- Returns a new iterator that iterates trough it's consecutive iterators, +-- basically concatenating them. +-- @param iter1 First iterator to concatenate. +-- @param iter2 Second iterator to concatenate. +-- @return function The concatenated iterators. +concat_iterators = function(iter1, iter2) +s return function(cmd) + if cmd == "reset" then + iter1("reset") + iter2("reset") + return + end + local v1 = {iter1(cmd)} + if not (next(v1) == nil) then + return table.unpack(v1) + end + local v2 = {iter2(cmd)} + if not (next(v2) == nil) then + return table.unpack(v2) + end + return iter1(cmd) + end +end + +--- Returns a new iterator that filters it's results based on the filter. +-- @param iterator Iterator that needs to be filtered +-- @param filter Function that returns bool, which serves as a filter +-- @return function The filtered iterator. +filter_iterator = function(iterator, filter) + return function(cmd) + local result = {iterator(cmd)} + if filter(table.unpack(result)) then + return result + end + end +end + return _ENV; diff --git a/scripts/cvs-brute-repository.nse b/scripts/cvs-brute-repository.nse index a0da1af95..837193b85 100644 --- a/scripts/cvs-brute-repository.nse +++ b/scripts/cvs-brute-repository.nse @@ -31,8 +31,10 @@ With knowledge of the correct repository name, usernames and passwords can be gu -- @args cvs-brute-repository.repofile a file containing a list of repositories -- to guess --- Version 0.1 +-- Version 0.2 -- Created 07/13/2010 - v0.1 - created by Patrik Karlsson +-- Revised 08/07/2012 - v0.2 - revised to suit the changes in brute +-- library [Aleksandar Nikolic] author = "Patrik Karlsson" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" @@ -102,7 +104,7 @@ action = function(host, port) end end - local repository_iterator = function() + repository_iterator = function() local function next_repo() for line in f:lines() do if ( not(line:match("#!comment")) ) then @@ -119,8 +121,8 @@ action = function(host, port) engine.options.passonly = true engine.options.firstonly = false engine.options.nostore = true - engine:addIterator(brute.Iterators.account_iterator({""}, repos, "user")) - if ( repofile ) then engine:addIterator(repository_iterator()) end + engine.iterator = brute.Iterators.account_iterator({""}, repos, "user") + if ( repofile ) then engine.iterator = unpwdb.concat_iterators(engine.iterator,repository_iterator()) end status, result = engine:start() return result diff --git a/scripts/oracle-brute.nse b/scripts/oracle-brute.nse index 4f39464f0..ba5e5fa7d 100644 --- a/scripts/oracle-brute.nse +++ b/scripts/oracle-brute.nse @@ -57,6 +57,8 @@ result in a large number of accounts being locked out on the database server. -- changed code to use ConnectionPool -- Revised 03/13/2012 - v0.4 - revised by László Tóth -- added support for SYSDBA accounts +-- Revised 08/07/2012 - v0.5 - revised to suit the changes in brute +-- library [Aleksandar Nikolic] -- -- Summary @@ -213,7 +215,7 @@ action = function(host, port) return ("\n ERROR: Failed to open %s"):format(DEFAULT_ACCOUNTS) end - engine:addIterator(brute.Iterators.credential_iterator(f)) + engine.iterator = brute.Iterators.credential_iterator(f) end engine.options.script_name = SCRIPT_NAME