Exploiting Server Side Request Forgery on a Node/Express Application (hosted on Amazon EC2)

I recently came across a Server Side Request Forgery (SSRF) vulnerability within an application that I assessed.  The application was hosted on Amazon EC2 and was using Node.js, Express.js, and as I found out later, Needle.js.

Discovery

 

Manual Discovery

In the discovery phase, I noticed a function of the application that was taking a user specified URL and displaying the first paragraph from that URL into the page.  This application allowed a user to share a URL with their friends, and grabbing the first paragraph was a feature that would provide the friends with more context.

The thing is, when looking at my Burp history, I could not find the request to the URL that I specified in my logs.  This should raise an eyebrow!  This means that the server is taking the URL I specified, making a request on my behalf, and then returning the result to me. That right there is SSRF.  Then, the only question was: What is the risk?

Automated Discovery

Since April 2015, if you are using the Burp Collaborator (and you definitely should be), you should be able to detect SSRF if you send the vulnerable request to the active scanner.  The following image shows a few different ways Burp Collaborator can identify SSRF (as Out-of-band resource load and External service interaction).


 

Exploitation Demonstration

I wanted to demonstrate this SSRF vulnerability without sharing any details about the assessed application. To do this, I re-created the vulnerability by somehow hacking together my first Node.js application (and it actually worked).

Source: https://github.com/sethsec/Nodejs-SSRF-App

My application, which for this demo is hosted on an Amazon EC2 micro instance, runs Node.js, and uses Express.js and Needle.js (which is what makes the SSRF request).

Just as the real application did, my demo application takes a URL specified by the user, and makes a request using Needle.js. The real application accepted the user supplied URL from a JSON parameter in the BODY, however my Node skills are not there yet, so for my demo the URL is sent via a GET parameter.

This is the most pertinent part of the vulnerable app:



This is what it looks like in action:





It looks just like an iframe, doesn't it?   But if it were a typical iframe, your browser would be making the request to sethsec.blogspot.com, and the application would not be vulnerable to SSRF.

However, when I ask the vulnerable server to make a request to ifconfig.pro, a site that shows the IP Address and User Agent of the requester, I can confirm the SSRF pretty clearly:



















The two most interesting items are the source IP and the User_Agent.  This response from ifconfig.pro proves that the request was sent by the EC2 instance, specifically from Needle, a Node.js HTTP client.

Who cares? What is the risk?  

Well, as mentioned above and discovered by many excellent researchers, if you can get the server to make a request for you, you can often gain access to things you otherwise would not have the ability to access.

Accessing the Amazon EC2 Metadata Service

For example, if your application is running on an Amazon EC2 instance, you can query the instance metadata service at 169.254.169.254 (a non-routable address).  This service is ONLY accessible via the instance itself, so without SSRF, command injection, or something similar, you would never be able to reach this service.



This is just one example of a metadata object.  Erik Peterson (@silvexis) covers much more sensitive things that can potentially live in the metadata service in his excellent talk Bringing a Machete to the Amazon. For example, this next request allows an attacker to retrieve the temporary security credentials for the "admins" role, which would allow an attacker to control access to your AWS resources.



Accessing the Amazon EC2 User Data Object

Another place you will want to look is the user-data container, located at: http://169.254.169.254/latest/user-data.  Amazon gives the following warning to devs:

http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-add-user-data

But as we all know, if it is easier to store some passwords or other sensitive data in user-data, some people will, which is why you should check.  This is what that request looks like from my vulnerable EC2 instance:


For a complete list of what to look for if you have access to the EC2 metadata service, check out this document from Amazon: Instance Metadata and User Data.

Scanning and Accessing the Back End Infrastructure

In addition to checking the metadata service (and also looking for user data), you should try to exploit SSRF to look for services, hosts, and resources that are accessible via the vulnerable server, but not accessible to you directly. Burp Intruder is a great tool to accomplish each of these tasks.

 

