coolbins/system/usr/share/nmap/scripts/http-vuln-cve2011-3368.nse

161 lines
5.7 KiB
Lua

local http = require "http"
local os = require "os"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
local vulns = require "vulns"
local rand = require "rand"
description = [[
Tests for the CVE-2011-3368 (Reverse Proxy Bypass) vulnerability in Apache HTTP server's reverse proxy mode.
The script will run 3 tests:
* the loopback test, with 3 payloads to handle different rewrite rules
* the internal hosts test. According to Contextis, we expect a delay before a server error.
* The external website test. This does not mean that you can reach a LAN ip, but this is a relevant issue anyway.
References:
* http://www.contextis.com/research/blog/reverseproxybypass/
]]
---
-- @usage
-- nmap --script http-vuln-cve2011-3368 <targets>
--
-- @output
-- PORT STATE SERVICE
-- 80/tcp open http
-- | http-vuln-cve2011-3368:
-- | VULNERABLE:
-- | Apache mod_proxy Reverse Proxy Security Bypass
-- | State: VULNERABLE
-- | IDs: CVE:CVE-2011-3368 BID:49957
-- | Description:
-- | An exposure was reported affecting the use of Apache HTTP Server in
-- | reverse proxy mode. The exposure could inadvertently expose internal
-- | servers to remote users who send carefully crafted requests.
-- | Disclosure date: 2011-10-05
-- | Extra information:
-- | Proxy allows requests to external websites
-- | References:
-- | https://www.securityfocus.com/bid/49957
-- |_ https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368
--
-- @args http-vuln-cve2011-3368.prefix sets the path prefix (directory) to check for the vulnerability.
--
author = {"Ange Gutek", "Patrik Karlsson"}
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = {"intrusive", "vuln"}
portrule = shortport.http
action = function(host, port)
local vuln = {
title = 'Apache mod_proxy Reverse Proxy Security Bypass',
IDS = { CVE='CVE-2011-3368', BID='49957'},
description = [[
An exposure was reported affecting the use of Apache HTTP Server in
reverse proxy mode. The exposure could inadvertently expose internal
servers to remote users who send carefully crafted requests.]],
references = { 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3368' },
dates = {
disclosure = { year='2011', month='10', day='05'}
},
}
local report = vulns.Report:new(SCRIPT_NAME, host, port)
local prefix = stdnse.get_script_args("http-vuln-cve2011-3368.prefix") or ""
-- Take a reference chrono for a 404
local start = os.time(os.date('*t'))
local random_page = rand.random_alpha(20)
local reference = http.get(host,port,("%s/%s.htm"):format(prefix,random_page))
local chrono_404 = os.time(os.date('*t'))-start
-- TEST 1: the loopback test, with 3 payloads to handle different rewrite rules
local all
all = http.pipeline_add(("%s@localhost"):format(prefix),nil, all)
all = http.pipeline_add(("%s:@localhost"):format(prefix),nil, all)
all = http.pipeline_add(("%s:@localhost:80"):format(prefix), nil, all)
local bypass_request = http.pipeline_go(host,port, all)
if ( not(bypass_request) ) then
stdnse.debug1("got no answers from pipelined queries")
return stdnse.format_output(false, "Got no answers from pipelined queries")
end
-- going through the results of TEST 1 we could see
-- * 200 OK
-- o This could be the result of the server being vulnerable
-- o This could also be the result of a generic error page
-- * 40X Error
-- o This is most likely the result of the server NOT being vulnerable
--
-- We can not determine whether the server is vulnerable or not solely
-- by relying on the 200 OK. If we have no 200 OK abort, otherwise continue
local got_200_ok
for _, response in ipairs(bypass_request) do
if ( response.status == 200 ) then
got_200_ok = true
end
end
-- if we didn't get at least one 200 OK, the server is most like NOT vulnerable
if ( not(got_200_ok) ) then
vuln.state = vulns.STATE.NOT_VULN
return report:make_output(vuln)
end
for i=1, #bypass_request, 1 do
stdnse.debug1("test %d returned a %d",i,bypass_request[i].status)
-- here a 400 should be the evidence for a patched server.
if ( bypass_request[i].status == 200 and vuln.state ~= vulns.STATE.VULN ) then
-- TEST 2: the internal hosts test. According to Contextis, we expect a delay before a server error.
-- According to my (Patrik) tests, internal hosts reachable by the server may return instant responses
local tests = {
{ prefix = "", suffix = "" },
{ prefix = ":", suffix = ""},
{ prefix = ":", suffix = ":80"}
}
-- try a bunch of hosts, and hope we hit one that's
-- not on the network, this will give us the delay we're expecting
local hosts = {
"10.10.10.10",
"192.168.211.211",
"172.16.16.16"
}
-- perform one request for each host, and stop once we
-- receive a timeout for one of them
for _, h in ipairs(hosts) do
local response = http.get(
host,
port,
("%s%s@%s%s"):format(prefix, tests[i].prefix, h, tests[i].suffix),
{ timeout = ( chrono_404 + 5 ) * 1000 }
)
-- check if the GET timed out
if ( not(response.status) ) then
vuln.state = vulns.STATE.VULN
break
end
end
end
end
-- TEST 3: The external website test. This does not mean that you can reach a LAN ip, but this is a relevant issue anyway.
local external = http.get(host,port, ("%s@scanme.nmap.org"):format(prefix))
if ( external.status == 200 and string.match(external.body,"Go ahead and ScanMe") ) then
vuln.extra_info = "Proxy allows requests to external websites"
end
return report:make_output(vuln)
end