coolbins/system/usr/share/nmap/scripts/http-svn-enum.nse

133 lines
3.9 KiB
Lua

local http = require "http"
local shortport = require "shortport"
local slaxml = require "slaxml"
local stdnse = require "stdnse"
local tab = require "tab"
description = [[Enumerates users of a Subversion repository by examining logs of most recent commits.
]]
---
-- @usage nmap --script http-svn-enum <target>
--
-- @args http-svn-enum.count The number of logs to fetch. Defaults to the last 1000 commits.
-- @args http-svn-enum.url This is a URL relative to the scanned host eg. /default.html (default: /).
--
-- @output
-- PORT STATE SERVICE REASON
-- 443/tcp open https syn-ack
-- | http-svn-enum:
-- | Author Count Revision Date
-- | gyani 183 34965 2015-07-24
-- | robert 1 34566 2015-06-02
-- | david 2 34785 2015-06-28
--
-- @xmloutput
-- <table></table>
-- <table>
-- <elem>Author</elem>
-- <elem>Count</elem>
-- <elem>Revision</elem>
-- <elem>Date</elem>
-- </table>
-- <table>
-- <elem>gyani</elem>
-- <elem>183</elem>
-- <elem>34965</elem>
-- <elem>2015-07-24</elem>
-- </table>
-- <table>
-- <elem>robert</elem>
-- <elem>1</elem>
-- <elem>34566</elem>
-- <elem>2015-06-02</elem>
-- </table>
-- <table>
-- <elem>david</elem>
-- <elem>2</elem>
-- <elem>34785</elem>
-- <elem>2015-06-28</elem>
-- </table>
author = "Gyanendra Mishra"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
local ELEMENTS = {
["creator-displayname"] = "author",
["version-name"] = "version",
["date"] = "date",
}
local function get_callback(name, unames, temp)
if ELEMENTS[name] then
return function(content)
if not content then content = "unknown" end --useful for "nil" authors
temp[ELEMENTS[name]] = name == "date" and content:sub(1, 10) or content
if temp.date and temp.version and temp.author then
unames[temp.author] = {unames[temp.author] and unames[temp.author][1] + 1 or 1, temp.version, temp.date}
end
end
end
end
portrule = shortport.http
action = function(host, port)
local count = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".count")) or 1000
local url = stdnse.get_script_args(SCRIPT_NAME .. ".url") or "/"
local output, revision, unames = tab.new(), nil, {}
local options = {
header = {
["Depth"] = 0,
},
}
-- first we fetch the current revision number
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 = name == "version-name" and function(content) revision = tonumber(content) end end,
closeElement = function(name) parser._call.text = function() return nil end end
}
parser:parseSAX(response.body, {stripWhitespace=true})
if revision then
local start_revision = revision > count and revision - count or 1
local content = '<?xml version="1.0"?> <S:log-report xmlns:S="svn:"> <S:start-revision>'.. start_revision .. '</S:start-revision> <S:discover-changed-paths/> </S:log-report>'
options = {
header = {
["Depth"] = 1,
},
content = content,
}
local temp = {}
response = http.generic_request(host, port, "REPORT", url, options)
if response and response.status == 200 then
parser._call.startElement = function(name) parser._call.text = get_callback(name, unames, temp) end
parser._call.closeElement = function(name) if name == "log-item" then temp ={} end parser._call.text = function() return nil end end
parser:parseSAX(response.body, {stripWhitespace=true})
tab.nextrow(output)
tab.addrow(output, "Author", "Count", "Revision", "Date")
for revision_author, data in pairs(unames) do
tab.addrow(output, revision_author, data[1], data[2], data[3])
end
if next(unames) then return output end
end
end
end
end