Demo Setup

To set up for the demo, I am using a second EC2 instance running Kali, with an internal IP address of 172.31.40.122 and listening on port 8080.  The web service on this host is not accessible from the Internet. In fact, the only connections allowed to 172.31.40.122 are from the internal IP address of the server that is vulnerable to SSRF, and only on port 8080/tcp.

Here is is the EC2 security policy for 172.31.40.122 (Kali running apache):



Scanning for ports (XSPA)


1) Make the initial request through Burp. In this example, I just attempted to access TCP port 1.



2) Send the initial request to Burp Intruder.



3) Set the payload position (to the port).



4) Set the payload itself. For the demo, I am selecting 11 sequential ports, but you could easily paste in the top X tcp ports from nmap or a list of common web server ports.



5) Start the attack.  As you can see from the screenshot below, there are a few potential ways to infer which port is open and which ports are closed.  In this case, you can use the response code OR the length of the response:



Alright! I just determined that 172.31.40.122 is listening on port 8080, and that it is running a web server.

 

Scanning for hosts

This follows the exact same steps as above, but instead of setting the port as the payload position, you would set the IP address range you want to scan, so that you are scanning a range of IP addresses for a particular port, like port 80/tcp or 443/tcp.

It would look like this:



I did not want to actually scan other EC2 IPs, so I'm just leaving this example here.  But, basically, you pick back up again at step 4 and everything else is the same as above.

You could also use the Cluster Bomb attack type in Burp and scan for ports and services at the same time.

 

Scanning the internal web server

Next, I'll show how we can once again repeat the process described above, but this time we'll scan for files and/or directories on a server, rather than scanning ports or hosts.

As this is my demo, I know the target file is located at users.txt, but I threw a few other pages into Burp Intruder to show what this would look like.   In a real world scenario, you would want to use a source of directories and file names from a resource like fuzzdb.

Just like in the previous examples, you can find a match by looking for a difference.  In this example, everything gives you a 200 status, but the length of users.txt is shorter than all of the others. This is your first clue that you found a file that exists.



As with the ports and services example from above, you could also use the Cluster Bomb attack type in Burp to scan multiple web servers you have identified with the same file/directory list, all at the same time.

Remediation

Rather than proxying requests on behalf of users, the application should have the user’s browser retrieve the desired information. If it is necessary to proxy the request, a white list should be used on the server side and the User-Agent information should be stripped or modified.


Additional Resources 

There are several great SSRF resources out there. In my opinion, Nicolas Grégoire (@Agarri_FR) is the master of SSRF (and XXE), so if you have not read up on  much about either, you need to check out some of his blog posts and talks.

One of my favorite talks: Nicolas Grégoire - Hunting for Top Bounties
One of my favorite blog posts: Compromising an unreachable Solr server with CVE-2013-6397

I decided to blog about this because I just submitted a SSRF finding as a pull request to Mubix's Common Findings Database Project (CFDB). That finding has some of the same content I included here. In the CFDB finding, I include a bunch of links to prior work as well as some useful resources. 

I think CFDB is a great project, and sorely needed at this point in our industry.  I urge anyone who can contribute to do so.   

Final Thoughts

Unlike client side vulnerabilities like XSS and CSRF, SSRF can potentially give you access to back end infrastructure that you would not otherwise have access to. Keep an eye out for it, and if you do find it, remember to demonstrate the risk.

Did I miss the mark on anything? Was I inaccurate? Was this post helpful to you? Feedback is welcome and encouraged!


Comments

Sandeep V said…
Thanks for sharing . Its really an awesome one.
avicoder said…
Nice take on NodeJS part.
Fox Pentester said…

And what can you do with External service interaction (DNS)? It would be interesting to know.

Popular posts from this blog

Exploiting Python Code Injection in Web Applications

Pentest Home Lab - 0x3 - Kerberoasting: Creating SPNs so you can roast them