AWS Common Issues - Part 2 | Faraday

AWS Common Issues – Part 2

September 6, 2022

Credentials on EC2 Instances

If you ever developed any web application with a certain degree of complexity you must have realized that at some point you also need to store credentials, tokens, secrets, and sensitive information on these applications.

In an AWS environment, these applications usually run in EC2 instances, which are virtual machines created by Amazon under a set of parameters we get to decide. In other cases, they could be running in containers, or have been created by Kubernetes, and this would open up a wide range of attacks that will be left for another white paper. For now, let’s analyze how to secure application secrets in EC2 instances.

Main Attack Vectors

First, let’s take a look at the main ways attackers use to access sensitive information in this context:

1- Root access: A root access to an AWS account implies unrestricted access to the information in it. The Root user in Linux systems grants the same level of privileges and access. If an attacker is Root, whether in Linux or AWS, then it is game over.

2 – Bad practices when storing sensitive information: Using public URLs, public objects in S3 buckets, or hardcoding credentials in the application source code all imply easy ways for attackers to access our data.

3 – Privilege escalation: Here lies every technique of privilege escalation mentioned in our last edition (“Top 5 security tips: AWS Cloud Infrastructure”), in the “Principle of least privilege” section. If an escalation technique is possible, any attacker with low privileges will have access to EC2 instances and will be able to read any information they contain.

4 – User-Data: AWS gives us the option of using the user-data field, available in the AWS metadata API, through the URL http://169.254.169.254/1.0/user-data. If we place secrets there, any application that opens a socket in that direction will have access to our sensitive information. This includes, of course, attackers exploiting an SSRF bug in the application, erroneous Firewall and/or reverse proxy configurations, and attackers with limited AWS access, but with the privilege to read EC2 metadata.

These are some of the possible attack vectors.

How to Avoid Them

How can we avoid them? There are several options available, but none of them is a golden solution: each has advantages and disadvantages and requires further analysis:

1. Using IAM roles.

2. Hardcoding the secret in the AMI image.

3. Hardcoding the secret in a public/authenticated URL.

4. Using private S3 objects and providing the URL yourself.

5. Sending secrets in the instance’s deployment time.

6. Sending the secret through user-data.

7. Using AWS Secrets Manager.

8. Using an identity-based secret and encryption management system.

As we can see, there are multiple options. Each of them is useful for a specific context. However, it would be nice to have some general solutions. In this article, we’ll see solutions we may apply in most cases with the least attack vectors possible. With this concept in mind, we will provide two technical recommendations that can be used to share secrets in EC2 instances.

1. Sharing “secrets” or accesses to AWS services: AWS IAM Roles

If we need to provide access to the whole range of services Amazon AWS provides, the best option in terms of security is -without a doubt- to use the Roles system that IAM offers.

Instead of sending or storing Access Key and Secrets Key that will later be used by SDKs or CLI-mode interfaces to access and operate with the services we want, we have the option of enabling temporary credentials. They change after a couple of minutes and can be used by SDKs in a native way or through CLI interfaces, without the need of exposing a specific user’s permanent credentials or access keys.

The advantages of this technique are:

If you’d like to know how to implement IAM roles in EC2 instances at a technical level, check out these links:

2. Sharing non-native AWS secrets: AWS Secrets Manager

Let’s talk about what happens if we need to provide our EC2 instances with secrets that are not AWS-native, such as:

Usually, when we develop an application that needs information from a database or an API, credentials or secrets are hard-coded in order to access that information in the application itself, exposing the source code to potential leaks, revealing the secrets and obstructing their rotation. In order to rotate the secret, we would need to update it in every used instance in our app. Because of this, the secret rotation practice usually found in audited applications is very low or practically zero.

By using AWS Secrets Manager, we can store and safely supply these secrets in our EC2 instances or any script/application that requires it by using an API call to Secrets Manager in order to obtain that secret in a programmatic way.

In addition to keeping our code free from secrets, it automatically helps us by rotating them according to a set of rules we may define, reducing the risks of compromised information.

One of the best features of this solution is its integration with AWS Identity and Access Management (IAM), which allows us to attach permission policies to users, groups, and roles in order to allow/deny access to specific secrets. This way, we can have precise and granular control on who has access to what, reducing the surface exposure to attacks and/or leaks.

If you’d like to know how to implement Secrets Manager in EC2 instances at a technical level, check out these links: