SSRF – an old friend in the limelight
SSRF has been around for a while; this year it made it to both the OWASP Top 10 and CWE Top 25. What's behind the acronym?
Recently OWASP released their new Top 10 list that included a new item discussing a “concrete” vulnerability: the Server-Side Request Forgery (SSRF). SSRF also became part of the 2021 CWE Top 25 Most Dangerous Software Weaknesses list in 2021. Both circumstances indicate that this type of vulnerability increasingly became a problem for many systems and at many companies developing and operating Web applications. SSRF, for instance, was in the background of the Capital One breach, where an employee could steal more than 100 million consumer applications.
Actually, it is not a new vulnerability. For instance, the Common Weakness Enumeration first enlisted it in February 2013, though it was first used in 2008, as mentioned in an ERPScan whitepaper. Though it did not make it to the CWE Top 25 list until 2021, it was on the radar well before. From 2019 to 2021, SSRF moved 6 positions up on the CWE Top 25: in 2019 it was the 30th, while in 2020 it was the 27th, and finally in 2021 it became the 24th.
As a result of digital transformation, every company that wanted to remain competitive renewed their business processes and moved to the internet, thus exposing themselves to others who may have good or malicious intentions. Usually, these companies expose only a tiny fragment of their underlying infrastructure directly, hiding their internal operation behind a firewall. But if they made a mistake in the implementation, an attacker could exploit this exposed server as a proxy and do many things through it.
In addition, “implementation” above does not solely refer to making mistakes in the source code, but also using third party components that may potentially contain an SSRF vulnerability. For example, there was a similar bug in the implementation of curl quite some time ago.
How does an SSRF attack work?
SSRF is a confused deputy attack by nature. In these types of attacks the adversary can trick an application to act as a proxy (the “deputy”) and do something malicious that serves the attacker’s purpose – for instance, reveal some sensitive information, or accomplish a (REST) API call. This action is something the attacker itself cannot do due to lack of privileges, but the application has permissions to do. And furthermore, the application can be tricked to do it by “confusing” it. In this, it is very similar to many martial arts (Aikido for instance), where you don’t necessarily have to be stronger (in our case: have more permissions) than the enemy. It’s enough to be smarter than them and use the power of the opponent to beat them. SSRF is the Aikido of software security.
In case of SSRF, the adversary initiates an action in the system that contains a URL pointing to a resource that is otherwise inaccessible to the attacker. The resource is not necessarily accessed via http:// or https://; it may be any other protocol, such as file, FTP, gopher or ldap. The vulnerability itself occurs if the code on the server can – and does – access these resources without any validation and opens the resource specified by the attacker. The vulnerable server functionality that allows somebody to trigger such an access can be literally anything. It may be a configuration feature on the UI. A server action may send a GET or a POST request defined by the user. The user may upload an XML or JSON file where the parser will trigger an http request as a side effect of parsing. Or one can also exploit an injection vulnerability.
On the other hand, by issuing a harmful request, adversaries may do many things. For example, they can delete or add a user in a database – or increase the balance of an account. Also, one can use SSRF to steal confidential information such as passwords (or – in best case – salts password hashes with the corresponding salts), business secrets, or sensitive personal information (GDPR!). In some cases, the attacker will simply use SSRF for port scanning on the intranet, thus exploring the internal network of a company.
SSRF with External XML Entity (XXE)
External XML Entity (XXE) is one of those famous attacks that can trigger SSRF. Let’s see how.
XXE can occur if an adversary sends in an XML with a well-crafted DTD (Document Type Definition) as part of it to the application. A DTD can contain different markup declarations telling the parser how to parse the XML. Among others, it can define entities – which work kind of like variables – as key-value pairs. Such an entity can also be an external entity, if we use the SYSTEM keyword with a URI. In that case the parser will use the appropriate protocol, typically file:// or http:// to open the file or fire an HTTP request. The content of the file or the response to the request will be the value of the entity in question, when used in the XML. The parser will just replace the reference to the entity with this actual value, gathered from the file or the response.
So, how can XXE lead to SSRF? It’s trivial. Just put the desired request in a URI within an external (SYSTEM) entity as part of the DTD. Then refer to this external entity in the XML. The parser will do you a favor, and fire the desired request when it processes the XML. This will completely happen on the server-side within the intranet. No firewall or intrusion detection will stop this. Not to mention that similar cloud security solutions – for instance Security Groups on AWS – will also be ineffective here.
Here is an example payload XML that you can just send to any application that parses XML on the server side:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE foo [ <!ELEMENT foo ANY > <!ENTITY xxe SYSTEM "http://10.0.0.101/Delete/4" >]> <foo>&xxe;</foo>
When parsing the above XML, the parser will do a GET request to http://10.0.0.101/Delete/4 when filling in the value of the <foo> tag. And the goal of course is not to fill in <foo>, but to execute the contained HTTP request itself towards the REST API, and maliciously delete item 4 from the database.
You can find some further interesting payloads here.
Protecting against XXE attacks is simple – but it’s just as easy to forget to do it. The usual approach is to configure the parser correctly, telling it not to accept and handle DTD entities at all (any entities, or just external entities). Obviously, no entities means no XXE. Worth noting: disabling the entities – or even better, the DTD altogether – prevents some further XML-based attacks, for instance the billion laughs.
The corresponding OWASP cheat sheet defines two types of scenarios where an SSRF attack can work:
- The vulnerable server can send requests to some identified applications. For example, a server using multiple, predetermined LDAP interfaces in its user management system.
- The vulnerable server can be configured to send requests to any arbitrary network location. For example, setting a webhook or a callback URL.
In both cases, a thorough validation of the received and handled URL is the best practice against an SSRF attack. Needless to say, the validation should be based on an allowlist, not a denylist. We should explicitly enlist the allowed targets when triggering an HTTP request server-side. Proper access control will also remarkably tighten the attack surface. The various modules and applications in the internal network should not trust each other by default. They should authenticate each other and check whether they have the right privileges to issue the given request and access the resource in question. Pretty much aligned to the zero trust architecture. Finally, using internal firewalls can also prevent such undesired access.