summaryrefslogtreecommitdiffstats
path: root/scripts/http-svn-enum.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/http-svn-enum.nse')
-rw-r--r--scripts/http-svn-enum.nse132
1 files changed, 132 insertions, 0 deletions
diff --git a/scripts/http-svn-enum.nse b/scripts/http-svn-enum.nse
new file mode 100644
index 0000000..4a23113
--- /dev/null
+++ b/scripts/http-svn-enum.nse
@@ -0,0 +1,132 @@
+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