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,
}