Welcome to our Pentest Files blog series.
Each blog post will present an interesting or dangerous finding one of our testers has identified in an actual recent pen-test, so you can see the kinds of cool things our pen-testers get up to, and also to help you take steps to prevent similar vulnerabilities in your own assets.
These findings are taken from real reports, anonymised, and published with kind permission from our clients.
During annual testing for one of our customers, it was discovered that their web application was vulnerable to a server-side request forgery (SSRF) vulnerability.
Our tester, Adam, was able to successfully exploit functionality on the webapp to gather notable system information as well as further exploitation leading to the retrieval of valid API credentials for the customers’ AWS EC2 instances, escalating the overall score rating from a medium to critical severity.
A typical SSRF vulnerability occurs when an attacker instructs an application to load an internal resource which otherwise should not have been accessible from the outside.
For example, an attacker could supply an internal HTTP URL to a vulnerable application function. Should the application allow us to load this resource and return the resulting response of the request to the user, it could be used to access internal services and gather sensitive information.
Lets take a car dealership for example, they have an application that lets us see what cars are in stock. To provide stock information of a car, the application must query the various back end APIs they use.
This is done by passing the URL to the relevant back-end API endpoint via a front-end HTTP request. So when a user views the stock status for a car, their browser makes a request like this:
POST /model/carstock HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 224 carStockApi=http://carstocks.io:8080/product/stock/check%7FproductId%3E7%26carstoreId%221
This causes the server to make a request to the specified URL, retrieve the stock status, and then provide this information back to the user.
Now what’s to stop us from intercepting the request and modifying the parameters, such as the URL that it uses? Like so:
POST /model/carstock HTTP/1.0 Content-Type: application/x-www-form-urlencoded Content-Length: 224 carStockApi=http://localhost/admin
What’s important to recognise here, is that the attacker cannot simply visit the /admin address directly in their browser, as they are unauthenticated and perhaps won’t see anything of interest, but when you are tricking the application into making internal requests, normal access controls are bypassed. This is because the request appears as if it is coming from a trusted source, which it is!
Depending on the environment configurations of an application, an attacker could potentially leverage an SSRF vulnerability to;
- Access internal resources,
- Launch Denial Of Service (DoS) Attacks,
- Bypass external security perimeter resources,
- Conduct internal port scanning,
- And even accessing sensitive resources.
During the test, when creating a new transaction on the target webapp, Adam was able to manipulate the parameters within the HTTP requests, tricking the server into creating new HTTP requests.
So what's the danger here?
An attacker could potentially abuse this vulnerability by proxying malicious requests through the application, so they appear as if they are originating from the clients own infrastructure, causing all kinds of issues.
Adam was able to further escalate the SSRF vulnerability and leverage it to query the client's Instance Metadata Service 1 (IMDS1) API at the clever AWS 169.254.169.254 URI.
The 169.254.169.254 IP address if often described as a ‘magic’ IP address in the cloud world. It is a link local address that is only available internally, from the host machine, and without encryption. In AWS’ case, they have used it to store specific user and meta data from an specific instance.
IMDS, or more commonly known as the EC2 Metadata API provides a useful method to access user data, information and credential material associated with a specific EC2 instance.
An Amazon EC2 instance is hosted on a virtual server in Amazon's Elastic Compute Cloud (EC2), it is often utilised by businesses to run their applications on the AWS cloud infrastructure.
Exploiting the IMDSv1 API is often a target for attackers, as it can be used to gain a solid foothold into AWS-based networks, and SSRF plays a big role here, as it often does in these scenarios.
The API endpoints held valid credentials for the customer’s AWS EC2 instances. These valid credentials provided us read/write permissions for all of their AWS S3 storage buckets, which contained a plethora of sensitive information.
As well as EC2 credentials, Adam was able to make further malicious callbacks on the webapp to successfully obtain credentials for the clients authentication software.
Fortunately, we were able to catch this issue for our client before any harm was done, and we were delighted to assure them that the vulnerability had been resolved after they had completed the provided remediation steps and retesting had been completed.
To mitigate the risks of the vulnerabilities that were found during the test, we had a few remediation steps our client could take.
Firstly, suggested that the AWS IMDSv1 API be migrated to the newer version IMDSv2, which requires significantly more steps for an attacker to successfully exploit.
We also recommended they enforce IMDSv2 for all EC2 instances, with IMDSv2 would significantly reduce the risk of an adversary stealing IAM credentials via SSRF.
IMDSv1 is deemed to be insecure as there is no authentication requirement to obtain data from the endpoint, which allows attackers to easily gain access to sensitive information within the metadata service.
IMDSv2 is the newest version of the API, offering enhanced instance metadata access, that now requires session-oriented requests - to add defense against unauthorised access.
We also provided suggestions for implementing a whitelist of allowed callback URLs so attackers are no longer able to trick the server into making new HTTP requests from the internal network (example 127.0.0.1, or 10.x.x.x). We were only able to make limited suggestions, as the client required the web application to make server-side HTTP requests as part of legitimate application functionality.