local http = require "http" local shortport = require "shortport" local slaxml = require "slaxml" local stdnse = require "stdnse" description = [[Requests information from a Subversion repository. ]] --- -- @usage nmap --script http-svn-info -- -- @args http-svn-info.url This is a URL relative to the scanned host eg. /default.html (default: /) -- -- @output -- 443/tcp open https syn-ack -- | http-svn-info: -- | Path: . -- | URL: https://svn.nmap.org/ -- | Relative URL: ^/ -- | Repository Root: https://svn.nmap.org -- | Repository UUID: e0a8ed71-7df4-0310-8962-fdc924857419 -- | Revision: 34938 -- | Node Kind: directory -- | Last Changed Author: yang -- | Last Changed Rev: 34938 -- |_ Last Changed Date: Sun, 19 Jul 2015 13:49:59 GMT-- -- -- @xmloutput -- . -- https://svn.nmap.org/ -- ^/ -- https://svn.nmap.org -- e0a8ed71-7df4-0310-8962-fdc924857419 -- 34938 -- directory -- yang -- 34938 -- Sun, 19 Jul 2015 13:49:59 GMT author = "Gyanendra Mishra" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"default", "discovery", "safe"} portrule = shortport.http local ELEMENTS = { ["repository-uuid"] = "Repository UUID", ["version-name"] = "Last Changed Rev", ["creator-displayname"] = "Last Changed Author", ["getlastmodified"] = "Last Changed Date", ["baseline-relative-path"] = "Relative URL", ["href"] = "Repository Root", ["getcontentlength"] = "file" } local output_order = { "Last Changed Author", "Last Changed Rev", "Last Changed Date", } local function get_text_callback(store, name) if ELEMENTS[name] == nil then return end return function(content) store[ELEMENTS[name]] = content end end action = function(host, port) local url = stdnse.get_script_args(SCRIPT_NAME .. ".url") or "/" local output = {} local ordered_output = stdnse.output_table() local options = { header = { ["Depth"] = 0, }, } local response = http.generic_request(host, port, "PROPFIND", url, options) if response and response.status == 207 then local parser = slaxml.parser:new() parser._call = {startElement = function(name) parser._call.text = get_text_callback(output, name) end, closeElement = function(name) parser._call.text = function() return nil end end } parser:parseSAX(response.body, {stripWhitespace=true}) if next(output) then ordered_output["Path"] = url:match("/([^/]*)$"):len() > 0 and url:match("/([^/]*)$") or url:match("/([^/]*)/$") or "." if output["file"] then ordered_output["Name"] = url:match("/([^/]*)$") end ordered_output["URL"] = host.targetname and port.service .. "://" .. host.targetname .. url ordered_output["Relative URL"] = output["Relative URL"] and "^/" .. output["Relative URL"] or "^/" output["Repository Root"] = output["Repository Root"]:gsub("%/%!svn.*", ""):len() > 0 and output["Repository Root"]:gsub("%/%!svn.*", "") or "/" ordered_output["Repository Root"] = port.service .. "://" .. host.targetname .. output["Repository Root"] ordered_output["Repository UUID"] = output["Repository UUID"] if url ~= output["Repository Root"] then local temp_output = {} response = http.generic_request(host, port, "PROPFIND", output["Repository Root"], options) if response and response.status == 207 then parser._call.startElement = function(name) parser._call.text = get_text_callback(temp_output, name) end parser:parseSAX(response.body, {stripWhitespace=true}) ordered_output["Revision"] = temp_output["Last Changed Rev"] end else ordered_output["Revision"] = output["Last Changed Rev"] end if not output["file"] then ordered_output["Node Kind"] = "directory" else ordered_output["Node Kind"] = "file" end for _, value in ipairs(output_order) do ordered_output[value] = output[value] end return ordered_output end end end