From ef61c118310f4dcc3c51624a4f74c42e0c305c03 Mon Sep 17 00:00:00 2001 From: dmiller Date: Thu, 23 Jan 2014 21:52:38 +0000 Subject: [PATCH] Retab creds.lua (Fixes some nsedoc ugliness) --- nselib/creds.lua | 630 ++++++++++++++++++++++++----------------------- 1 file changed, 316 insertions(+), 314 deletions(-) diff --git a/nselib/creds.lua b/nselib/creds.lua index fc127610b..bffb32a4e 100644 --- a/nselib/creds.lua +++ b/nselib/creds.lua @@ -10,23 +10,23 @@ -- The following code illustrates how a script may add discovered credentials -- to the database: -- --- local c = creds.Credentials:new( SCRIPT_NAME, host, port ) --- c:add("patrik", "secret", creds.State.VALID ) +-- local c = creds.Credentials:new( SCRIPT_NAME, host, port ) +-- c:add("patrik", "secret", creds.State.VALID ) -- -- -- The following code illustrates how a script can return a table of discovered -- credentials at the end of execution: -- --- return tostring(creds.Credentials:new(SCRIPT_NAME, host, port)) +-- return tostring(creds.Credentials:new(SCRIPT_NAME, host, port)) -- -- -- The following code illustrates how a script may iterate over discovered -- credentials: -- --- local c = creds.Credentials:new(creds.ALL_DATA, host, port) --- for cred in c:getCredentials(creds.State.VALID) do --- showContentForUser(cred.user, cred.pass) --- end +-- local c = creds.Credentials:new(creds.ALL_DATA, host, port) +-- for cred in c:getCredentials(creds.State.VALID) do +-- showContentForUser(cred.user, cred.pass) +-- end -- -- -- The library also enables users to add credentials through script arguments @@ -35,10 +35,10 @@ -- passed using script arguments will be added with the PARAM state. The -- following code may be used by a scripts to retrieve these credentials: -- --- local c = creds.Credentials:new(creds.ALL_DATA, host, port) --- for cred in c:getCredentials(creds.State.PARAM) do --- ... do something ... --- end +-- local c = creds.Credentials:new(creds.ALL_DATA, host, port) +-- for cred in c:getCredentials(creds.State.PARAM) do +-- ... do something ... +-- end -- -- -- Any globally added credentials will be made available to all scripts, @@ -47,8 +47,10 @@ -- arguments will only be made available to scripts with ports matching -- the service name. The following two examples illustrate how credentials are -- added globally and for the http service: +-- -- --script-args creds.global='admin:nimda' -- --script-args creds.http='webadmin:password' +-- -- -- The service name at this point may be anything and the entry is created -- dynamically without validating whether the service exists or not. @@ -64,15 +66,15 @@ -- The following code illustrates how a script may save its discovered credentials -- to a file: -- --- local c = creds.Credentials:new( SCRIPT_NAME, host, port ) --- c:add("patrik", "secret", creds.State.VALID ) --- status, err = c:saveToFile("outputname","csv") +-- local c = creds.Credentials:new( SCRIPT_NAME, host, port ) +-- c:add("patrik", "secret", creds.State.VALID ) +-- status, err = c:saveToFile("outputname","csv") -- -- --- Supported output formats are CSV, verbose and plain. In both verbose and plain --- records are seperated by colons. The difference between the two is that verbose --- includes the credential state. The file extension is automatically added to --- the filename based on the type requested. +-- Supported output formats are CSV, verbose and plain. In both verbose and plain +-- records are seperated by colons. The difference between the two is that verbose +-- includes the credential state. The file extension is automatically added to +-- the filename based on the type requested. -- -- @author "Patrik Karlsson " -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html @@ -80,17 +82,17 @@ -- Version 0.4 -- Created 2011/02/06 - v0.1 - created by Patrik Karlsson -- Revised 2011/27/06 - v0.2 - revised by Patrik Karlsson --- * added documentation --- * added getCredentials function +-- * added documentation +-- * added getCredentials function -- -- Revised 2011/05/07 - v0.3 - revised by Patrik Karlsson --- * modified getCredentials to return an iterator --- * added support for adding credentials as --- script arguments +-- * modified getCredentials to return an iterator +-- * added support for adding credentials as +-- script arguments -- -- Revised 2011/09/04 - v0.4 - revised by Tom Sellers --- * added saveToFile function for saving credential --- * table to file in CSV or text formats +-- * added saveToFile function for saving credential +-- * table to file in CSV or text formats local bit = require "bit" local coroutine = require "coroutine" @@ -104,31 +106,31 @@ _ENV = stdnse.module("creds", stdnse.seeall) -- Table containing the different account states State = { - LOCKED = 1, - VALID = 2, - DISABLED = 4, - CHANGEPW = 8, - PARAM = 16, - EXPIRED = 32, - TIME_RESTRICTED = 64, - HOST_RESTRICTED = 128, - LOCKED_VALID = 256, - DISABLED_VALID = 512, - HASHED = 1024, + LOCKED = 1, + VALID = 2, + DISABLED = 4, + CHANGEPW = 8, + PARAM = 16, + EXPIRED = 32, + TIME_RESTRICTED = 64, + HOST_RESTRICTED = 128, + LOCKED_VALID = 256, + DISABLED_VALID = 512, + HASHED = 1024, } StateMsg = { - [State.LOCKED] = 'Account is locked', - [State.VALID] = 'Valid credentials', - [State.DISABLED] = 'Account is disabled', - [State.CHANGEPW] = 'Valid credentials, password must be changed at next logon', - [State.PARAM] = 'Credentials passed to script during Nmap execution', - [State.EXPIRED] = 'Valid credentials, account expired', - [State.TIME_RESTRICTED] = 'Valid credentials, account cannot log in at current time', - [State.HOST_RESTRICTED] = 'Valid credentials, account cannot log in from current host', - [State.LOCKED_VALID] = 'Valid credentials, account locked', - [State.DISABLED_VALID] = 'Valid credentials, account disabled', - [State.HASHED] = 'Hashed valid or invalid credentials', + [State.LOCKED] = 'Account is locked', + [State.VALID] = 'Valid credentials', + [State.DISABLED] = 'Account is disabled', + [State.CHANGEPW] = 'Valid credentials, password must be changed at next logon', + [State.PARAM] = 'Credentials passed to script during Nmap execution', + [State.EXPIRED] = 'Valid credentials, account expired', + [State.TIME_RESTRICTED] = 'Valid credentials, account cannot log in at current time', + [State.HOST_RESTRICTED] = 'Valid credentials, account cannot log in from current host', + [State.LOCKED_VALID] = 'Valid credentials, account locked', + [State.DISABLED_VALID] = 'Valid credentials, account disabled', + [State.HASHED] = 'Hashed valid or invalid credentials', } @@ -137,305 +139,305 @@ ALL_DATA = "all_script_data" -- The RegStorage class RegStorage = { - --- Creates a new RegStorage instance - -- - -- @return a new instance - new = function(self) - local o = {} - setmetatable(o, self) - self.__index = self - o.filter = {} - return o - end, + --- Creates a new RegStorage instance + -- + -- @return a new instance + new = function(self) + local o = {} + setmetatable(o, self) + self.__index = self + o.filter = {} + return o + end, - --- Add credentials to storage - -- - -- @param scriptname the name of the script adding the credentials - -- @param host host table, name or ip - -- @param port number containing the port of the service - -- @param service the name of the service - -- @param user the name of the user - -- @param pass the password of the user - -- @param state of the account - add = function( self, scriptname, host, port, service, user, pass, state ) - local cred = { - scriptname = scriptname, - host = host, - port = port, - service = service, - user = user, - pass = pass, - state = state - } - nmap.registry.creds = nmap.registry.creds or {} - table.insert( nmap.registry.creds, cred ) - end, + --- Add credentials to storage + -- + -- @param scriptname the name of the script adding the credentials + -- @param host host table, name or ip + -- @param port number containing the port of the service + -- @param service the name of the service + -- @param user the name of the user + -- @param pass the password of the user + -- @param state of the account + add = function( self, scriptname, host, port, service, user, pass, state ) + local cred = { + scriptname = scriptname, + host = host, + port = port, + service = service, + user = user, + pass = pass, + state = state + } + nmap.registry.creds = nmap.registry.creds or {} + table.insert( nmap.registry.creds, cred ) + end, - --- Sets the storage filter - -- - -- @param host table containing the host - -- @param port table containing the port - -- @param state table containing the account state - setFilter = function( self, host, port, state ) - self.filter.host = host - self.filter.port = port - self.filter.state = state - end, + --- Sets the storage filter + -- + -- @param host table containing the host + -- @param port table containing the port + -- @param state table containing the account state + setFilter = function( self, host, port, state ) + self.filter.host = host + self.filter.port = port + self.filter.state = state + end, - --- Returns a credential iterator matching the selected filters - -- - -- @return a credential iterator - getAll = function( self ) - local function get_next() - local host, port = self.filter.host, self.filter.port + --- Returns a credential iterator matching the selected filters + -- + -- @return a credential iterator + getAll = function( self ) + local function get_next() + local host, port = self.filter.host, self.filter.port - if ( not(nmap.registry.creds) ) then return end + if ( not(nmap.registry.creds) ) then return end - for _, v in pairs(nmap.registry.creds) do - local h = ( v.host.ip or v.host ) - if ( not(host) and not(port) ) then - if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then - coroutine.yield(v) - end - elseif ( not(host) and ( port == v.port ) ) then - if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then - coroutine.yield(v) - end - elseif ( ( host and ( h == host or h == host.ip ) ) and not(port) ) then - if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then - coroutine.yield(v) - end - elseif ( ( host and ( h == host or h == host.ip ) ) and port.number == v.port ) then - if ( not(self.filter.state) or ( v.state == bit.band(self.filter.state, v.state) ) ) then - coroutine.yield(v) - end - end - end - end - return coroutine.wrap(get_next) - end, + for _, v in pairs(nmap.registry.creds) do + local h = ( v.host.ip or v.host ) + if ( not(host) and not(port) ) then + if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then + coroutine.yield(v) + end + elseif ( not(host) and ( port == v.port ) ) then + if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then + coroutine.yield(v) + end + elseif ( ( host and ( h == host or h == host.ip ) ) and not(port) ) then + if ( not(self.filter.state) or ( v.state == self.filter.state ) ) then + coroutine.yield(v) + end + elseif ( ( host and ( h == host or h == host.ip ) ) and port.number == v.port ) then + if ( not(self.filter.state) or ( v.state == bit.band(self.filter.state, v.state) ) ) then + coroutine.yield(v) + end + end + end + end + return coroutine.wrap(get_next) + end, } -- The credentials class Credentials = { - --- Creates a new instance of the Credentials class - -- @param scriptname string containing the name of the script - -- @param host table as received by the scripts action method - -- @param port table as received by the scripts action method - new = function(self, scriptname, host, port) - local o = {} - setmetatable(o, self) - self.__index = self - o.storage = RegStorage:new() - o.storage:setFilter(host, port) - o.host = host - o.port = ( port and port.number ) and port.number - o.service = ( port and port.service ) and port.service - o.scriptname = scriptname - return o - end, + --- Creates a new instance of the Credentials class + -- @param scriptname string containing the name of the script + -- @param host table as received by the scripts action method + -- @param port table as received by the scripts action method + new = function(self, scriptname, host, port) + local o = {} + setmetatable(o, self) + self.__index = self + o.storage = RegStorage:new() + o.storage:setFilter(host, port) + o.host = host + o.port = ( port and port.number ) and port.number + o.service = ( port and port.service ) and port.service + o.scriptname = scriptname + return o + end, - --- Add a discovered credential - -- - -- @param user the name of the user - -- @param pass the password of the user - -- @param state of the account - add = function( self, user, pass, state ) - local pass = ( pass and #pass > 0 ) and pass or "" - assert( self.host, "No host supplied" ) - assert( self.port, "No port supplied" ) - assert( state, "No state supplied") - assert( self.scriptname, "No scriptname supplied") + --- Add a discovered credential + -- + -- @param user the name of the user + -- @param pass the password of the user + -- @param state of the account + add = function( self, user, pass, state ) + local pass = ( pass and #pass > 0 ) and pass or "" + assert( self.host, "No host supplied" ) + assert( self.port, "No port supplied" ) + assert( state, "No state supplied") + assert( self.scriptname, "No scriptname supplied") - -- there are cases where we will only get a user or password - -- so as long we have one of them, we're good - if ( user or pass ) then - self.storage:add( self.scriptname, self.host, self.port, self.service, user, pass, state ) - end - end, + -- there are cases where we will only get a user or password + -- so as long we have one of them, we're good + if ( user or pass ) then + self.storage:add( self.scriptname, self.host, self.port, self.service, user, pass, state ) + end + end, - --- Returns a credential iterator - -- - -- @param state mask containing values from the State table - -- @return credential iterator, returning a credential each time it's - -- called. Unless filtered by the state mask all credentials - -- for the host, port match are iterated over. - -- The credential table has the following fields: - -- host - table as received by the action function - -- port - number containing the port number - -- user - string containing the user name - -- pass - string containing the user password - -- state - a state number @see State - -- service - string containing the name of the service - -- scriptname - string containing the name of the - -- script that added the credential - getCredentials = function(self, state) - local function next_credential() - if ( state ) then - self.storage:setFilter(self.host, { number=self.port, service = self.service }, state) - end + --- Returns a credential iterator + -- + -- @param state mask containing values from the State table + -- @return credential iterator, returning a credential each time it's + -- called. Unless filtered by the state mask all credentials + -- for the host, port match are iterated over. + -- The credential table has the following fields: + -- host - table as received by the action function + -- port - number containing the port number + -- user - string containing the user name + -- pass - string containing the user password + -- state - a state number @see State + -- service - string containing the name of the service + -- scriptname - string containing the name of the + -- script that added the credential + getCredentials = function(self, state) + local function next_credential() + if ( state ) then + self.storage:setFilter(self.host, { number=self.port, service = self.service }, state) + end - for cred in self.storage:getAll() do - if ( ( self.scriptname == ALL_DATA ) or - ( cred.scriptname == self.scriptname ) ) then - coroutine.yield(cred) - end - end + for cred in self.storage:getAll() do + if ( ( self.scriptname == ALL_DATA ) or + ( cred.scriptname == self.scriptname ) ) then + coroutine.yield(cred) + end + end - if ( state and State.PARAM == bit.band(state, State.PARAM) ) then - local creds_global = stdnse.get_script_args('creds.global') - local creds_service - local creds_params + if ( state and State.PARAM == bit.band(state, State.PARAM) ) then + local creds_global = stdnse.get_script_args('creds.global') + local creds_service + local creds_params - if ( self.service ) then - creds_service = stdnse.get_script_args('creds.' .. self.service ) - end + if ( self.service ) then + creds_service = stdnse.get_script_args('creds.' .. self.service ) + end - if ( creds_service ) then creds_params = creds_service end - if ( creds_global and creds_service ) then - creds_params = creds_params .. ',' .. creds_global - elseif ( creds_global ) then - creds_params = creds_global - end + if ( creds_service ) then creds_params = creds_service end + if ( creds_global and creds_service ) then + creds_params = creds_params .. ',' .. creds_global + elseif ( creds_global ) then + creds_params = creds_global + end - if ( not(creds_params) ) then return end + if ( not(creds_params) ) then return end - for _, cred in ipairs(stdnse.strsplit(",", creds_params)) do - -- if the credential contains a ':' we have a user + pass pair - -- if not, we only have a user with an empty password - local user, pass - if ( cred:match(":") ) then - user, pass = cred:match("^(.-):(.-)$") - else - user = cred:match("^(.*)$") - end - coroutine.yield( { host = self.host, - port = self.port, - user = user, - pass = pass, - state = State.PARAM, - service = self.service } ) - end - end - end - return coroutine.wrap( next_credential ) - end, + for _, cred in ipairs(stdnse.strsplit(",", creds_params)) do + -- if the credential contains a ':' we have a user + pass pair + -- if not, we only have a user with an empty password + local user, pass + if ( cred:match(":") ) then + user, pass = cred:match("^(.-):(.-)$") + else + user = cred:match("^(.*)$") + end + coroutine.yield( { host = self.host, + port = self.port, + user = user, + pass = pass, + state = State.PARAM, + service = self.service } ) + end + end + end + return coroutine.wrap( next_credential ) + end, - --- Returns a table of credentials - -- - -- @return tbl table containing the discovered credentials - getTable = function(self) - local result = {} + --- Returns a table of credentials + -- + -- @return tbl table containing the discovered credentials + getTable = function(self) + local result = {} - for v in self.storage:getAll() do - local h = ( v.host.ip or v.host ) - local svc = ("%s/%s"):format(v.port,v.service) - local c - if ( v.user and #v.user > 0 ) then - if StateMsg[v.state] then - c = ("%s:%s - %s"):format(v.user, v.pass, StateMsg[v.state]) - else - c = ("%s:%s"):format(v.user, v.pass) - end - else - if StateMsg[v.state] then - c = ("%s - %s"):format(v.pass, StateMsg[v.state]) - else - c = ("%s"):format(v.pass) - end - end - local script = v.scriptname - assert(type(h)=="string", "Could not determine a valid host") + for v in self.storage:getAll() do + local h = ( v.host.ip or v.host ) + local svc = ("%s/%s"):format(v.port,v.service) + local c + if ( v.user and #v.user > 0 ) then + if StateMsg[v.state] then + c = ("%s:%s - %s"):format(v.user, v.pass, StateMsg[v.state]) + else + c = ("%s:%s"):format(v.user, v.pass) + end + else + if StateMsg[v.state] then + c = ("%s - %s"):format(v.pass, StateMsg[v.state]) + else + c = ("%s"):format(v.pass) + end + end + local script = v.scriptname + assert(type(h)=="string", "Could not determine a valid host") - if ( script == self.scriptname or self.scriptname == ALL_DATA ) then - result[h] = result[h] or {} - result[h][svc] = result[h][svc] or {} - table.insert( result[h][svc], c ) - end - end + if ( script == self.scriptname or self.scriptname == ALL_DATA ) then + result[h] = result[h] or {} + result[h][svc] = result[h][svc] or {} + table.insert( result[h][svc], c ) + end + end - local output = {} - for hostname, host in pairs(result) do - local host_tbl = { name = hostname } - for svcname, service in pairs(host) do - local svc_tbl = { name = svcname } - for _, account in ipairs(service) do - table.insert(svc_tbl, account) - end - -- sort the accounts - table.sort( svc_tbl, function(a,b) return a 0 ) then - output = output[1][1] - output.name = nil - elseif ( self.host and #output > 0 ) then - output = output[1] - output.name = nil - end - return (#output > 0 ) and output - end, + -- sort the IP addresses + table.sort( output, function(a, b) return ipOps.compare_ip(a.name, "le", b.name) end ) + if ( self.host and self.port and #output > 0 ) then + output = output[1][1] + output.name = nil + elseif ( self.host and #output > 0 ) then + output = output[1] + output.name = nil + end + return (#output > 0 ) and output + end, - -- Saves credentials in the current object to file - -- @param filename string name of the file - -- @param fileformat string file format type, values = csv | verbose | plain (default) - -- @return status true on success, false on failure - -- @return err string containing the error if status is false - saveToFile = function(self, filename, fileformat) + -- Saves credentials in the current object to file + -- @param filename string name of the file + -- @param fileformat string file format type, values = csv | verbose | plain (default) + -- @return status true on success, false on failure + -- @return err string containing the error if status is false + saveToFile = function(self, filename, fileformat) - if ( fileformat == 'csv' ) then - filename = filename .. '.csv' - else - filename = filename .. '.txt' - end + if ( fileformat == 'csv' ) then + filename = filename .. '.csv' + else + filename = filename .. '.txt' + end - local f = io.open( filename, "w") - local output = nil + local f = io.open( filename, "w") + local output = nil - if ( not(f) ) then - return false, ("ERROR: Failed to open file (%s)"):format(filename) - end + if ( not(f) ) then + return false, ("ERROR: Failed to open file (%s)"):format(filename) + end - for account in self:getCredentials() do - if ( fileformat == 'csv' ) then - output = "\"" .. account.user .. "\",\"" .. account.pass .. "\",\"" .. StateMsg[account.state] .. "\"" - elseif ( fileformat == 'verbose') then - output = account.user .. ":" .. account.pass .. ":" .. StateMsg[account.state] - else - output = account.user .. ":" .. account.pass - end - if ( not(f:write( output .."\n" ) ) ) then - return false, ("ERROR: Failed to write file (%s)"):format(filename) - end - end + for account in self:getCredentials() do + if ( fileformat == 'csv' ) then + output = "\"" .. account.user .. "\",\"" .. account.pass .. "\",\"" .. StateMsg[account.state] .. "\"" + elseif ( fileformat == 'verbose') then + output = account.user .. ":" .. account.pass .. ":" .. StateMsg[account.state] + else + output = account.user .. ":" .. account.pass + end + if ( not(f:write( output .."\n" ) ) ) then + return false, ("ERROR: Failed to write file (%s)"):format(filename) + end + end - f:close() - return true - end, + f:close() + return true + end, - --- Get credentials with optional host and port filter - -- If no filters are supplied all records are returned - -- - -- @param host table or string containing the host to filter - -- @param port number containing the port to filter - -- @return table suitable from stdnse.format_output - __tostring = function(self) - local all = self:getTable() - if ( all ) then return stdnse.format_output(true, all) end - end, + --- Get credentials with optional host and port filter + -- If no filters are supplied all records are returned + -- + -- @param host table or string containing the host to filter + -- @param port number containing the port to filter + -- @return table suitable from stdnse.format_output + __tostring = function(self) + local all = self:getTable() + if ( all ) then return stdnse.format_output(true, all) end + end, }