First of all, let’s start with what is an SSRF?
As SaN ThosH explained it in their excellent post (about SSRF server side request forgery types), Server Side Request Forgery (SSRF) refers to an attack where an attacker is able to send a crafted request from a vulnerable web application in order to make it appear that the server is sending the request, possibly bypassing access controls such as firewalls that prevent the attackers from accessing the URLs directly.
In this blog post, I’ll show you how a server can be used as a proxy to conduct port scanning of hosts in internal and external networks. Below you will find an example of how I was able to get into a Port Scanner and exploit an SSRF vulnerability.
The approach highlighted here differs from a Blind SSRF exploit. A blind SSRF occurs when an application's backend receives an HTTP request but the request is not sent to the frontend of the application.
The vulnerable URL had an import note from the URL functionality as you can see here:
Import from URL functionality of a vulnerable web application
In addition to http:// I tried the other URL schemas to read and make the server perform actions (file:///, dict://, ftp://, ldap:// and gopher://). However, only http:// worked.
When I tried to import the URL **http://127.0.0.1:22**, the web server returned an interesting banner:
Testing SSRF in import from URL functionality of a vulnerable web application
I saw the response from the website, it was a JSON telling the information I could see in my web browser:
Part of the JSON message in the body request when a port was opened
But when I asked for another port, i.e. 1234, the error message was different:
Part of the JSON message in the body request when a port was closed
So then I tried another port, i.e. 4567, and got the same “Connection refused — connect(2)” error. So I started thinking…
This behavior makes me think that when the server has a port open it then it shows me a different message than “Connection refused — connect(2)”. Then I used Burp to make a fuzzer iterating port number which marks responses when it found the ^((?!Connection refused).)$* regex in their body.
Burp Intruder configuration
Burp Intruder configuration
After that, I launched the attack and ordered results according to the “Connection refused” column. Thus, I knew what ports were opened (the payload column) because the requests were marked with a checkmark:
Burp Intruder results
Of course, you can do it with a script, but I think pentesters need to be more practical because, oftentimes, we have a short and limited time to test.
With this point type of attack, you can imagine that if you are doing an internal and external pentest, you could do a host discovery and, even more, a port scan of the entire internal network, potentially bypassing firewalls because surely this host is on DMZ.
There are ways to mitigate this vulnerability as you can see in the next paragraphs, but it’s very important to know that simple blacklists and regular expressions applied to user input are a bad approaches to mitigating SSRF. In general, blacklists are a poor means of security control because attackers will always find methods to bypass them.
Regarding mitigation, it would have been impossible to exploit this vulnerability if the following mitigations were implemented:
Whitelists and DNS resolution The most robust way to avoid Server Side Request Forgery (SSRF) is to whitelist the DNS name or IP address that your application needs to access. If a whitelist approach does not suit you because the site should accept user URLs, you must rely on a blacklist (remember this is not the best solution). It’s important to validate user input properly. For example, do not allow requests to private (non-routable) IP addresses (detailed in RFC 1918).
Response handling To prevent response data from leaking to the attacker, you must ensure that the received response is as expected. Under no circumstances should the raw response body from the request sent by the server be delivered to the client.
Limit the requests number You should set a limit with a CAPTCHA or similar. An adaptive CAPTCHA might be a good option for example, reCAPTCHA, which uses an advanced risk analysis engine and adaptive challenges to keep attacks (or automated software) from engaging in abusive activities on your site.
As you can see, the correct mitigation to adopt will vary from application to application. In other words, there is no universal fix to SSRF because it demands highly on application functionality and business requirements.
I hope you enjoyed this post!
Here are a few other interesting articles about SSRF: