Enumerate Secrets in AWS

Tips and tricks for discovering secrets in AWS

Secrets Enumeration

Plaintext or hard-coded secrets can be found in many resources throughout AWS if there is poor hygiene in the management of secrets.


CloudFormation

  • CloudFormation is an AWS infrastructure-as-code solution written in JSON or YAML

# provides details of the stacks including parameter key/values
aws --region us-east-1 cloudformation describe-stacks

# view the source code for a specific stack
aws --region us-east-1 cloudformation get-template --stack-name execution | jq -r '.TemplateBody'

EC2 Instances

User-data

  • Secrets can end up hard-coded in EC2 user-data and can easily be discovered either with the action ec2:DescribeInstanceAttributeor by querying the IMDS service

AWS CLI

# using the aws cli 
aws --region us-west-2 ec2 describe-instance-attribute --attribute userData --instance-id i-0aa2215a0322ec638 | jq -r '.UserData.Value' | base64 --decode

# output
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials

IMDSv1

# querying IMDSv1 (code execution on the instance is required)
curl http://169.254.169.254/latest/user-data

# output 
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials

IMDSv2

  • Version 2 of IMDS requires obtaining a token first

# querying IMDSv2 (code execution on the instance is required)
TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"`

curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/user-data

# output 
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials

EC2 Launch Templates

  • EC2 Launch Templates allow for defining consistent configurations (Security Groups, IMDS, Instance types, User Data, etc.) for any EC2 that uses the template

  • So User Data can also be found in the templates

# find launch templates
aws --region us-west-2 ec2 describe-launch-templates

{
    "LaunchTemplates": [
        {
            "LaunchTemplateId": "lt-12k4n9f933873nkdn",
            "LaunchTemplateName": "my-ec2-template",
            "CreateTime": "2025-1-03T11:32:25+00:00",
            "CreatedBy": "arn:aws:iam::111111111111:dev_user",
            "DefaultVersionNumber": 1,
            "LatestVersionNumber": 1
        }
    ]
}
# view the launch template
aws --region us-west-2 ec2 describe-launch-template-versions --launch-template-id lt-12k4n9f933873nkdn --versions 1

