diff --git a/scripts/http-method-tamper.nse b/scripts/http-method-tamper.nse
index a2c0e931c..a54926e69 100644
--- a/scripts/http-method-tamper.nse
+++ b/scripts/http-method-tamper.nse
@@ -1,82 +1,168 @@
-local http = require "http"
-local shortport = require "shortport"
-local stdnse = require "stdnse"
-local table = require "table"
-
description = [[
-Tests whether a JBoss target is vulnerable to jmx console authentication bypass (CVE-2010-0738).
+Attempts to bypass password protected resources (HTTP 401 status) by performing HTTP verb tampering.
+If an array of paths to check is not set, it will crawl the web server and perform the check against any
+password protected resource that it finds.
-It works by checking if the target paths require authentication or redirect to a login page that could be
-bypassed via a HEAD request. RFC 2616 specifies that the HEAD request should be treated exactly like GET but
-with no returned response body. The script also detects if the URL does not require authentication at all.
+The script determines if the protected URI is vulnerable by performing HTTP verb tampering and monitoring
+ the status codes. First, it uses a HEAD request, then a POST request and finally a random generated string
+( This last one is useful when web servers treat unknown request methods as a GET request. This is the case
+ for PHP servers ).
-For more information, see:
-* CVE-2010-0738 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0738
+If the table paths is set, it will attempt to access the given URIs. Otherwise, a web crawler
+is initiated to try to find protected resources. Note that in a PHP environment with .htacess files you need to specify a
+path to a file rather than a directory to find misconfigured .htaccess files.
+
+References:
* http://www.imperva.com/resources/glossary/http_verb_tampering.html
* https://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST_%28OWASP-CM-008%29
-
+* http://www.mkit.com.ar/labs/htexploit/
+* http://capec.mitre.org/data/definitions/274.html
]]
---
--- @usage
--- nmap --script=http-method-tamper --script-args 'http-method-tamper.paths={/path1/,/path2/}'
+-- @usage nmap -sV --script http-method-tamper
+-- @usage nmap -p80 --script http-method-tamper --script-args 'http-method-tamper.paths={/protected/db.php,/protected/index.php}'
--
-- @output
--- PORT STATE SERVICE
--- 80/tcp open http
+-- PORT STATE SERVICE REASON
+-- 80/tcp open http syn-ack
-- | http-method-tamper:
--- |_ /jmx-console/: Authentication bypass.
+-- | VULNERABLE:
+-- | Authentication bypass by HTTP verb tampering
+-- | State: VULNERABLE (Exploitable)
+-- | Description:
+-- | This web server contains password protected resources vulnerable to authentication bypass
+-- | vulnerabilities via HTTP verb tampering. This is often found in web servers that only limit access to the
+-- | common HTTP methods and in misconfigured .htaccess files.
+-- |
+-- | Extra information:
+-- |
+-- | URIs suspected to be vulnerable to HTTP verb tampering:
+-- | /method-tamper/protected/pass.txt [POST]
+-- |
+-- | References:
+-- | http://www.imperva.com/resources/glossary/http_verb_tampering.html
+-- | http://www.mkit.com.ar/labs/htexploit/
+-- | http://capec.mitre.org/data/definitions/274.html
+-- |_ https://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST_%28OWASP-CM-008%29
--
--- @args http-method-tamper.path Array of paths to check. Defaults
--- to {"/jmx-console/"}.
+-- @args http-method-tamper.uri Base URI to crawl. Not aplicable if http-method-tamper.paths is set.
+-- @args http-method-tamper.paths Array of paths to check. If not set, the script will crawl the web server.
+-- @args http-method-tamper.timeout Web crawler timeout. Default: 10000ms
+---
-author = "Hani Benhabiles"
+author = "Paulino Calderon "
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"safe", "auth", "vuln"}
+local http = require "http"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local table = require "table"
+local httpspider = require "httpspider"
+local vulns = require "vulns"
+local url = require "url"
portrule = shortport.http
-action = function(host, port)
- local paths = stdnse.get_script_args("http-method-tamper.paths")
- local result = {}
-
- -- convert single string entry to table
- if ( "string" == type(paths) ) then
- paths = { paths }
- end
-
- -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests
- local _, http_status, _ = http.identify_404(host,port)
- if ( http_status == 200 ) then
- stdnse.print_debug(1, "%s: Exiting due to ambiguous response from web server on %s:%s. All URIs return status 200.", SCRIPT_NAME, host.ip, port.number)
- return false
- end
-
- -- fallback to jmx-console
- paths = paths or {"/jmx-console/"}
-
- for _, path in ipairs(paths) do
- local getstatus = http.get(host, port, path).status
-
- -- Checks if HTTP authentication or a redirection to a login page is applied.
- if getstatus == 401 or getstatus == 302 then
- local headstatus = http.head(host, port, path).status
- if headstatus == 500 and path == "/jmx-console/" then
- -- JBoss authentication bypass.
- table.insert(result, ("%s: Vulnerable to CVE-2010-0738."):format(path))
- elseif headstatus == 200 then
- -- Vulnerable to authentication bypass.
- table.insert(result, ("%s: Authentication bypass possible"):format(path))
- end
- -- Checks if no authentication is required for Jmx console
- -- which is default configuration and common.
- elseif getstatus == 200 then
- table.insert(result, ("%s: Authentication was not required"):format(path))
- end
- end
-
- return stdnse.format_output(true, result)
+--
+-- Checks if the web server does not return status 401 when requesting with other HTTP verbs.
+-- First, it tries with HEAD, POST and then with a random string.
+--
+local function probe_http_verbs(host, port, uri)
+ stdnse.print_debug(2, "%s:Tampering HTTP verbs %s", SCRIPT_NAME, uri)
+ local head_req = http.head(host, port, uri)
+ if head_req and head_req.status ~= 401 then
+ return true, "HEAD"
+ end
+ local post_req = http.post(host, port, uri)
+ if post_req and post_req.status ~= 401 then
+ return true, "POST"
+ end
+ --With a random generated verb we also look for "invalid method" status 501
+ local random_verb_req = http.generic_request(host, port, stdnse.generate_random_string(4), uri)
+ if random_verb_req and random_verb_req.status ~= 401 and random_verb_req.status ~= 501 then
+ return true, "GENERIC"
+ end
+
+ return false
+end
+
+action = function(host, port)
+ local vuln_uris = {}
+ local paths = stdnse.get_script_args(SCRIPT_NAME..".paths")
+ local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or "/"
+ local timeout = stdnse.get_script_args(SCRIPT_NAME..".timeout") or 10000
+ local vuln = {
+ title = 'Authentication bypass by HTTP verb tampering',
+ state = vulns.STATE.NOT_VULN,
+ description = [[
+This web server contains password protected resources vulnerable to authentication bypass
+vulnerabilities via HTTP verb tampering. This is often found in web servers that only limit access to the
+ common HTTP methods and in misconfigured .htaccess files.
+ ]],
+ references = {
+ 'http://www.mkit.com.ar/labs/htexploit/',
+ 'http://www.imperva.com/resources/glossary/http_verb_tampering.html',
+ 'https://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST_%28OWASP-CM-008%29',
+ 'http://capec.mitre.org/data/definitions/274.html'
+ }
+ }
+ local vuln_report = vulns.Report:new(SCRIPT_NAME, host, port)
+
+ -- If paths is not set, crawl the web server looking for http 401 status
+ if not(paths) then
+ local crawler = httpspider.Crawler:new(host, port, uri, { scriptname = SCRIPT_NAME } )
+ crawler:set_timeout(timeout)
+
+ while(true) do
+ local status, r = crawler:crawl()
+ if ( not(status) ) then
+ if ( r.err ) then
+ return stdnse.format_output(true, "ERROR: %s", r.reason)
+ else
+ break
+ end
+ end
+ if r.response.status == 401 then
+ stdnse.print_debug(2, "%s:%s is protected! Let's try some verb tampering...", SCRIPT_NAME, tostring(r.url))
+ local parsed = url.parse(tostring(r.url))
+ local probe_status, probe_type = probe_http_verbs(host, port, parsed.path)
+ if probe_status then
+ stdnse.print_debug(1, "%s:Vulnerable URI %s", SCRIPT_NAME, uri)
+ table.insert(vuln_uris, parsed.path..string.format(" [%s]", probe_type))
+ end
+ end
+ end
+ else
+ -- Paths were set, check them and exit. No crawling here.
+
+ -- convert single string entry to table
+ if ( type(paths) == "string" ) then
+ paths = { paths }
+ end
+ -- iterate through given paths/files
+ for _, path in ipairs(paths) do
+ local path_req = http.get(host, port, path)
+
+ if path_req.status == 401 then
+ local probe_status, probe_type = probe_http_verbs(host, port, path)
+ if probe_status then
+ stdnse.print_debug(1, "%s:Vulnerable URI %s", SCRIPT_NAME, path)
+ table.insert(vuln_uris, path..string.format(" [%s]", probe_type))
+ end
+ end
+
+ end
+ end
+
+ if ( #vuln_uris > 0 ) then
+ vuln.state = vulns.STATE.EXPLOIT
+ vuln_uris.name = "URIs suspected to be vulnerable to HTTP verb tampering:"
+ vuln.extra_info = stdnse.format_output(true, vuln_uris)
+ end
+
+ return vuln_report:make_output(vuln)
end
diff --git a/scripts/http-vuln-cve2010-0738.nse b/scripts/http-vuln-cve2010-0738.nse
new file mode 100644
index 000000000..e62558fa5
--- /dev/null
+++ b/scripts/http-vuln-cve2010-0738.nse
@@ -0,0 +1,79 @@
+description = [[
+Tests whether a JBoss target is vulnerable to jmx console authentication bypass (CVE-2010-0738).
+
+It works by checking if the target paths require authentication or redirect to a login page that could be
+bypassed via a HEAD request. RFC 2616 specifies that the HEAD request should be treated exactly like GET but
+with no returned response body. The script also detects if the URL does not require authentication at all.
+
+For more information, see:
+* CVE-2010-0738 http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0738
+* http://www.imperva.com/resources/glossary/http_verb_tampering.html
+* https://www.owasp.org/index.php/Testing_for_HTTP_Methods_and_XST_%28OWASP-CM-008%29
+
+]]
+
+---
+-- @usage
+-- nmap --script=http-vuln-cve2010-0738 --script-args 'http-vuln-cve2010-0738.paths={/path1/,/path2/}'
+--
+-- @output
+-- PORT STATE SERVICE
+-- 80/tcp open http
+-- | http-vuln-cve2010-0738:
+-- |_ /jmx-console/: Authentication bypass.
+--
+-- @args http-vuln-cve2010-0738.paths Array of paths to check. Defaults
+-- to {"/jmx-console/"}.
+
+author = "Hani Benhabiles"
+license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
+categories = {"safe", "auth", "vuln"}
+
+local http = require "http"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local table = require "table"
+
+portrule = shortport.http
+
+action = function(host, port)
+ local paths = stdnse.get_script_args(SCRIPT_NAME..".paths")
+ local result = {}
+
+ -- convert single string entry to table
+ if ( "string" == type(paths) ) then
+ paths = { paths }
+ end
+
+ -- Identify servers that answer 200 to invalid HTTP requests and exit as these would invalidate the tests
+ local _, http_status, _ = http.identify_404(host,port)
+ if ( http_status == 200 ) then
+ stdnse.print_debug(1, "%s: Exiting due to ambiguous response from web server on %s:%s. All URIs return status 200.", SCRIPT_NAME, host.ip, port.number)
+ return false
+ end
+
+ -- fallback to jmx-console
+ paths = paths or {"/jmx-console/"}
+
+ for _, path in ipairs(paths) do
+ local getstatus = http.get(host, port, path).status
+
+ -- Checks if HTTP authentication or a redirection to a login page is applied.
+ if getstatus == 401 or getstatus == 302 then
+ local headstatus = http.head(host, port, path).status
+ if headstatus == 500 and path == "/jmx-console/" then
+ -- JBoss authentication bypass.
+ table.insert(result, ("%s: Vulnerable to CVE-2010-0738."):format(path))
+ elseif headstatus == 200 then
+ -- Vulnerable to authentication bypass.
+ table.insert(result, ("%s: Authentication bypass possible"):format(path))
+ end
+ -- Checks if no authentication is required for Jmx console
+ -- which is default configuration and common.
+ elseif getstatus == 200 then
+ table.insert(result, ("%s: Authentication was not required"):format(path))
+ end
+ end
+
+ return stdnse.format_output(true, result)
+end
diff --git a/scripts/script.db b/scripts/script.db
index 2a54f9dfb..a77f95be7 100644
--- a/scripts/script.db
+++ b/scripts/script.db
@@ -194,6 +194,7 @@ Entry { filename = "http-virustotal.nse", categories = { "external", "malware",
Entry { filename = "http-vlcstreamer-ls.nse", categories = { "discovery", "safe", } }
Entry { filename = "http-vmware-path-vuln.nse", categories = { "safe", "vuln", } }
Entry { filename = "http-vuln-cve2009-3960.nse", categories = { "exploit", "intrusive", } }
+Entry { filename = "http-vuln-cve2010-0738.nse", categories = { "auth", "safe", "vuln", } }
Entry { filename = "http-vuln-cve2010-2861.nse", categories = { "intrusive", "vuln", } }
Entry { filename = "http-vuln-cve2011-3192.nse", categories = { "safe", "vuln", } }
Entry { filename = "http-vuln-cve2011-3368.nse", categories = { "intrusive", "vuln", } }