Stop Using Predictable Bucket Names: A Failed Attempt at Hacking Satellites
In the evolution of cloud infrastructure, leveraging instance metadata and cloud APIs as part of your Infrastructure as Code(IaC) has been a revolutionizing way of allowing you to create DRY infrastructure you can deploy anywhere with contextualization. For instance, lets say you wanted a package mirror on S3 that has a bucket in every region you currently operate in. You can simply leverage the instance metadata to retrieve the current region you are in, allowing you to deploy the following terraform in any region you desire. Fantastic! Not so fast.
Services like S3 having a global namespace, anyone can create a bucket called securityrunners-packages-region so long as it has not already been created. So what would happen if an attacker created securityrunners-packages-us-west-2 and an engineer decided to start exploring deploying resources in that region. Well now an attacker can arbitrarily execute code on your systems. This is referred to as S3 Bucket Namesquatting. There have also been related research from Bucket Monopoly: Breaching AWS Accounts Through Shadow Resources and AWS CDK Risk: Exploiting a Missing S3 Bucket Allowed Account Takeover.
After spending about a week looking into AWS service buckets, I was unable to find an exploitable scenario in which AWS services depend on buckets that do not exist yet. So you might be asking, why did I write this up if I have no new interesting research? Well I have thoroughly been enjoying Bluesky and it's rapid adoption of new users. I stumbled across Nick Frichette's post regarding the highest region number he found and I decided to share what I found in this research. So here I am. Another motivation for me doing so is that even after this became a well known issue, I still find myself making this mistake over and over. I am confident this is an industry wide issue that should be brought to everyones' attention, so please check your buckets for region names and read through this post. You will not regret it.
Hunting For Region Buckets
A few months back I stumbled across a litany of publicly listable service buckets and quickly realized most of them are created in every region AWS owns. Since some of the assets in the bucket predate my AWS usage prior to 2014, I stumbled across a few buckets that no longer exist hosting scripts. While that research was not fruitful, I decided to get a list of every possible service bucket to check to see if bucket namesquatting was possible on service buckets as they often hosted scripts.
What helped fuel this research was me deciding to create a tool to scrape all AWS documentation for retrieval augmented generation(RAG) for AI systems over a month ago. I leveraged a tool I opened source called awsdocs features in my blog AI-Assisted Security Research to help do the searching for me. To get a comprehensive list all I had to do was leverage ripgrep and a few different regexes and list out as many buckets as I could find.
rg "<code class=\"replaceable\">region</code>" .
Digging Deeper
So now I have a comprehensive list of all the documented service buckets that include a region in their name, what do I do next. See which of these buckets contain scripts and then check to see if I can find a region in which a bucket does not exist. So let's find some buckets with predictable names containing scripts leveraged by customers/service.
Region Specific Bucket Artifacts
So after a bit of digging around, there were quite a few service buckets that contained scripts/packages for customers or the service itself to install. So now all I have to do start digging around and hope for the best! Here was a preliminary list of services I was able to find that would be potentially concerning.
- s3://amazon-ssm-{{REGION}}/some text
- latest/linux_amd64/amazon-ssm-agent.rpm
- SSM Installation Documentation
- s3://groundstation-wb-digif-software-{{REGION}}some text
- aws-groundstation-agent/1.0.2677.0/amazon_linux_2_x86_64/aws-groundstation-agent.rpm
- Install the AWS Groundstation agent
- Hacking satellite communications would be cool
- s3://aws-glue-sparkui-prod-{{REGION}}some text
- /public/misc/glue-4_0/setup.py
- Region specific CloudFormation stackssome text
- s3://amazon-ams-{{REGION}}some text
- /latest/linux/prepare_instance_for_ami_and_shutdown.sh
- Management Advanced EC2 Instance Stack Resize
- s3://amazon-sagemaker-operator-for-k8s-{{REGION}}some text
- /kubectl-smlogs-plugin/v1/linux.amd64.tar.gz
- Kubernetes SageMaker Operators End of Support
- s3://amazon-ssm-packages-{{REGION}}some text
- /Packages/AwsEnaNetworkDriver/windows/amd64/1.0.8/AwsEnaNetworkDriver.zip
- SSM Agent Technical Details
- s3://aws-application-migration-service-{{REGION}}some text
- /latest/windows/AwsReplicationWindowsInstaller.exe
- The same is also true with the buckets containing the hashes giving users a false sense of securitysome text
- s3://aws-application-migration-service-hashes-{{REGION}}
- AWS Application Migration Service Agentless Migration
- s3://aws-glue-ml-data-quality-assets-{{REGION}}/some text
- /jars/aws-glue-ml-data-quality-etl.jar
- AWS service pulling jarssome text
Disappointment
So when I first started digging around, every single service specific bucket that I stumbled across already had every region covered. Well as a security researcher it's not uncommon to spend a week and find nothing. Though I decided look a little deeper. I knew I was missing a whole world of undocumented buckets I had stumbled upon when digging through those buckets I had found. Also is Amazon's internal guidance just pre-create every region bucket? What happens when there is a new region?
us-east-3?!?!?
When digging through some of the buckets and going through different regions, I accidentally fat fingered one and realized a bucket that should not exist called us-east-3. I decide to check other buckets and realized that wow us-east-3 is present on a few of the buckets. Perhaps I discovered a new region? Well I quickly came to the realization that no I did not stumble across a new region, just that the service team had created every possible bucket for every possible future regions. I also found inconsistencies in just how many buckets were created for different regions as some would stop at five or ten. Then I ended up stumbling across a bucket that went up to us-east-15. So I now hold the current world record for highest region discovered, which I have proudly taken from the previous record holder of ap-southeast-7 by Nick Frinchette.
Below is a list of all the buckets I found that include high region numbers. I only checked a few so maybe you will find more and beat my record? If so, let me know! Also you may perhaps spot some inconsistencies when looking at these.
- s3://aws-parallelcluster-{{REGION}}
- s3://aws-drs-clients-{{REGION}}
- s3://aws-elastic-disaster-recovery-{{REGION}}
- s3://aws-elastic-disaster-recovery-hashes-{{REGION}}
- s3://amazon-ssm-us-east-{{REGION}}
- s3://amazon-ssm-{{REGION}}
Hacking Satellites?
Clearly someone during the review process has advised service teams to create these buckets in advanced as there might be a risk that a future region gets announced and you wouldn't want an attacker to control the bucket that contains scripts for that region. So after seeing this was a clear trend internally, lets find buckets that aren't created for regions that do not yet exist. Then I stumbled across AWS Ground Station. AWS Ground Station allows you to control satellites and ingest data with a fully managed Ground Station. Not only that but you install the agent using scripts stored in a region specific bucket. AWS really has every service under the sun don't they? Well I quickly realized that it was not following the same trend as other services and realized us-west-3 was up for grabs. Sweet! Now I just need to wait for a new region to get released to demonstrate impact...
So after a bit of waiting, I eventually get the email along the lines of "we do not believe the behavior you describe in this report presents a security concern, rather, it is expected behavior." This is a fair response as there is no current impact for this issue and all depends on how they internally handle creating new regions. While this is purely a guess, I would imagine there are well over a hundred buckets with regions in their names that amazon uses to host internal/customer facing scripts/packages. It would not be impossible for amazon to have scripts as part of region creation that would alarm and have a comprehensive inventory of every 100+ service bucket. Though I have my doubts... Until then, I hope us-west-3 gets created and maybe an unlikely I told you so?
Unique Suffix
Another clear pattern I discovered as part of this research is AWS finally including random suffixes in service bucket names. Making them non-predictable and ensuring this does not occur in the future. Though this has the significant side effect of having to create a terraform map if you want your IaC to be dry, unfortunately security often comes at the expense of convenience.
Unverified Wall of Shame
These are all the service related buckets I was able to list easily that had predictable bucket names. While I unfortunately was not able to take over any of these buckets, it just shows how frequent this practice is in the industry and internally at AWS. Please note that I have barely gone through this list and these were just the buckets I checked in the laziest way imaginable. Please do not hesitate to continue my research as I am sure someone will stumble across something similar and perhaps more interesting as a result.
amazon-ams-{{REGION}}
amazon-sagemaker-operator-for-k8s-{{REGION}}
amazon-ssm-packages-{{REGION}}
amazon-ssm-{{REGION}}
ams-patch-templates-{{REGION}}
athena-examples-{{REGION}}
aws-application-migration-service-hashes-{{REGION}}
aws-application-migration-service-{{REGION}}
aws-drs-clients-{{REGION}}
aws-drs-internal-{{REGION}}
aws-elastic-disaster-recovery-hashes-{{REGION}}
aws-elastic-disaster-recovery-{{REGION}}
aws-genomics-static-{{REGION}}
aws-glue-ml-data-quality-assets-{{REGION}}
aws-glue-sparkui-prod-{{REGION}}
aws-mgn-clients-hashes-{{REGION}}
aws-mgn-clients-{{REGION}}
aws-mgn-internal-hashes-{{REGION}}
aws-mgn-internal-{{REGION}}
al2023-repos-{{REGION}}-de612dc2
amazon-ams-{{REGION}}
amazon-sagemaker-operator-for-k8s-{{REGION}}
amazon-ssm-packages-{{REGION}}
amazon-ssm-{{REGION}}
ams-patch-templates-{{REGION}}
athena-examples-{{REGION}}
aws-application-migration-service-hashes-{{REGION}}
aws-application-migration-service-{{REGION}}
aws-drs-clients-{{REGION}}
aws-drs-internal-{{REGION}}
aws-elastic-disaster-recovery-hashes-{{REGION}}
aws-elastic-disaster-recovery-{{REGION}}
aws-genomics-static-{{REGION}}
aws-glue-ml-data-quality-assets-{{REGION}}
aws-glue-sparkui-prod-{{REGION}}
aws-mgn-clients-hashes-{{REGION}}
aws-mgn-clients-{{REGION}}
aws-mgn-internal-hashes-{{REGION}}
aws-mgn-internal-{{REGION}}
aws-parallelcluster-{{REGION}}
aws-ssm-document-attachments-{{REGION}}
aws-supernova-marketplace-{{REGION}}-prod
aws-windows-downloads-{{REGION}}
braket-external-assets-preview-{{REGION}}
databrew-public-datasets-{{REGION}}
datapipeline-{{REGION}}
dcv-license.{{REGION}}
ec2imagebuilder-managed-resources-{{REGION}}-prod
ec2imagebuilder-toe-{{REGION}}-prod
elasticbeanstalk.{{REGION}}
emr-data-access-control-{{REGION}}
glue-s3-dq-bucket-{{REGION}}-results
greengrass-feedback-connector-data-{{REGION}}
groundstation-cloudformation-templates-{{REGION}}
groundstation-wb-digif-software-{{REGION}}
jumpstart-cache-prod-{{REGION}}
launchwizard-example-{{REGION}}
launchwizard-scripts-postconfig-{{REGION}}
launchwizard-scripts-preconfig-{{REGION}}
lex-botresourcegenerations-{{REGION}}-prod
m2-{{REGION}}
ml-transforms-public-datasets-{{REGION}}
nice-enginframe-download-{{REGION}}
opsworks-instance-agent-{{REGION}}
opsworks-instance-assets-{{REGION}}
patch-baseline-snapshot-{{REGION}}
proserve-blocks-datasets-{{REGION}}
research-engineering-studio-{{REGION}}
sagemaker-edge-release-store-{{REGION}}-linux-armv8
sagemaker-edge-release-store-{{REGION}}-linux-x64
sagemaker-edge-release-store-{{REGION}}-windows-x64
sagemaker-edge-release-store-{{REGION}}-windows-x86
sagemaker-sample-data-{{REGION}}
senate-apn-reports-{{REGION}}-prod
spacy-sagemaker-{{REGION}}-bucket
{{REGION}}-birdwatcher-prod
{{REGION}}.elasticmapreduce
{{REGION}}.elasticmapreduce.samples
{{REGION}}-greengrass-updatesamazon-ams-{{REGION}}
amazon-sagemaker-operator-for-k8s-{{REGION}}
amazon-ssm-packages-{{REGION}}
amazon-ssm-{{REGION}}
ams-patch-templates-{{REGION}}
athena-examples-{{REGION}}
aws-application-migration-service-hashes-{{REGION}}
aws-application-migration-service-{{REGION}}
aws-drs-clients-{{REGION}}
aws-drs-internal-{{REGION}}
aws-elastic-disaster-recovery-hashes-{{REGION}}
aws-elastic-disaster-recovery-{{REGION}}
aws-genomics-static-{{REGION}}
aws-glue-ml-data-quality-assets-{{REGION}}
aws-glue-sparkui-prod-{{REGION}}
aws-mgn-clients-hashes-{{REGION}}
aws-mgn-clients-{{REGION}}
aws-mgn-internal-hashes-{{REGION}}
aws-mgn-internal-{{REGION}}
aws-parallelcluster-{{REGION}}
aws-ssm-document-attachments-{{REGION}}
aws-supernova-marketplace-{{REGION}}-prod
aws-windows-downloads-{{REGION}}
braket-external-assets-preview-{{REGION}}
databrew-public-datasets-{{REGION}}
datapipeline-{{REGION}}
dcv-license.{{REGION}}
ec2imagebuilder-managed-resources-{{REGION}}-prod
ec2imagebuilder-toe-{{REGION}}-prod
elasticbeanstalk.{{REGION}}
emr-data-access-control-{{REGION}}
glue-s3-dq-bucket-{{REGION}}-results
greengrass-feedback-connector-data-{{REGION}}
groundstation-cloudformation-templates-{{REGION}}
groundstation-wb-digif-software-{{REGION}}
jumpstart-cache-prod-{{REGION}}
launchwizard-example-{{REGION}}
launchwizard-scripts-postconfig-{{REGION}}
launchwizard-scripts-preconfig-{{REGION}}
lex-botresourcegenerations-{{REGION}}-prod
m2-{{REGION}}
ml-transforms-public-datasets-{{REGION}}
nice-enginframe-download-{{REGION}}
opsworks-instance-agent-{{REGION}}
opsworks-instance-assets-{{REGION}}
patch-baseline-snapshot-{{REGION}}
proserve-blocks-datasets-{{REGION}}
research-engineering-studio-{{REGION}}
sagemaker-edge-release-store-{{REGION}}-linux-armv8
sagemaker-edge-release-store-{{REGION}}-linux-x64
sagemaker-edge-release-store-{{REGION}}-windows-x64
sagemaker-edge-release-store-{{REGION}}-windows-x86
sagemaker-sample-data-{{REGION}}
senate-apn-reports-{{REGION}}-prod
spacy-sagemaker-{{REGION}}-bucket
{{REGION}}-birdwatcher-prod
{{REGION}}.elasticmapreduce
{{REGION}}.elasticmapreduce.samples
{{REGION}}-greengrass-updates
aws-parallelcluster-{{REGION}}
aws-ssm-document-attachments-{{REGION}}
aws-supernova-marketplace-{{REGION}}-prod
aws-windows-downloads-{{REGION}}
braket-external-assets-preview-{{REGION}}
databrew-public-datasets-{{REGION}}
datapipeline-{{REGION}}
dcv-license.{{REGION}}
ec2imagebuilder-managed-resources-{{REGION}}-prod
ec2imagebuilder-toe-{{REGION}}-prod
elasticbeanstalk.{{REGION}}
emr-data-access-control-{{REGION}}
glue-s3-dq-bucket-{{REGION}}-results
greengrass-feedback-connector-data-{{REGION}}
groundstation-cloudformation-templates-{{REGION}}
groundstation-wb-digif-software-{{REGION}}
jumpstart-cache-prod-{{REGION}}
launchwizard-example-{{REGION}}
launchwizard-scripts-postconfig-{{REGION}}
launchwizard-scripts-preconfig-{{REGION}}
lex-botresourcegenerations-{{REGION}}-prod
m2-{{REGION}}
ml-transforms-public-datasets-{{REGION}}
nice-enginframe-download-{{REGION}}
opsworks-instance-agent-{{REGION}}
opsworks-instance-assets-{{REGION}}
patch-baseline-snapshot-{{REGION}}
proserve-blocks-datasets-{{REGION}}
research-engineering-studio-{{REGION}}
sagemaker-edge-release-store-{{REGION}}-linux-armv8
sagemaker-edge-release-store-{{REGION}}-linux-x64
sagemaker-edge-release-store-{{REGION}}-windows-x64
sagemaker-edge-release-store-{{REGION}}-windows-x86
sagemaker-sample-data-{{REGION}}
senate-apn-reports-{{REGION}}-prod
spacy-sagemaker-{{REGION}}-bucket
{{REGION}}-birdwatcher-prod
{{REGION}}.elasticmapreduce
{{REGION}}.elasticmapreduce.samples
{{REGION}}-greengrass-updates
Conclusion
Please spend the time looking through your company's buckets and see if you have any that are vulnerable to bucket namesquatting. Seriously. If you do, create an inventory of these buckets and ensure you have some controls in your IaC to prevent it from being deployed in regions you have not currently bootstrapped. In addition to that, it might be a good idea to reserve bucket names in advanced to prevent this kind of incident that can occur. For future buckets, try to avoid using non-random suffixes to your bucket names. I know that's a decade long habit all cloud engineers have, though it is for the best. Hopefully I have better prepared you for your next security review and you are slightly more aware of this issue moving forward.
While I was unable to bring anything new/novel to the table, hopefully you had an interesting read! Please do let me know if you were able to find any AWS assets greater than the current record of us-east-15 and don't hesitate to sign up for my newsletter if you feel this content is for you.