coolbins/system/usr/share/nmap/scripts/http-webdav-scan.nse

183 lines
5.7 KiB
Lua

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