mirror of
https://github.com/nmap/nmap.git
synced 2025-12-29 10:59:02 +00:00
XML structured output for brute.lua and creds.lua
The @xmloutput section documentation is not done, and I'm not sure how
to best do it, since it will be the same for all brute.lua scripts. This
is how it looks:
metasploit-msgrpc-brute:
<table key="Accounts">
<table>
<elem key="username">root</elem>
<elem key="state">Valid credentials</elem>
<elem key="password">root</elem>
</table>
</table>
<elem key="Statistics">Performed 3 guesses in 4 seconds, average tps: 0</elem>
creds-summary:
<table key="127.0.0.1">
<table key="9929/nping-echo">
<table>
<elem key="password">123456</elem>
<elem key="state">Valid credentials</elem>
</table>
</table>
<table key="55553/unknown">
<table>
<elem key="username">root</elem>
<elem key="state">Valid credentials</elem>
<elem key="password">root</elem>
</table>
</table>
</table>
This commit is contained in:
@@ -543,10 +543,10 @@ Engine =
|
||||
creds.Credentials:new( self.options.script_name, self.host, self.port ):add(response.username, response.password, response.state )
|
||||
else
|
||||
self.credstore = self.credstore or {}
|
||||
table.insert(self.credstore, response:toString() )
|
||||
table.insert(self.credstore, tostring(response) )
|
||||
end
|
||||
|
||||
stdnse.debug1("Discovered account: %s", response:toString())
|
||||
stdnse.debug1("Discovered account: %s", tostring(response))
|
||||
|
||||
-- if we're running in passonly mode, and want to continue guessing
|
||||
-- we will have a problem as the username is always the same.
|
||||
@@ -702,13 +702,12 @@ Engine =
|
||||
valid_accounts = self.credstore
|
||||
end
|
||||
|
||||
local result = {}
|
||||
local result = stdnse.output_table()
|
||||
-- Did we find any accounts, if so, do formatting
|
||||
if ( valid_accounts and #valid_accounts > 0 ) then
|
||||
valid_accounts.name = self.options.title or "Accounts"
|
||||
table.insert( result, valid_accounts )
|
||||
result[self.options.title or "Accounts"] = valid_accounts
|
||||
else
|
||||
table.insert( result, {"No valid accounts found", name="Accounts"} )
|
||||
result.Accounts = "No valid accounts found"
|
||||
end
|
||||
|
||||
-- calculate the average tps
|
||||
@@ -719,27 +718,21 @@ Engine =
|
||||
local tps = ( sum == 0 ) and ( self.counter / time_diff ) or ( sum / #self.tps )
|
||||
|
||||
-- Add the statistics to the result
|
||||
local stats = {}
|
||||
table.insert(stats, ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps ) )
|
||||
stats.name = "Statistics"
|
||||
table.insert( result, stats )
|
||||
result.Statistics = ("Performed %d guesses in %d seconds, average tps: %d"):format( self.counter, time_diff, tps )
|
||||
|
||||
if ( self.options.max_guesses > 0 ) then
|
||||
-- we only display a warning if the guesses are equal to max_guesses
|
||||
for user, guesses in pairs(self.account_guesses) do
|
||||
if ( guesses == self.options.max_guesses ) then
|
||||
table.insert( result, { name = "Information",
|
||||
("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses) } )
|
||||
result.Information = ("Guesses restricted to %d tries per account to avoid lockout"):format(self.options.max_guesses)
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
result = ( #result ) and stdnse.format_output( true, result ) or ""
|
||||
|
||||
-- Did any error occur? If so add this to the result.
|
||||
if ( self.error ) then
|
||||
result = result .. (" \n ERROR: %s"):format( self.error )
|
||||
result.ERROR = self.error
|
||||
return false, result
|
||||
end
|
||||
return true, result
|
||||
|
||||
105
nselib/creds.lua
105
nselib/creds.lua
@@ -261,9 +261,37 @@ Account = {
|
||||
(self.state and " - " .. self.state or "")
|
||||
)
|
||||
end,
|
||||
|
||||
--- Less-than operation for sorting
|
||||
--
|
||||
-- Lexicographic comparison by user, pass, and state
|
||||
__lt = function (a, b)
|
||||
if a.user >= b.user then
|
||||
return false
|
||||
elseif a.pass >= b.pass then
|
||||
return false
|
||||
elseif a.state >= b.state then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
end,
|
||||
}
|
||||
|
||||
|
||||
-- Return a function suitable for use as a __pairs metamethod
|
||||
-- which will cause the table to yield its values sorted by key.
|
||||
local function sorted_pairs (sortby)
|
||||
return function (t)
|
||||
local order = stdnse.keys(t)
|
||||
table.sort(order, sortby)
|
||||
return coroutine.wrap(function()
|
||||
for i,k in ipairs(order) do
|
||||
coroutine.yield(k, t[k])
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
-- The credentials class
|
||||
Credentials = {
|
||||
|
||||
@@ -378,63 +406,50 @@ Credentials = {
|
||||
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")
|
||||
if ( v.scriptname == self.scriptname or self.scriptname == ALL_DATA ) then
|
||||
local h = ( v.host.ip or v.host )
|
||||
assert(type(h)=="string", "Could not determine a valid host")
|
||||
local svc = ("%s/%s"):format(v.port,v.service)
|
||||
|
||||
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 )
|
||||
table.insert( result[h][svc], Account:new(
|
||||
v.user ~= "" and v.user or nil,
|
||||
v.pass,
|
||||
v.state
|
||||
)
|
||||
)
|
||||
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
|
||||
for _, host_tbl in pairs(result) do
|
||||
for _, svc_tbl in pairs(host_tbl) do
|
||||
-- sort the accounts
|
||||
table.sort( svc_tbl, function(a,b) return a<b end)
|
||||
table.insert( host_tbl, svc_tbl )
|
||||
table.sort( svc_tbl )
|
||||
end
|
||||
-- sort the services
|
||||
table.sort( host_tbl,
|
||||
function(a,b)
|
||||
return tonumber(a.name:match("^(%d+)")) < tonumber(b.name:match("^(%d+)"))
|
||||
end
|
||||
)
|
||||
table.insert( output, host_tbl )
|
||||
setmetatable(host_tbl, {
|
||||
__pairs = sorted_pairs( function(a,b)
|
||||
return tonumber(a:match("^(%d+)")) < tonumber(b:match("^(%d+)"))
|
||||
end )
|
||||
})
|
||||
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
|
||||
setmetatable(result, {
|
||||
__pairs = sorted_pairs( function(a, b)
|
||||
return ipOps.compare_ip(a, "le", b)
|
||||
end )
|
||||
})
|
||||
|
||||
local _
|
||||
if ( self.host and next(result) ) then
|
||||
_, result = next(result)
|
||||
end
|
||||
return (#output > 0 ) and output
|
||||
if ( self.host and self.port and next(result) ) then
|
||||
_, result = next(result)
|
||||
end
|
||||
return next(result) and result
|
||||
end,
|
||||
|
||||
-- Saves credentials in the current object to file
|
||||
@@ -482,7 +497,7 @@ Credentials = {
|
||||
-- @return table suitable from <code>stdnse.format_output</code>
|
||||
__tostring = function(self)
|
||||
local all = self:getTable()
|
||||
if ( all ) then return stdnse.format_output(true, all) end
|
||||
if ( all ) then return tostring(all) end
|
||||
end,
|
||||
|
||||
}
|
||||
|
||||
@@ -32,10 +32,10 @@ categories = {"auth", "default", "safe"}
|
||||
postrule = function()
|
||||
local all = creds.Credentials:new(creds.ALL_DATA)
|
||||
local tab = all:getTable()
|
||||
if ( tab and #tab > 0 ) then return true end
|
||||
if ( tab and next(tab) ) then return true end
|
||||
end
|
||||
|
||||
action = function()
|
||||
local all = creds.Credentials:new(creds.ALL_DATA)
|
||||
return (all and tostring(all) or nil)
|
||||
return all:getTable()
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user