local http = require "http" local vulns = require "vulns" local io = require "io" local nmap = require "nmap" local shortport = require "shortport" local stdnse = require "stdnse" local string = require "string" 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, http://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 modules/auxiliary/scanner/http/wmap_dir_webdav_unicode_bypass.rb auxiliary module. 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 -- -- @output -- 80/tcp open http syn-ack -- | http-iis-webdav-vuln: -- | VULNERABLE: -- | WebDAV access -- | State: VULNERABLE -- | Vulnerability in IIS 5.1/6.0 that allows arbitrary users to access secured WebDAV folders. -- | Extra information: -- | /secret -- | /webdav -- | References: -- | http://www.skullsecurity.org/blog/?p=271 -- | http://blog.zoller.lu/2009/05/iis-6-webdac-auth-bypass-and-data.html -- | http://www.kb.cert.org/vuls/id/787932 -- | http://seclists.org/fulldisclosure/2009/May/att-134/IIS_Advisory_pdf.bin -- |_ http://www.microsoft.com/technet/security/advisory/971492.mspx -- -- @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, "/web" will try "/web/xxx". -- -- This script can be tested by setting up a Windows XP Professional Virtual Machine with Service -- Pack <= 1. In "Add or Remove Programs" IIS can be installed from the installation disk image -- used for the virtual machine. Within the IIS control menu, each directory can be given access -- properties by entering the directory's Properties Menu, clicking the Directory Security tab, -- pressing Edit under "Anonymous access and authentication control." For the root IIS directory -- (first listed as "Default Web Site") the only option selected should be "Anonymous Access." -- It is important to leave the User Name and Password fields alone since they must match what -- is stored in other parts of the system and they can be difficult to recover. To create new -- directories, right click on the root IIS directory and select "New -> Virtual Directory." The -- alias will be the name of the directory (ie. 127.0.0.1/example/) and the Directory Security -- setting should be "Basic Authentication" and "Integrated Windows Authentication" in order to -- create a vulnerable directory. ----------------------------------------------------------------------- author = "Ron Bowes, Andrew Orr" license = "Same as Nmap--See http://nmap.org/book/man-legal.html" categories = {"vuln", "intrusive"} portrule = shortport.http ---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 = '' 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 a state from vulns.STATE 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 vulns.STATE.VULN else stdnse.debug1("Folder does not seem vulnerable: %s", folder) return vulns.STATE.NOT_VULN 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 -- Not necessarily "LIKELY_VULN", more like "UNKNOWN" to differentiate between cases return vulns.STATE.LIKELY_VULN 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 vulns_state = vulns.STATE.VULN 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 end result = go_single(host, port, line) if(result == vulns.STATE.VULN) then results[#results + 1] = line vulns_state = result end end file:close() return true, results, vulns_state end action = function(host, port) local report = vulns.Report:new(SCRIPT_NAME, host, port) local vuln = { title = 'WebDAV access', state = vulns.STATE.NOT_VULN, description = [[ Vulnerability in IIS 5.1/6.0 that allows arbitrary users to access secured WebDAV folders.]], references = { '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' }, extra_info = {} } -- Start by checking if '/' is protected -- if it is, we can't do the tests local result = go_single(host, port, "/") if(result == vulns.STATE.NOT_VULN) then stdnse.debug1("Root folder is password protected, aborting.") vuln.extra_info = "Could not determine vulnerability, since root folder is password protected" return report:make_output(vuln) 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).") vuln.extra_info = "WebDAV is DISABLED. Server is not currently vulnerable." return report:make_output(vuln) 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 nmap.verbosity() > 0 and "ERROR: This web server is not supported." or nil end end if(nmap.registry.args.webdavfolder ~= nil) then local folder = nmap.registry.args.webdavfolder local result = go_single(host, port, "/" .. folder) if (result == vulns.STATE.VULN) then vuln.extra_info = string.format("WebDAV is ENABLED. Folder is vulnerable: %s", folder) vuln.state = result elseif(result == vulns.STATE.NOT_VULN) then vuln.extra_info = string.format("WebDAV is ENABLED. Folder is NOT vulnerable: %s", folder) else vuln.extra_info = string.format("WebDAV is ENABLED. Could not determine vulnerability of folder: %s", folder) end return report:make_output(vuln) else local status status, vuln.extra_info, vuln.state = go(host, port) if(status == false) then return nmap.verbosity() > 0 and "ERROR: " .. results or nil else if(#vuln.extra_info == 0) then if(vuln.state == vulns.STATE.NOT_VULN) then vuln.extra_info = "WebDAV is ENABLED. Protected folder found but could not be exploited. Server does not appear to be vulnerable." else vuln.extra_info = "WebDAV is ENABLED. No protected folder found; check not run. If you know a protected folder, add --script-args=webdavfolder=" end end end end return report:make_output(vuln) end