Michael Jordon, David Robinson - October 2011
In this blog I will describe a new type of security vulnerability which can allow full internal system access from the internet from an unauthenticated perspective. This technique exploits insecurely configured reverse web proxies to gain access to internal/DMZ systems.
Apache web server is affected by this issue when running in reverse proxy mode; Context have worked with Apache to produce a patch which reduces the risk of exploitable misconfigurations (CVE-2011-3368).
Reverse proxies are common place in the head-end infrastructure of organisations. They route web (HTTP and HTTPS) protocols from an external request to one of several internal web servers. The common usages are for providing load balancing, separating static content from dynamic content, or to present a single web server which is actually made up of several different web servers at different paths.
Context have proven this attack against Apache web servers which are using “mod_rewrite” to proxy web requests internally. Other proxies may suffer from this issue.
If the Apache configuration file is configured like this (prior to Apache's recent update):
RewriteRule ^(.*) http://internalserver:80$1 [P]
And not like this:
RewriteRule ^(.*) http://internalserver:80/$1 [P]
Then access from the internet to any internal system that is accessible from the inside of the proxy is possible; the difference is the trailing slash after the port number. Both lines appear to work in the same manner from the outside; what the rule says is, if any pages are requested from the Apache proxy server (as defined by the regular expression) then change the request to the internal server on port 80 (the port number is optional and does not change this attack) and append the path requested to the end of the URL ($1). The [P] indicates that the request should then be proxied. I control the $1 part of the URL rewrite. Now what is needed is to change the URL so that it accesses any arbitrary internal system, firstly changing the port number which is easy. If I requested a page of “80” then the port number will be changed to 8080. So, using CAT the raw HTTP request would look like this:
GET 80/console HTTP/1.0
Causing the proxy to try to connect to:
That’s fine, but the chances of there being anything of interest on port 8080 are pretty low; we want full control over the URL being generated. At first glance however, it appears that it is too late in the URL to be able to change which domain is being requested. Time to go back to basics on the structure of a URL.
The URL specification takes the following form: (http://en.wikipedia.org/wiki/URI_scheme).
Most parts are familiar however the authentication details are not so commonly used but is part of the specification. Therefore, Apache will understand and honour the credentials and forward them on as part of any request. Using this part of the specification we can change the resulting rewrite rule to make the ‘internalserver’ and port ‘80’ into a username and password. We then have full control of the domain, path and arguments. So in CAT the attack string would be:
GET @InternalNotAccessibleServer/console HTTP/1.0
Which the Apache reverse proxy will interpret and create the following request:
When the request is made it will send a username of internalserver with a password of 80 to the InternalNotAccessibleServer and load the page ‘console’. The username and password will be ignored by the internal server and it will respond with its standard response. We can access any internal/DMZ system which the proxy can access including administration interfaces on firewalls, routers, web servers, databases etc. Context has had plenty of success with this attack where credentials are weak on the internal systems allowing for full network compromise e.g. uploading Trojan WAR files on to JBoss servers. The full request is proxied including the method, cookies and any POST data.
This issue could also affect configurations where the proxy is configured to serve requests to certain paths from a different web server. The recent Apache update does not protect against this type of mis-configuration. For example a proxy setting of:
Rewriterule ^/images(.*) http://InternalImageServer$1
Means requests for anything in the images directory should be sent to the internal image server; where as other requests would be handled by another rule. Therefore we can modify the attack string to the following which would cause the same attack to work:
GET /images@InternalNotAccessibleServer/console HTTP/1.0
The mod_rewrite rule does not have to have a port specified. In which case the above attack will have the domain interpreted as a hostname but with no password and will still work. However, if a port was to be specified on the attack string then Apache will interpret the extra colon as being a password. For example, if the mod_rewrite rule is:
RewriteRule ^(.*) http://InternalImageServer$1
And a request is to be made to an internal server on port 8080 the following attack string would not work:
GET @InternalNotAccessibleServer:8080 HTTP/1.0
Because the following string would be generated:
The bold section above will be interpreted as being the username and the “8080” as the password. Therefore in the case where a port is being specified in the targeted server and the rewrite rule does not contain a port it is necessary to add in a colon at the start to act as a blank password.
GET :@InternalNotAccessibleServer:8080 HTTP/1.0
Resulting in the following URL which would allow for internal access on port 8080:
The easiest way to test for this issue is to request an external website by pre-pending the URL with an “at” symbol. If the proxy returns that site then there is an issue.
However, if the proxy cannot route outbound to the Internet then an alternative is to connect to localhost and check for the original page. This test can be extended to connect to different ports on the local host and check for a timing difference. These tests would need to be performed on all directories on the webserver. The following table shows example test strings that could be used to detect a reverse proxy bypass vulnerability:
|Attack String||Result if Vulnerable||Result if Not Vulnerable|
|GET @www.contextis.com HTTP/1.0||Displays Context’s website||400 Error page or a generic error page.|
|GET @localhost HTTP/1.0||Displays the original website|
|GET :@localhost HTTP/1.0|
|GET @targetdomain HTTP/1.0
Where target domain is the domain name for the website under test.
|GET :@localhost:8123 HTTP/1.0|
8123 Is a port which is assumed to not have anything listening.
|Delay before displaying an error page.||Immediate error page.|
|GET @10.0.0.1 HTTP/1.0|
The IP address is assumed to be an IP that does not have a web server.
|Delay before displaying an error page.
Or an internal server if you are lucky.
|GET :@localhost:80 HTTP/1.0||Displays the original website|
If this is found then an attacker would need to know about, or brute force, the internal IP address configuration or hostnames. Of course, the web application running on the site might reveal this in an error message, HTTP headers etc. CAT’s fuzzer can then be used to scan the internal address range to find valid machines, which can then be port scanned to find web interfaces.
So what can people do about this? Patch, to ensure that you are using the latest version of Apache. Second, it is important for reverse proxy configurations to be reviewed, to ensure that the rewrite rules cannot be used to access internal systems. As can be seen in this blog, rewrite rules have a great deal of flexibility but the implications of mis-configuration are critical. Context therefore recommends this new type of security vulnerability, which affects Apache and potentially other proxy configurations is included in any penetration tests and security configuration reviews.