SSRF’s up! Real World Server-Side Request Forgery (SSRF)
In this blog post we’re going to explain what an SSRF attack is, how to test for it, and some basic guidelines on how to fix it. We will be using a real-world example, exploiting a vulnerability we discovered in a commercial Business Intelligence product called Dundas BI.
Following responsible disclosure guidelines, the vulnerability discussed in this blog post has been reported to the vendor and the vendor has released a patched version of the software. Shorebreak Security would like to commend Dundas BI for taking security issues seriously and rapidly addressing the vulnerability, and for also sending us a sweet care package full of schwag.
What is SSRF?
SSRF (Server-Side Request Forgery) is a type of vulnerability that allows an attacker to force an application to issue requests on behalf of the attacker, to unintended resources. The following diagram attempts to explain visually how this attack works:
Conceptually, the attack “encapsulates” a malicious request (request B) in an authentic request (request A), by means of manipulating the vulnerable feature. It is important to note here that “Target server(s), network(s)” can include both internal and external networks from the perspective of the vulnerable server.
Because of the nature of this vulnerability, these features will probably have some of the following characteristics (this list is not exhaustive):
- Linking/embedding external resources (embedding images is a common vector, also PHP file includes).
- Checking the status of a resource (common in monitoring tools).
- Server forwarding a request to another system: external authentication, querying an external API, a misconfigured reverse proxy.
SSRF exists when the server, as part of one of its features, fetches data or queries an internal or external resource. The key is that this request includes a value that the attacker can manipulate, potentially allowing the attacker to completely change the request being performed by the server.
This vulnerability is conceptually very similar to Remote File Inclusion vulnerabilities, which are very common in applications coded in PHP. These two vulnerabilities have a lot of overlap. You can use an RFI to achieve a lot of the same things you achieve with SSRF (port scanning, name resolution, etc.), but in a typical RFI vulnerable application, the goal is code execution. Usually, SSRF does not allow code execution, but it all depends on the particular implementation of the vulnerable code. It is common though, to leverage a SSRF vulnerability to attack internal applications.
Usually, SSRF is exploited to perform network scans or attacks against internal systems that may be hidden from the Internet, but accessible from the affected server. The usefulness of this attack will be very much dependent on the verbosity of the error messages returned by the server. For example, if the attacker modifies the request so that the server tries to fetch a resource from an Internet IP on a different protocol (i.e., making it request http://example.com:21, example.com having port 21 open with an FTP service listening) it would be necessary to see what the error message says. It could say “Request failed”, it could say “Connection timed out”, or even “Connection refused”. These error messages offer valuable information to the attacker which will be key to determine the result of the manipulated request.
After mapping out the server responses with their meaning, it is possible to start leveraging the attack. Usually, one of the interesting things one can do is write a Python script which will port scan the internal network, to gather insight into the target’s infrastructure. After mapping out the network, it is also possible to leverage the SSRF to perform targeted attacks against internal services. Normally, the malicious SSRF request performed by the server will be an HTTP request, so a common attack vector on internal networks are HTTP servers.
Again, the impact and the potential of this attack will be very much dependent on the particular implementation.
SSRF in Dundas BI
Dundas BI is a web-based analytics solution developed by Dundas Data Visualization, Inc. During one of our engagements, we found that this product was being used. After spending some time exploring the features and attack surface of the application, one thing caught our eye. One of Dundas BI features allows you to export what you’re seeing in the data dashboard to different formats. For example, the following screenshot shows how it’s possible to share the current dashboard as an image:
After submitting the form, the following POST request is issued:
It caught our attention that a parameter with the name “viewUrl” was being sent, with a value that appeared to be a relative URI:
It seems that this “shortLink” will be used as a shareable link, which you could send to other users in an easy way (we didn’t analyze how that short link is constructed/deconstructed, but it could be an interesting vector to study in another blog post).
The fact that the variable name included “url” in it made us think that this could be a good candidate for this type of vulnerability.
When you have a SSRF candidate, a good first test to do is the following:
- Set up a controlled Internet (or internal if testing locally) facing web server.
- Make sure you can see the request log for that server, (“tail -f /var/log/apache2/access.log” or the output from Python’s HTTP server module, or even just “nc -nlvp <80|443>”).
- Try changing the variable value to something like “http://<your_webserver>/zzz”.
- If you receive a request to “/zzz” after you execute the manipulated request, it is probable that the attack worked.
We performed the exact same steps in this case, setting the “viewUrl” variable with the value of “http://<our_ip>/zzz” and executed the “share as image” POST request.
Immediately, we received a request from the server!:
And yes, that’s the cookie value also being sent to the attacker.
After the malicious request was executed by the server, we checked what the response was from the actual server:
The server fails, revealing a stack trace detailing that the problem was that the resource was not found (HTTP error 404). After this first successful attempt, we started to further explore the application behavior to determine the potential impact of the vulnerability.
The next thing to test was sending the same request but modifying the “malicious” URL so as to test different ports, protocols, and hostnames. Interesting variations are the following:
- Test for ports. After the first SSRF test was successful, replicate request against the same controlled server but changing the port. Test for a port that is open and to another that is closed. Analyze both responses and compare the differences.
- Brute-force for internal hostnames. Instead of an IP address, brute-force for URLs that point to potential internal hostnames, hoping that the server will resolve them. It is useful to omit TLDs or domain names if you don’t have that information.
- Try different handlers. This could overlap with LFI/RFI, but if for example this was a PHP application, you could try the “file://”, “php://input”, etc. handlers. Each framework could have different URL handlers so make sure you try to understand the application and make smart tests.
- Search for other local services running on the vulnerable machine that could be leveraged or exploited to further access.
After performing most of these tests, we were able to determine the following, given the verbose responses of the server:
- ERR_INVALID_HTTP_RESPONSE -> Target server port open and service is HTTP
- ERR_ABORTED -> Target server port open and service is HTTP
- ConnectionRefused -> Target server responsive but port closed
- ConnectionTimedOut -> Target server unreachable
- UnsafePort -> Port considered unsafe by Dundas (.e.g: SSH, FTP, etc.)
So, this allowed us to create a set of scripts that scan networks (internal or external), resolve internal DNS names and eventually attack HTTP servers (internal or external) using the Dundas application as a proxy.
In this case, the SSRF request being performed is an HTTP request. So, for example, one could not use this vector to attack services that do not speak HTTP (unless the target service is terrible and fails when it receives unknown input).
Other interesting real-world examples
If you’re interested in reading about other examples of this attack, we’ve gathered a few write-ups that we found interesting:
- “How I Chained 4 vulnerabilities on GitHub Enterprise, From SSRF Execution Chain to RCE!” by Orange Tsai. – https://blog.orange.tw/2017/07/how-i-chained-4-vulnerabilities-on.html
- “Into the Borg – SSRF inside Google production network” by Enguerran Gillier. – https://opnsec.com/2018/07/into-the-borg-ssrf-inside-google-production-network/
- “Wappalyzer SSRF Write up” by Alyssa Herrera. – https://email@example.com/wappalyzer-ssrf-write-up-2dab4df064ae
- “Pivoting from blind SSRF to RCE with HashiCorp Consul” by Peter Adkins. – https://www.kernelpicnic.net/2017/05/29/Pivoting-from-blind-SSRF-to-RCE-with-Hashicorp-Consul.html
There are three main strategies to follow when trying to address this vulnerability. Through some basic defensive techniques it is possible to prevent this attack or at least make it significantly more difficult for an attacker to exploit:
- Input validation: as always, it is important to validate user (attacker) supplied data so that only expected values are accepted. If a variable only needs to have the name of a resource and you know names are only letters, then the character set for that variable should only be a-z, A-Z and eventually 0-9. It is best to keep it simple, use tested libraries for data validation and avoid complex regular expressions. In the case of SSRF-type payloads, if applicable, reject any value that has an unwanted scheme in it (schemes are “http://”, “https://”, “ftp://”, etc.).
- Whitelisting: if the application requires fetching resources from external servers programmatically, then define a whitelist of known resources (hostnames and schemes) and refer to each element on the list through an identifier, so as to not expose the location of the resource to the attacker.
Blacklisting is not recommended, as there are multiple tricks an attacker can use to bypass basic black lists. For example, one can refer to an IP address by using decimal notation: http:9999999. Where 9999999 would be the IP address expressed as decimal. This would bypass a filter that rejects anything looking like “http://<IP>”.
- Traffic filtering: implement network level protections (firewall rules) to restrict which servers the application can communicate with. Don’t forget about localhost traffic, as SSRF can be used to access different ports on the server hosting the application.
Software: Dundas BI
Vendor: Dundas Data Visualization, Inc.
Affected version: < 184.108.40.2060
Vulnerability type: Server-Side Request Forgery
CVE ID: CVE-2018-18569
Date discovered: 09/18/2018
Date disclosed: 09/20/2018
Before disclosing publicly, Shorebreak contacted the vendor to report the vulnerability and allowed reasonable time for a fix to be released. The response by the vendor was immediate and a new version of the software which addressed this issue was released quickly.
- 09/20/2018: Shorebreak contacted vendor for the first time informing about the issue
- 09/20/2018: Vendor responded requesting more information.
- 09/21/2018: Shorebreak sent the vendor the technical report detailing the vulnerability and PoC.
- 09/21/2018: Vendor acknowledged receiving the report.
- 10/02/2018: Vendor informed Shorebreak that Dundas BI version 220.127.116.110 was released, which included a fix for this vulnerability.
- 10/11/2018: Shorebreak tested the vulnerability on version 18.104.22.1681, confirming that it was addressed.