{
    "LaunchTemplateVersions": [
        {
            "LaunchTemplateId": "lt-12k4n9f933873nkdn",
            "LaunchTemplateName": "my-ec2-template",
            "VersionNumber": 1,
            "VersionDescription": "my-ec2-template",
            "CreateTime": "2025-1-03T11:32:25+00:00",
            "CreatedBy": "arn:aws:iam::111111111111:dev_user",
            "DefaultVersion": true,
            "LaunchTemplateData": {
                "UserData": "ZWNobyAiYXdzX2FjY2Vzc19rZXlfaWQgPSBBS0lBWnh4eHh4eHh4eHgiID4+IH4vZWMyLXVzZXIvLmF3cy9jcmVkZW50aWFscwplY2hvICJhd3Nfc2VjcmV0X2FjY2Vzc19rZXkgPSBUMmE0U2pEbnFzUFV4eHh4eHh4IiA+PiB+L2VjMi11c2VyLy5hd3MvY3JlZGVudGlhbHM="
            }
}
# decode the user data
aws --region us-west-2 ec2 describe-launch-template-versions --launch-template-id lt-12k4n9f933873nkdn --versions 1 | jq -r '.LaunchTemplateVersions[].LaunchTemplateData.UserData' | base64 --decode

# output
echo "aws_access_key_id = AKIAZxxxxxxxxxx" >> ~/ec2-user/.aws/credentials
echo "aws_secret_access_key = T2a4SjDnqsPUxxxxxxx" >> ~/ec2-user/.aws/credentials

EBS Snapshots

  • EBS Snapshots are backups of EC2 instances and may have secrets and credentials stored on them

  • See also EBS Snapshots

# find snapshots in your account
aws --region us-west-2 ec2 describe-snapshots --owner-ids self

Lambda

Environment Variables

  • Environment variables are commonly used for storing data used by the lambda function

# list all functions and return their env vars
aws --region us-east-1 lambda list-functions | jq -r '.Functions[].Environment.Variables'

{
  "ACCESS_KEY_ID": "AKIA.....",
  "SECRET_ACCESS_KEY": "SetQ...."
}
{
  "RDS_HOST": "mydatabase.sflwnf3l22n3.us-east-1.rds.amazonaws.com",
  "RDS_PASSWORD": "superSecret",
  "RDS_USER": "root"
}

Functions

  • The code of the function may also contain information

# download a function's code
aws --region us-east-1 lambda get-function --function-name myfunction --query 'Code.Location' --output text | xargs curl -o lambda_func.zip

unzip lambda_func.zip

RDS Snapshots

  • RDS Snapshots are backups of RDS instances and may contain valuable data

  • See also RDS Snapshots

# find snapshots in your account
aws rds describe-db-snapshots --region us-west-2

Secrets Manager

# list secrets 
aws --region us-west-2 secretsmanager list-secrets                             
{
    "SecretList": [
        {
            "ARN": "arn:aws:secretsmanager:us-west-2:111111111111:secret:production_database_password-BOcLnM",
            "Name": "production_database_password",
            "LastChangedDate": "2025-01-03T18:43:58.802000-07:00",
            "Tags": [],
            "SecretVersionsToStages": {
                "2ad27539-26a6-40fc-b412-774e749e6d80": [
                    "AWSCURRENT"
                ]
            },
            "CreatedDate": "2025-01-03T18:43:58.491000-07:00"
        }
    ]
}
# get secret (can only decrypt if you have permission to the kms key)
aws --region us-west-2 secretsmanager get-secret-value --secret-id production_database_password

{
    "ARN": "arn:aws:secretsmanager:us-west-2:111111111111:secret:production_database_password-BOcLnM",
    "Name": "production_database_password",
    "VersionId": "2ad27539-26a6-40fc-b412-774e749e6d80",
    "SecretString": "{\"database_password\":\"l;3lj2l3nr23nrlnslfio3\"}",
    "VersionStages": [
        "AWSCURRENT"
    ],
    "CreatedDate": "2025-01-03T18:43:58.798000-07:00"
}

SSM

Documents

  • SSM Documents allow you to run automations and commands on EC2s and may contain sensitive data

  • See also SSM Documents

# find ssm documents in your account
aws ssm list-documents | jq -r '.DocumentIdentifiers[] | select(.Owner | contains("111111111111")) | (.Name)'

Parameters

  • You can reference Systems Manager parameters in your scripts, commands, SSM documents, and configuration and automation workflows

# list ssm parameters
aws --region us-west-2 ssm describe-parameters

{
    "Parameters": [
        {
            "Name": "uninstall_key",
            "ARN": "arn:aws:ssm:us-west-2:111111111111:parameter/uninstall_key",
            "Type": "SecureString",
            "KeyId": "alias/aws/ssm",
            "LastModifiedDate": "2025-01-03T18:36:21.900000-07:00",
            "LastModifiedUser": "arn:aws:iam::111111111111:user/dev_user",
            "Description": "key used to uninstall our EDR",
            "Version": 1,
            "Tier": "Standard",
            "Policies": [],
            "DataType": "text"
        }
    ]
}
# get parameter (can only decrypt if you have permission to the kms key)
aws --region us-west-2 ssm get-parameter --name uninstall_key --with-decryption 

{
    "Parameter": {
        "Name": "uninstall_key",
        "Type": "SecureString",
        "Value": "34n34n393nndg0sng4",
        "Version": 1,
        "LastModifiedDate": "2025-01-03T18:36:21.900000-07:00",
        "ARN": "arn:aws:ssm:us-west-2:111111111111:parameter/uninstall_key",
        "DataType": "text"
    }
}

Terraform State

  • While not an AWS resource, Terraform State files are commonly stored in AWS S3 buckets

  • If the data is not encrypted and/or you have access to decrypt the data, Terraform State files often contain secrets, credentials, and information about the cloud environment

# example of downloading terraform state from file 
aws s3 cp s3://myterraformstatebucket/prod/prod-account-a/terraform.tfstate .

# output state to json to see sensitive values
terraform show -json | jq                           
{
  "format_version": "1.0",
  "terraform_version": "1.5.7",
  "values": {
    "root_module": {
      "resources": [
        {
          "address": "aws_iam_access_key.admin",
          "mode": "managed",
          "type": "aws_iam_access_key",
          "name": "admin",
          "provider_name": "registry.terraform.io/hashicorp/aws",
          "schema_version": 0,
          "values": {
            "create_date": "2025-01-04T01:17:57Z",
            "encrypted_secret": null,
            "encrypted_ses_smtp_password_v4": null,
            "id": "AKIA[...]",
            "key_fingerprint": null,
            "pgp_key": null,
            "secret": "dfY0XUrA[...]",
            "ses_smtp_password_v4": "BBPzJ[...]",
            "status": "Active",
            "user": "admin"
          },
          "sensitive_values": {
            "secret": true,
            "ses_smtp_password_v4": true
          }
        }
      ]
    }
  }
} 

# you can also just read the state file since it's json
cat terraform.tfstate | jq

Last updated

Was this helpful?