Sometimes, walls get in the way, and when that happens, we need a door. A door needs a proper lock, or a security vulnerability may result. Server-side request forgery (SSRF) vulnerabilities can manifest in a number of ways, but usually it’s because a door was installed without a lock.
Clearly, this should not be permitted, and is the reason why the same-origin policy exists.
SSRF by Design
In our analogy, a cross-domain proxy is a door into another domain — in more technical terms, it is a server-side component that makes HTTP requests on the client’s behalf, and returns the HTTP response so that the client can load content that is normally off limits due to the SOP.
Reusing what exists is usually a good engineering principle, and when I searched for “cross-domain proxy” on Google, the first search result was vulnerable to SSRF under the right circumstances. Out of the box, the proxy.php file is disabled, and it requires configuration before it can be used. One of the configuration options is CSAJAX_FILTER_DOMAIN. If this constant is set to true, then proxy.php can be used to access any domain.
Practical Exploitation of SSRF
Consider that a web application server is typically connected to network segments that are normally off limits to the larger Internet. A good example applicable to virtually all systems is local network interface address 127.0.0.1, commonly mapped to the hostname localhost. In a vulnerable configuration, an attacker can abuse proxy.php to access the CUPS HTTP daemon, which is typically only accessible at http://localhost:631. To do so, the attacker would make the following request:
Accessing the above URL should result in the following page:
The CUPS daemon is used to configure printers. Don’t get me wrong: Printers are fun, but they’re not really our goal. A shell would be nice, and older systems running the CUPS daemon on http://localhost:631 were vulnerable to Shellshock. The CUPS daemon runs as root, so the successful exploitation of Shellshock in this context results in root privileges. The CUPS Shellshock vulnerability can be remotely exploited by sending POST requests through an SSRF vulnerability, which is supported by proxy.php.
Further Down the Rabbit Hole
There are numerous attack opportunities that become available when SSRF is used to access internal networks. To help identify these opportunities, the Burp Intruder’s Cluster Bomb attack type can be used to make proxy.php into a useful port scanner for exploring the internal network. To execute this attack, start by configuring Intruder as shown in the following screenshot:
The above Cluster Bomb attack type will use every permutation of the two lists represented by the $localhost$ and $631$ placeholders. The former list contains IP addresses that we want to scan, and the latter is a list of ports that may be running an HTTP daemon. Executing this attack in a test environment resulted in the following:
Above, we see that an HTTP server running on 192.168.201.1 is accessible because it returned an HTTP 200 response. When accessed using SSRF, the following authentication page was exposed:
The solution to this problem is that application developers needs to be more careful about the holes made in walls; a same-origin policy bypass needs to be more restrictive than a wide-open cross-domain proxy. Cross-origin resource sharing (CORS), introduced in HTML5, was created specifically to allow for a selective bypass of same-origin policy, and should be used in place of a cross-domain proxy whenever possible. In the days before CORS, developers needed to construct their own creative bypasses to the SOP, such as a cross-domain proxy, or JSON with Padding (JSONP)*. Developers will find creative and interesting solutions to difficult problems, but these are not always secure solutions.
CORS isn’t a one-size-fits-all solution, though. If you need to use proxy.php, set CSAJAX_FILTERS to true and define the list of $valid_requests, which are whitelisted requests that the server is permitted to perform on the client’s behalf. Setting CSAJAX_FILTER_DOMAINS to false will ensure that proxy.php will only send the exact requests defined within $valid_requests. Meanwhile, setting the CSAJAX_FILTER_DOMAINS to true will only filter by domain name, which could still lead to a compromise.
Another approach to consider is the use of a cross-domain proxy service — and let someone else take this dangerous functionally off your hands.
Security can be a grueling process, and when the days drag on, sometimes developers notice holes in the wall and look the other way — without taking into consideration the ramifications.
(Note: It is important to note that JSONP can also lead to serious vulnerabilities and must be used with caution.)