1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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
|