local http = require "http"
local nmap = require "nmap"
local shortport = require "shortport"
local slaxml = require "slaxml"
local stdnse = require "stdnse"
local strbuf = require "strbuf"
local string = require "string"
local table = require "table"
local tableaux = require "tableaux"
description = [[
Performs XMLRPC Introspection via the system.listMethods method.
If the verbosity is > 1 then the script fetches the response
of system.methodHelp for each method returned by listMethods.
]]
---
-- @args xmlrpc-methods.url The URI path to request.
--
-- @output
-- | xmlrpc-methods:
-- | Supported Methods:
-- | list
-- | system.listMethods
-- | system.methodHelp
-- |_ system.methodSignature
--
-- @xmloutput
--
-- list
-- system.listMethods
-- system.methodHelp
-- system.methodSignature
--
author = "Gyanendra Mishra"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default", "safe", "discovery"}
portrule = shortport.http
local function set_80_columns(t)
local buffer = strbuf.new()
for method, description in pairs(t) do
buffer = (buffer .. string.format(" %s:\n\n", method))
local line, ll = {}, 0
local add_word = function(word)
if #word + ll + 1 < 78 then
table.insert(line, word)
ll = ll + #word + 1
else
buffer = buffer .. table.concat(line, " ") .. "\n"
ll = #word + 1
line = {word}
end
end
string.gsub(description, "(%S+)", add_word)
buffer = buffer .. table.concat(line, " ") .. "\n\n"
end
return "\n" .. strbuf.dump(buffer)
end
action = function(host, port)
local url = stdnse.get_script_args(SCRIPT_NAME .. ".url") or "/"
local data = ' system.listMethods '
local response = http.post(host, port, url, {header = {["Content-Type"] = "application/x-www-form-urlencoded"}}, nil, data )
if not (response and response.status and response.body) then
stdnse.debug1("HTTP POST failed")
return nil
end
local output = stdnse.output_table()
local parser = slaxml.parser:new()
local under_80 = {
__tostring = set_80_columns
}
if response.status == 200 and response.body:find("system.listMethods", nil, true) then
parser._call = {startElement = function(name)
parser._call.text = name == "string" and function(content) output["Supported Methods"] = output["Supported Methods"] or {} table.insert(output["Supported Methods"], content) end end,
closeElement = function(name) parser._call.text = function() return nil end end
}
parser:parseSAX(response.body, {stripWhitespace=true})
if nmap.verbosity() > 1 and tableaux.contains(output["Supported Methods"], "system.methodHelp") then
for i, method in ipairs(output["Supported Methods"]) do
data = ' system.methodHelp ' .. method .. ' '
response = http.post(host, port, url, {header = {["Content-Type"] = "application/x-www-form-urlencoded"}}, nil, data)
if response and response.status == 200 then
parser._call.startElement = function(name)
parser._call.text = name == "string" and function(content)
content = parser.unescape(content)
output["Supported Methods"][i] = nil
output["Supported Methods"][method] = content
end
end
parser:parseSAX(response.body, {stripWhitespace=true})
end
-- useful in cases when the output returned by the above request is empty
-- or the has no text in the string
-- element.
if output["Supported Methods"][i] then
output["Supported Methods"][i] = nil
output["Supported Methods"][method] = "Empty system.methodHelp output."
end
end
setmetatable(output["Supported Methods"], under_80)
end
return output
elseif response.body:find("faultCode", nil, true) then
output.error = "XMLRPC instance doesn't support introspection."
return output, output.error
end
end