Brute and unpwdb lib improvements that allow more flexible iterator specifications. Merge from my dev branch.

This commit is contained in:
aca 2012-07-10 16:35:48 +00:00
parent 6dd99f410b
commit c6ed7023c4
5 changed files with 114 additions and 58 deletions

View file

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

View file

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

View file

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

View file

@ -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 <patrik@cqure.net>
-- 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

View file

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