It’s a new beginning! Ermetic is now Tenable Cloud Security.

Exfiltrated, Signed, Delivered – What Can Go Wrong When an Amazon Elastic Compute Cloud (EC2) Instance is Exposed to SSRF 

New CNAPPgoat scenario makes experimentation easy by triggering calls to AWS service from an EC2 instance exposed to SSRF

Lior Zatlavi By Lior Zatlavi
Exfiltrated, Signed, Delivered – What Can Go Wrong When an Amazon Elastic Compute Cloud (EC2) Instance is Exposed to SSRF 

TL; DR: Using CNAPPgoat, you can now experiment with a technique that leverages exposure to SSRF to trigger calls to AWS services from within an Amazon EC2 instance. Check it out! 

Recap: What is CNAPPgoat?

CNAPPgoat is Tenable Cloud Security’s open-source contribution to the multicloud environment landscape. It is a vulnerable-by-design deployment tool tailored for defenders and pen testers. By allowing intentional vulnerabilities to be seeded across AWS, Azure and GCP, we aim to provide a realistic setting for practitioners to enhance their skills.

Protecting against Instance Credentials Exfiltration

As we’ve discussed on other occasions, AWS does much to mitigate the threat of credentials exfiltration from the metadata service of Amazon Elastic Compute Cloud (EC2) instances. AWS provides tools for responsibly enforcing use of IMDSv2, which is the considerably more secure metadata service API, and controls that prevent and detect use of these credentials from outside an instance.  

Recently, Stephen Bradshaw published a very interesting proof-of-concept tool about signing URLs. His post sheds light on a technique many are unaware of that can be a game-changer in the cat-and-mouse game of protecting the use of instance credentials. 

Just to be clear: What’s discussed in this post is not a review of an issue found within AWS’s implementation; rather, a technique that an attacker may use should an AWS customer mistakenly expose their instances to SSRF (which is within the customer’s part of the shared responsibility model). 

Don’t Impersonate, Manipulate 

What if we told you that a certain issue caused by software deployed on an Amazon EC2 instance may allow an attacker wielding credentials exfiltrated from the instance to trigger a call to an AWS service from the machine itself? That is, an issue that is not remote code execution (RCE) by nature, which would obviously allow this. 

Here’s the deal: A threat actor can use exfiltrated credentials from the instance to create a signed URL aimed at a certain AWS service and action. A customer may deploy software on an Amazon EC2 instance that would make it vulnerable to SSRF and allow the manipulation of the Amazon EC2 instance to make GET requests. By leveraging it, the threat actor can make the instance call to that signed URL, triggering the AWS service from the instance itself.  

Or, as Christophe Tafani-Dreeper has explained simply, the procedure goes like this: 

Stephen’s tool, aws_url_signer, is a cool (even if only initial) proof-of-concept for generating the signature using exfiltrated credentials and the desired command. 

Why Does This Matter? 

Prevention and detection mechanisms for mitigating usage of exfiltrated credentials won’t be able to mitigate actions triggered as described above because not only would they SEEM legitimate, they would also technically - from the point of view of the Amazon EC2 instance’s operation - BE legitimate (that is, called from within the instance). 

To mitigate this kind of activity, the issue itself would need to be detected and removed, or the call that leverages it would need to be detected/prevented from being triggered – for example, by using an effective input validation or implementation of a WAF. This is not an issue with the implementation of the IMDS operation of the Amazon EC2 service or any of the detection/prevention controls. Rather, it is a sophisticated way of leveraging an issue with deployed software – whose protection is, of course, as part of the shared responsibility model, the customer’s responsibility.  

So, for example, if we look at an example of a control (one we haven’t covered in past reviews), Amazon GuardDuty has alerts for usage of instance credentials exfiltrated from it whether used inside a different AWS account or from outside AWS. If the credentials are used in the method described above, the call would look like any legitimate call made from the Amazon EC2 instance so not be detected by these alerts – again, NOT Amazon GuardDuty’s fault. From Amazon GuardDuty’s point of view, the calls are being made from the instance itself. 

Another case where this can be relevant is if, as a preventative measure, you set up a data perimeter around a resource such as an S3 bucket by applying a resource-based policy that restricts access to it from a specific IP or even, via a VPC endpoint, VPC. Or, via an IAM policy or a SCP, you may set a restriction on the IAM role used by the Amazon EC2 instance using the new global condition keys released earlier this year: aws:Amazon EC2InstanceSourceVPC and aws:Amazon EC2InstanceSourcePrivateIPv4. If the call is made from the instance, these kinds of restrictions won’t limit it. 

This reality makes the exposure to SSRF – which, as mentioned before, is the customer’s responsibility – that much more dangerous. 

It can be essentially used as a cloaking device for using exfiltrated credentials. 

Cool and scary at the same time. 

