Most developers will have heard of Cross-Site Request Forgery (CSRF) before. It’s a constant threat that affects any website with a form, or performs any actions, and we typically have to mess around with CSRF tokens, XSRF headers, SameSite cookies, and more, to ensure our apps are protected.
Like I said, it’s a significant issue!
But what about SSRF, have you heard of it before? Do you even know what it means?
SSRF stands for Server-Side Request Forgery, and although both describe forged requests, it is very different from CSRF. CSRF is all about compromising the browser and the user, while SSRF is about compromising the server and the environment itself.
Also, fun fact, SSRF even has its own OWASP Top 10 category, while CSRF has to share with others.
What is SSRF?
SSRF, or Server-Side Request Forgery, is a type of security vulnerability where an attacker tricks a server into making unauthorized requests to internal or external resources. In an SSRF attack, the attacker manipulates the server to send requests to locations it normally shouldn’t have access to, such as private internal systems or sensitive external APIs. This vulnerability can lead to data exposure, unauthorized actions, or even full control of the compromised server.
Protecting your API from SSRF requires a multi-layered approach, as relying on simple validation might not be enough. Many developers unknowingly make critical mistakes when securing APIs, leaving them exposed to threats like SSRF. For example, improper input validation or the absence of proper access controls can easily open doors to such attacks. Moreover, it’s crucial to ensure that backend services are shielded from any unauthorized access attempts through carefully crafted network policies and role-based access controls (RBAC).
For more details on common mistakes and how to avoid them, check out this detailed guide on securing your API the wrong way. It breaks down common misconceptions and outlines best practices for keeping your API safe.
Unlike Cross-Site Request Forgery (CSRF), which targets the user’s browser, SSRF specifically exploits the server-side of web applications, making it a more dangerous and complex threat. Let’s break it down and explore it even further below.
Server-Side Attacks
There are two sides to web apps: the client side, which runs in the browser and encompasses the visual elements and user interface, and the server side, which runs on the server and has your business logic, database, configuration, etc.
Client side attacks and vulnerabilities run in the browser, usually within the victim’s browser. This gives the attacker access to the victim’s account and session, and depending on the vulnerability the scope can be quite broad - although it is still tied to what is available in the browser and what the victim has access to. You also need to get the victim to run your payload somehow. Sometimes this is easy, other times it’s quite difficult.
By comparison, Server side attacks and vulnerabilities run on the server (which you probably guessed, given the name). Which means that rather than exploiting a victim’s browser and user account, you’re exploiting functionality within the server itself - making the server perform actions it’s not supposed to be doing. SQL Injection is a good example of this - you’re modifying an existing SQL query to get it to retrieve information or perform actions it’s not supposed to do.
The other aspect of Server side attacks that’s worth mentioning is that you’re often working blind to find and exploit them. Client side attacks, on the other hand, can usually be found and tested easily within your own browser before seeking to compromise your victim.
Request Forgery
As the name would suggest, Request Forgery is where the attacker is able to trick a system into making requests that look legitimate, but are unauthorized or unintended. This can allow an attacker to perform actions they normally would not be able to perform, or access resources they should not be allowed to access.
During a Forged Request, the target system receives a request that looks legitimate, but is actually a forgery. This forged request will come from a source that the target trusts and will contain valid authorization. Maybe it has the right session cookie, or a valid authorization header, or maybe the IP address is allow-listed, or (and this will be important later) it’s arrived via a private network. It doesn’t matter what the reason is, the target trusts the source of the request, believes it’s legitimate, and authorizes it to perform the requested action or return the requested resource.
How SSRF Attacks Work
Understanding SSRF Attacks
If we first think about a CSRF attack, they work by running malicious javascript within the victim’s browser, which makes a forged request to the server. As far as the server is concerned, the request comes from a real user who has been authenticated, and it accepts the request and performs the action. (This is assuming the site is vulnerable to CSRF attacks, and has no protections in place.)
If we apply this to SSRF, the concept is roughly the same. If we can trick the server into making a specific request, we can perform actions or extract specific information that we should not be able to access.
Example of SSRF Attack
Consider this example:
We have a server sitting behind a cloud-based firewall (WAF), which protects it from DDoS attacks, detects brute-force and credential stuffing attempts, and blocks other malicious traffic from reaching the server.
In order to compromise this server, we need to bypass the firewall and send our requests directly to the server. However, the domain name points to the firewall, which means the only IP address we have is for the firewall and not the server behind it.
So in order to get the IP address of this server, we need to trick the server into giving it to us. If the application makes external requests to user-controlled URLs, such as to download and cache avatar images, then it gives us an opening we can abuse.
The attacker can submit a unique URL to a server they control, and monitor their logs for the request from the target server:
The request will come from an IP address, which may be the raw IP address of the target server. If this is the case, the attacker can then bypass the firewall and make requests directly to the target server.
What Else Can You Do with SSRF?
The above example is a very simple example, that can be abused to allow for other attacks to be more effective. However, there are also attacks you can perform with SSRF on its own.
Denial of Service
In the above example, we’re using an image URL to identify the server’s IP address. However, you could easily provide a URL to a huge image file, which the server would attempt to download and process. If the file was big enough, it may cause the server to either run out of disk space, or memory, preventing it from handling any more requests - effectively taking it offline.
In situations like this, rate limiting can be a crucial defense mechanism. By limiting the number of requests a user can make or setting a maximum payload size, you can prevent attackers from overwhelming your server with oversized files or excessive requests. Rate limiting not only prevents denial-of-service (DoS) attacks but also ensures that your server resources are allocated fairly and are not monopolized by malicious actors.
Rate limiting is an essential yet sometimes overlooked practice in API security. For a deeper look into its importance and how it can safeguard your API, read this article: Who cares about rate and resource limiting.
Why go to the effort of sending millions of requests to DDoS a server, when a single request can have a disastrous result?
Data Leaks
If the application accepts user-defined URLs, accesses them, and then returns the results from that request to the user in some format, then it allows the attacker to read data from the perspective of the server itself. This often inherits a privileged position, sometimes by allowing access to internal resources inside a private network that cannot be accessed from the outside, or since the server itself is automatically trusted, or maybe these requests are performed with additional headers that authorize requests.
One of the critical issues that arises during an SSRF attack is the improper handling of sensitive data. APIs that return too much information or expose internal endpoints can inadvertently leak vital data, including server metadata, authentication tokens, or even personal user data. Developers must be careful to sanitize all returned data, ensuring that nothing sensitive is sent back to the user. Furthermore, server-side validation should limit which URLs can be accessed via API requests, preventing unauthorized external or internal data exposure.
To learn more about preventing sensitive data leaks through your API responses, you can explore this comprehensive guide on how not to return data from your API.
To dive into this further, let’s take a look at the Capital One breach in 2019. There has been a lot written about this, and differing opinions regarding the exact exploitation method, so I’ll be focusing on an article written by Riyaz Walikar titled "An SSRF, privileged AWS keys and the Capital One breach".
Real-World Example: The Capital One Breach
In the attack, “the attacker gained access to a set of AWS access keys by accessing the AWS EC2 metadata service via a SSRF vulnerability.” With access to these keys, they were able to access the S3 buckets in the account, and all of the data stored within them.
In case you’re not familiar, Cloud and VM providers usually provide internal metadata endpoints that all of the servers that they provision are allowed to access. These include a bunch of boring information (IP address, network configuration, etc.) and often some really sensitive information too.
This can include API keys, public and private keys, authentication tokens, etc. It depends on the provider as to what they include, but it’s designed to make it easy for automated provisioning scripts and applications to access the environment-specific details, without user intervention.
Given what we know about SSRF now, you should be able to join the dots and see that a successful SSRF attack would be able to make a request to one of these metadata endpoints. Back when this attack occurred, there were no additional protections, so a simple request through SSRF was able to retrieve the metadata for the server.
Unauthorized Actions Through SSRF
I haven’t been able to scrounge up the details, but this particular SSRF vulnerability must have provided the retrieved data back to the user in some way. AWS provides a specific metadata endpoint that provides IAM Role credentials (IAM - authentication & authorization) for the server, and as per the article: “It appears that this role had excessive privileges allowing the listing and access to S3 storage. This privilege was used to list the buckets and download them locally.”
ubuntu@ip-xxx-xx-xx-x:~$ curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ISRM-WAF-Role
{
"Code" : "Success",
"LastUpdated" : "2019-08-03T20:42:03Z",
"Type" : "AWS-HMAC",
"AccessKeyId" : "ASIA5A6IYGGDLBWIFH5UQ",
"SecretAccessKey" : "sMX7//Ni2tu2hJua/fOXGfrapiq9PbyakBcJunpyR",
"Token" : "AgoJb3JpZ2luX2VjEH0aCXVzLWVhc3Qt[...]8RXr1axpYnJv2GHb8h/det89iwpyk77+8YcEvRc+DGTLIcUIxDoirgck9bpP3EBXfs=",
"Expiration" : "2019-08-04T03:16:50Z"
}
From that point onwards, the attacker was able to use the credentials they had obtained to access data within the S3 buckets.
It’s worth pointing out that this was only possible because the output of the request was sent back to the attacker in some form. If this data was not retrievable, making the GET
request to the metadata service wouldn’t have exposed anything on its own.
In addition to controlling what information is returned, you should also implement rate limiting to reduce the impact of potential SSRF attacks. Rate limiting can prevent attackers from flooding your API with requests, reducing the likelihood of denial-of-service (DoS) attacks or large-scale data exfiltration. Even if an attacker can make a forged request, rate limiting will limit how frequently they can exploit it, mitigating the damage.
Rate limiting is often overlooked but is essential for ensuring the stability and security of APIs. Read more about why rate limiting matters in this blog post: Who cares about rate and resource limiting.
One common way to do this is through a web preview or PDF generator, which takes a user-defined link and takes a screenshot (or PDF export) of the user’s URL. If the application isn’t blocking internal URLs, then this could very easily be used to obtain a screenshot of an internal metadata URL - complete with sensitive information being revealed.
Note, AWS have made some changes to prevent this attack from occurring. This is common among cloud/VM providers, which makes this style of attack significantly harder.
Unauthorised Actions
Even if you can’t retrieve the output from a request, you may still be able to perform actions on the server. Let’s take a look at the “How I Chained 4 vulnerabilities on GitHub Enterprise, From SSRF Execution Chain to RCE!” article by Orange Tsai.
The whole article is a great example of using smaller bugs like SSRF and chaining them into a very significant attack, but I just want to touch on the first bug, titled “First Bug - Harmless SSRF”.
In his article, he identifies the Webhook feature of Github is vulnerable to SSRF, where it sends a blind POST request to an endpoint the user defines. There are some limitations to the URL, but they find a way to bypass those and run requests against localhost.
The question is, what can be done with just a POST
request?
Well, it turns out that “There is an Elasticsearch service bound on port 9200. In the shutdown command, Elasticsearch doesn’t care about whatever the POST data is. Therefore, you can play its REST-ful API for fun :P”
All it takes is creating a Webhook for the following URL, and the Elasticsearch service will be shutdown any time it’s run.
http://0:9200/_shutdown/
In this specific case, there is limited scope beyond denial of service - but the concept is applicable to any endpoint that would take a blind POST
request within a private network. The potential for abuse is quite significant if the right endpoints exist and an attacker can find them.
Conclusion
I like to think about SSRF as a creative vulnerability. It’s not straightforward like Cross-Site Scripting (XSS) or SQL Injection (SQLi), where you just need to find the right combination of characters that allow some javascript to run. Instead, you need to bypass limited controls, find hidden endpoints, and get creative to extract the information you’re looking for.
For a hacker, this may be their sort of fun, and given how SSRF is often overlooked or misunderstood, there are probably a lot of vulnerable apps just waiting to be found. As developers, we need to understand how SSRF works and what sort of things can be done with it, if we have a chance of stopping it.
If you’re building an API, part of finding an attacker who is trying to exploit SSRF is being able to observe our APIs.