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.
Inspecting the HTTP headers indicates the server name is AmazonS3, hinting that the website might be hosted on an AWS S3 bucket.
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.
PRE admin/
PRE migration-files/
PRE shared/
PRE static/
2023-10-16 13:00:47 5347 index.html3.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.
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.
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.
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.
# 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.
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.
{
"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.
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.
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.
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.
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.
download: s3://dev.huge-logistics.com/migration-files/migrate_secrets.ps1 to ./migrate_secrets2.ps1
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.
# 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.
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: [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.
<?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.
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.
{
"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.
{
"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.
download: s3://dev.huge-logistics.com/admin/flag.txt to ./flag.txt