summaryrefslogtreecommitdiffstats
path: root/scripts/http-webdav-scan.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/http-webdav-scan.nse')
-rw-r--r--scripts/http-webdav-scan.nse182
1 files changed, 182 insertions, 0 deletions
diff --git a/scripts/http-webdav-scan.nse b/scripts/http-webdav-scan.nse
new file mode 100644
index 0000000..68029aa
--- /dev/null
+++ b/scripts/http-webdav-scan.nse
@@ -0,0 +1,182 @@
+local http = require "http"
+local ipOps = require "ipOps"
+local table = require "table"
+local tableaux = require "tableaux"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+
+description = [[
+A script to detect WebDAV installations. Uses the OPTIONS and PROPFIND methods.
+
+The script sends an OPTIONS request which lists the dav type, server type, date
+and allowed methods. It then sends a PROPFIND request and tries to fetch exposed
+directories and internal ip addresses by doing pattern matching in the response body.
+
+This script takes inspiration from the various scripts listed here:
+* http://carnal0wnage.attackresearch.com/2010/05/more-with-metasploit-and-webdav.html
+* https://github.com/sussurro/Metasploit-Tools/blob/master/modules/auxiliary/scanner/http/webdav_test.rb
+* http://code.google.com/p/davtest/
+]]
+
+---
+-- @usage
+-- nmap --script http-webdav-scan -p80,8080 <target>
+--
+-- @args http-webdav-scan.path The path to start in; e.g. <code>"/web/"</code>
+-- will try <code>"/web/xxx"</code>.
+--
+-- @output
+-- PORT STATE SERVICE
+-- 8008/tcp open http
+-- | http-webdav-scan:
+-- | Allowed Methods: GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, REPORT
+-- | Server Type: DAV/0.9.8 Python/2.7.6
+-- | Server Date: Fri, 22 May 2015 19:28:00 GMT
+-- | WebDAV type: Unknown
+-- | Directory Listing:
+-- | http://localhost
+-- | http://localhost:8008/WebDAVTest_b1tqTWeyRR
+-- | http://localhost:8008/WebDAVTest_A0QWJb7hcK
+-- | http://localhost:8008/WebDAVTest_hf9Mqqpi1M
+-- |_ http://localhost:8008/WebDAVTest_Ds5KBFywDq
+--
+-- @xmloutput
+-- <elem key="Allowed Methods">GET, HEAD, COPY, MOVE, POST, PUT,
+-- PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE, REPORT</elem>
+-- <elem key="Server Type">DAV/0.9.8 Python/2.7.6</elem>
+-- <elem key="Server Date">Fri, 22 May 2015 19:28:00 GMT</elem>
+-- <elem key="WebDAV type">Unknown</elem>
+-- <table key="Directory Listing">
+-- <elem>http://localhost</elem>
+-- <elem>http://localhost:8008/WebDAVTest_b1tqTWeyRR</elem>
+-- <elem>http://localhost:8008/WebDAVTest_A0QWJb7hcK</elem>
+-- <elem>http://localhost:8008/WebDAVTest_hf9Mqqpi1M</elem>
+-- <elem>http://localhost:8008/WebDAVTest_Ds5KBFywDq</elem>
+-- </table>
+
+author = "Gyanendra Mishra"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {
+ "safe",
+ "discovery",
+ "default",
+}
+
+
+portrule = shortport.http
+
+-- a function to test the OPTIONS method.
+local function get_options (host, port, path)
+ -- check if WebDAV is installed or not.
+ local response = http.generic_request(host, port, "OPTIONS", path)
+ if response and response.status == 200 then
+ local ret = {}
+ ret['Server Type'] = response.header['server']
+ ret['Allowed Methods'] = response.header['allow']
+ ret['Public Options'] = response.header['public']
+ ret['WebDAV'] = false
+ ret['Server Date'] = response.header['date']
+
+ if response.header['dav'] and response.header['dav']:find('1') then
+ ret['WebDAV'] = true
+ ret['WebDAV type'] = 'Unknown'
+ if response.header['X-MSDAVEXT'] then
+ ret['WebDAV type'] = 'SHAREPOINT DAV'
+ end
+ if response.header['dav']:match 'apache' then
+ ret['WebDAV type'] = 'Apache DAV'
+ end
+ end
+ return ret
+
+ else
+ return false
+ end
+end
+
+-- a function to extract internal ip addresses from PROPFIND response.
+local function getIPs(body)
+ local ip_pats = {'%f[%d]192%.168%.%d+%.%d+',
+ '%f[%d]10%.%d+%.%d+%.%d+',
+ '%f[%d]172%.1[6-9]%.%d+%.%d+',
+ '%f[%d]172%.2%d%.%d+%.%d+',
+ '%f[%d]172%.3[01]%.%d+%.%d+'}
+ local result = {}
+ for _, ip_pat in pairs(ip_pats) do
+ for ip in body:gmatch(ip_pat) do
+ if ipOps.expand_ip(ip) then
+ result[ip] = true
+ end
+ end
+ end
+ return tableaux.keys(result)
+end
+
+-- a function to test the PROPFIND method.
+local function check_propfind (host, port, path)
+ local options = {
+ header = {
+ ["Depth"] = 1,
+ ["Content-Length"] = 0,
+ },
+ }
+ local response = http.generic_request(host, port, "PROPFIND", path, options)
+ if response and response.status ~= 207 then
+ return false
+ end
+ local ret = {}
+ ret['WebDAV'] = false
+ local dir_pat = '<.-[hH][rR][eE][fF][^>]->(.-)</.-[hH][rR][eE][fF]>'
+ if response.body:find '<D:status>HTTP/1.1 200 OK</D:status>' then
+ ret['WebDAV'] = true
+ end
+ ret['Server Type'] = response.header['server']
+ ret['Server Date'] = response.header['date']
+ local ips = getIPs(response.body)
+ if next(ips) then ret['Exposed Internal IPs'] = getIPs(response.body) end
+ if response.body:gmatch(dir_pat) then
+ ret['Directory Listing'] = {}
+ for dir in response.body:gmatch(dir_pat) do
+ table.insert(ret['Directory Listing'], dir)
+ end
+ end
+ return ret
+end
+
+function action (host, port)
+
+ local path = stdnse.get_script_args(SCRIPT_NAME .. ".path") or '/'
+ local enabled = false
+ local output = stdnse.output_table()
+
+ local info = get_options(host, port, path)
+ if info then
+ if info['WebDAV'] then
+ enabled = true
+ stdnse.debug1("Target has WebDAV enabled.")
+ for name, data in pairs(info) do
+ if name ~= 'WebDAV' then
+ output[name] = data
+ end
+ end
+ else
+ stdnse.debug1 "Target isn't reporting WebDAV"
+ end
+ end
+
+ local davinfo = check_propfind(host, port, path)
+ if davinfo then
+ if davinfo['WebDAV'] then
+ for name, data in pairs(davinfo) do
+ if not output[name] and name ~= 'WebDAV' then
+ output[name] = data
+ end
+ end
+ if not enabled then
+ stdnse.debug1 "Target has WebDAV enabled."
+ end
+ end
+ end
+
+ if #output > 0 then return output else return nil end
+end