Skip to content

flaws.cloud — Level 2

Vulnerability: S3 bucket readable by any authenticated AWS user (not just the owner's account)


The Vulnerability Explained

Level 1 was open to everyone — no account needed. Level 2 is subtler and more dangerous in practice.

The bucket ACL was set to allow "Any Authenticated AWS User". The developer probably thought this meant "anyone in my AWS account" — but it doesn't. It means anyone who has ANY AWS account at all — including your free-tier personal account, a penetration tester's account, or an attacker's account.

AWS has hundreds of millions of accounts. "Any authenticated AWS user" is functionally close to "everyone on the internet" — the only barrier is having created a free AWS account.


Step 1 — Set Up Your AWS Credentials

aws configure

This is an interactive wizard. It prompts you for:

AWS Access Key ID [None]: AKIAIOSFODNN7EXAMPLE
AWS Secret Access Key [None]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Default region name [None]: us-west-2
Default output format [None]: json

What it does: Writes your credentials to two files:

~/.aws/credentials   ← stores Access Key ID and Secret Access Key
~/.aws/config        ← stores region and output format

These become your default profile. Any aws command you run without --profile will use these automatically.

Where do you get keys? IAM → Users → your user → Security credentials → Create access key.


Step 2 — Set Up a Named Profile

aws configure --profile flaws

Same as above, but stores the credentials under the name flaws instead of as the default. Useful when you have multiple sets of credentials — your personal keys, stolen keys from a target, etc.

Result in ~/.aws/credentials:

[default]
aws_access_key_id = YOURKEYHERE
aws_secret_access_key = YOURSECRETHERE

[flaws]
aws_access_key_id = FLAWSKEYHERE
aws_secret_access_key = FLAWSSECRETHERE


Step 3 — Verify Your Identity

aws sts get-caller-identity

STS = Security Token Service. This command asks AWS "who am I authenticated as right now?"

Example output:

{
    "UserId": "AIDAIOSFODNN7EXAMPLE",
    "Account": "123456789012",
    "Arn": "arn:aws:iam::123456789012:user/mark"
}

Field What it means
UserId Unique ID of the IAM identity
Account Your AWS account number (12-digit)
Arn Amazon Resource Name — the full unique identifier: arn:aws:<service>:<region>:<account>:<resource>

When to run this: Always run it first after setting up new credentials. Confirms the keys work, and tells you exactly what account/user you're operating as. Critical when using stolen/found credentials — you need to know whose account you've got.


Step 4 — Access the Level 2 Bucket

aws s3 ls s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud

No --no-sign-request needed — and no --profile needed either if this is run with your default credentials.

Why does this work? Your AWS account is authenticated, and the bucket ACL grants read access to all authenticated AWS users. Your default credentials satisfy that requirement.

aws configure --profile flaws
aws s3 ls s3://level2-c8b217a33fcf1f839f6f1f73a00a9ae7.flaws.cloud --profile flaws

Same thing but explicitly using the flaws profile — equivalent result, just demonstrating profile usage.


How AWS Credential Resolution Works (Priority Order)

When you run an aws command, the CLI looks for credentials in this order — first match wins:

  1. CLI flags (--profile name or explicit --access-key)
  2. Environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
  3. AWS config/credentials files (~/.aws/credentials — default profile)
  4. IAM instance profile (if running on an EC2 — the metadata service at 169.254.169.254)
  5. Container credentials (if running in ECS/EKS)

Practical implication: If you export AWS_ACCESS_KEY_ID=... in your terminal, those env vars override whatever's in ~/.aws/credentials — even if you have a default profile configured. This trips people up.


EC2 Metadata Service — Bonus Attack Surface

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/

169.254.169.254 is a link-local address — only accessible from within an EC2 instance itself, not from the internet. It's AWS's internal metadata service that tells the instance about itself.

If the EC2 has an IAM role attached, this endpoint will return temporary credentials:

curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
Returns:
{
    "AccessKeyId": "ASIAIOSFODNN7EXAMPLE",
    "SecretAccessKey": "wJalrXUtn...",
    "Token": "AQoXnyc4lcK4...",
    "Expiration": "2026-03-07T00:00:00Z"
}

These are temporary credentials (note Token field — that's the session token required with temp creds). They expire, but if you grab them during an SSRF attack or from a compromised instance, you have real AWS access for hours.

SSRF → Metadata = classic cloud attack chain. We'll see this more in later levels.


Vulnerability Summary

The flaw: S3 bucket ACL set to "Any Authenticated AWS User" instead of being restricted to specific accounts or IAM principals.

The misconception: Developer thought "authenticated" meant "authenticated to my account." It doesn't — it means authenticated to AWS as a whole.

The fix: Never use "Any Authenticated AWS User" as a permission. Always grant access to specific IAM ARNs, roles, or accounts. Use bucket policies for this, not legacy ACLs.

Why this matters: The attacker just needs a free AWS account — the same as signing up for Gmail. That's essentially a public bucket.