Making the Easy Proof-of-Concept Even Easier 

At Tenable we strongly believe that the best way to learn is to implement and demonstrate. To be at the audience’s fingertips, a demonstration must be as easy to set up as possible. 

Even though Stephen’s tool is pretty approachable and easy, we wanted to make it even easier. 

For this reason, we’ve embedded it in a CNAPPgoat scenario that creates an end-to-end demonstration of the technique of using a signed URL to trigger an AWS service from within an Amazon EC2 instance. You can use this demonstration for educational purposes to make more people aware of the importance of secure application development by showing very clearly how far the “right” kind of issue can go. 

So, without further ado, let’s dive into the scenario. 

Scenario Architecture 

This CNAPPgoat scenario will run an Amazon EC2 instance with two components. 

The main component is of course an application vulnerable to SSRF which we built by revising an open source SSRF demo published by Seth Art. To make things more interesting, we’ve integrated into the application a version of parse-url which is actually exposed to a critical (9.4) CVE. This should make the scenario detectable by CNAPP solutions that include a cloud workload protection capability. The application is run on a Docker container and listens on port 80. 

The other component is a flask application serving Stephen’s signing tool that listens on port 5000. We’ve encapsulated it in a separate Docker container thinking it could, by itself, be useful in other scenarios. 

The scenario deploys both containers on an Amazon EC2 instance with IMDSv1 enabled and publicly accessible to the internet on ports 22 (should a user want to SSH into the machine for configuration/exploration), 2375 (to retrieve the Docker images) and, of course, 80 and 5000. 

The scenario deploys both containers on an Amazon EC2 instance with IMDSv1 enabled and publicly accessible to the internet on ports 22 (should a user want to SSH into the machine for configuration/exploration), 2375 (to retrieve the Docker images) and, of course, 80 and 5000.

 

Playbook 

As with any CNAPPgoat scenario, the first thing you need to do is provision it (and, as always, MAKE SURE YOU DO SO SAFELY and understand exactly what you’re deploying). 

So if you have CNAPPgoat and your AWS credentials set up you can then run:

cnappgoat provision cwpp-aws-vulnerable-container-parse-url-on-ec2

When the provisioning is done you can ask CNAPPgoat to show the scenario output using the following command: 

cnappgoat output cwpp-aws-vulnerable-container-parse-url-on-ec2

Among other things, the output should include the public IP address of the Amazon EC2 instance created: 

Please note that after deployment you may need to wait a few minutes for the Docker images to load, so please be patient if the site doesn’t load immediately. 

You can then easily access the application with your browser. The initial page loading will look like this: 

As the app loads, you will see clear, step–by-step instructions on how to leverage the SSRF it is exposed to by providing as input a URL that will be called in a GET request by the instance. 

The app will even show you how to bypass the installed version parse-url library to avoid detection of the URL and then how to make calls to retrieve the session credentials from the IMDS. You will reach a screen that looks something like this: 

The displayed information shows that we were able to retrieve the credentials from the instance’s metadata service.

We then set up everything for you to generate a signed url for a request to an AWS service. 

In a dedicated field you can specify a command describing the desired action (as specified in Stephen’s repository); by default, we automatically populate the text field with the parameters necessary to run get-caller-identity. 

You can then press the “Sign” button; doing so sends a request with those parameters and the credentials retrieved from the instance’s metadata service to the flask app serving aws_url_signer - to create the presigned URL. 

Once retrieved, the signed url is placed in the “URL Input” box so it can be sent to the instance to be called from. The upper part of the screen will look like this: 

Just to be clear: Once the credentials were retrieved, this step could have been done locally (or by running the aws_url_signer on a different machine) and did not require access to the instance itself. 

Once we press “Go” and send the signed URL to the instance to make the GET request we will get back its output: 

And that’s that! We’ve triggered the instance to perform a call to an AWS service without running code on the instance. 

Once you’re done, it is crucial, as always, to clean up properly and destroy the scenario: 

cnappgoat destroy cwpp-aws-vulnerable-container-parse-url-on-ec2

Conclusion 

The technique described here and demonstrated by Stephen’s tool is a great example of how a vulnerable application may be a beachhead to a cloud environment; specifically, by leveraging SSRF exposure to trigger calls to AWS services in a hard-to-detect manner – from within an Amazon EC2 instance. 

Our aim with the CNAPPgoat scenario presented in this post is to make your job of exploring and explaining the technique a lot easier. The scenario can be a great motivator for employing the mechanisms Amazon has in place to mitigate credentials exfiltration risk as well as employ secure software development techniques to avoid SSRF exposure. 

CNAPPgoat has many other scenarios you can explore and hopefully benefit from in a similar manner. 

If you have questions about this scenario, CNAPPgoat or cloud security in general, feel free to reach out. 

[email protected] 

Skip to content