Check for S3 Buckets Vulnerable to Shadow Resource Hijacking (Bucket Monopoly)
Overview
This check identifies S3 buckets with predictable names that could be hijacked by attackers before your organization uses them. Many AWS services automatically create buckets following specific naming patterns (like aws-glue-assets-<account-id>-<region>). If an attacker creates these buckets first, your services may unknowingly write sensitive data to attacker-controlled storage.
Risk
Severity: High
If this check fails, your organization faces several serious risks:
- Data leakage: Services may write sensitive data (logs, artifacts, configurations) to buckets owned by attackers
- Code injection: Attackers can place malicious scripts or configurations in pre-claimed buckets that your services later execute
- Denial of service: Legitimate bucket creation fails because the name is already taken
- Privilege escalation: Compromised artifacts could lead to broader access within your AWS environment
This attack is known as "Shadow Resource Hijacking" or "Bucket Monopoly" because attackers monopolize predictable bucket names before legitimate users need them.
Remediation Steps
Prerequisites
You need:
- AWS Console access with S3 permissions, OR
- AWS CLI configured with credentials that can create S3 buckets
AWS Console Method
- Review the Prowler finding to identify which bucket names are flagged as vulnerable
- Open the S3 Console at https://console.aws.amazon.com/s3/
- Click "Create bucket"
- Enter the exact bucket name from the Prowler finding (e.g.,
aws-glue-assets-123456789012-us-east-1) - Select the correct region that matches the finding
- Enable "Block all public access" (checked by default)
- Enable default encryption using SSE-S3 (AES-256)
- Click "Create bucket"
- Repeat for each flagged bucket name and region combination
Important: The bucket name must match exactly what the AWS service expects. Common patterns include:
| Service | Bucket Name Pattern |
|---|---|
| AWS Glue | aws-glue-assets-<account-id>-<region> |
| SageMaker | sagemaker-<region>-<account-id> |
| Athena | aws-athena-query-results-<account-id>-<region> |
| CloudTrail | aws-cloudtrail-logs-<account-id>-<region> |
AWS CLI (optional)
Pre-provision a Single Bucket
For us-east-1 (no location constraint needed):
aws s3api create-bucket \
--bucket aws-glue-assets-123456789012-us-east-1 \
--region us-east-1
For other regions, include the location constraint:
aws s3api create-bucket \
--bucket aws-glue-assets-123456789012-us-west-2 \
--region us-west-2 \
--create-bucket-configuration LocationConstraint=us-west-2
Apply Security Settings
After creating each bucket, apply security best practices:
# Block public access
aws s3api put-public-access-block \
--bucket aws-glue-assets-123456789012-us-east-1 \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" \
--region us-east-1
# Enable default encryption
aws s3api put-bucket-encryption \
--bucket aws-glue-assets-123456789012-us-east-1 \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"AES256"}}]}' \
--region us-east-1
Pre-provision Buckets Across Multiple Regions
Use this script to create buckets for common AWS services across all regions:
#!/bin/bash
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGIONS="us-east-1 us-east-2 us-west-1 us-west-2 eu-west-1 eu-central-1 ap-southeast-1 ap-northeast-1"
for REGION in $REGIONS; do
echo "Creating buckets in $REGION..."
# AWS Glue bucket
GLUE_BUCKET="aws-glue-assets-${ACCOUNT_ID}-${REGION}"
if [ "$REGION" = "us-east-1" ]; then
aws s3api create-bucket --bucket "$GLUE_BUCKET" --region "$REGION" 2>/dev/null
else
aws s3api create-bucket --bucket "$GLUE_BUCKET" --region "$REGION" \
--create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null
fi
# SageMaker bucket
SAGEMAKER_BUCKET="sagemaker-${REGION}-${ACCOUNT_ID}"
if [ "$REGION" = "us-east-1" ]; then
aws s3api create-bucket --bucket "$SAGEMAKER_BUCKET" --region "$REGION" 2>/dev/null
else
aws s3api create-bucket --bucket "$SAGEMAKER_BUCKET" --region "$REGION" \
--create-bucket-configuration LocationConstraint="$REGION" 2>/dev/null
fi
done
echo "Bucket pre-provisioning complete."
CloudFormation (optional)
Deploy this template in each region where you use AWS services that create predictable bucket names:
AWSTemplateFormatVersion: '2010-09-09'
Description: Pre-provision S3 buckets to prevent Shadow Resource hijacking
Parameters:
AccountId:
Type: String
Description: Your AWS Account ID
AllowedPattern: '[0-9]{12}'
Resources:
GlueAssetsBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'aws-glue-assets-${AccountId}-${AWS::Region}'
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
SageMakerBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub 'sagemaker-${AWS::Region}-${AccountId}'
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
Outputs:
GlueAssetsBucketName:
Description: Name of the pre-provisioned Glue assets bucket
Value: !Ref GlueAssetsBucket
SageMakerBucketName:
Description: Name of the pre-provisioned SageMaker bucket
Value: !Ref SageMakerBucket
Deploying the Template
aws cloudformation create-stack \
--stack-name shadow-resource-prevention \
--template-body file://template.yaml \
--parameters ParameterKey=AccountId,ParameterValue=123456789012 \
--region us-east-1
Deploy Across Multiple Regions with StackSets
For organization-wide protection, use CloudFormation StackSets to deploy across all accounts and regions:
aws cloudformation create-stack-set \
--stack-set-name shadow-resource-prevention \
--template-body file://template.yaml \
--permission-model SERVICE_MANAGED \
--auto-deployment Enabled=true,RetainStacksOnAccountRemoval=false
aws cloudformation create-stack-instances \
--stack-set-name shadow-resource-prevention \
--deployment-targets OrganizationalUnitIds=ou-xxxx-xxxxxxxx \
--regions us-east-1 us-east-2 us-west-1 us-west-2 eu-west-1 \
--parameter-overrides ParameterKey=AccountId,ParameterValue=123456789012
Terraform (optional)
This module pre-provisions S3 buckets across multiple regions to prevent shadow resource hijacking:
variable "account_id" {
description = "Your AWS Account ID"
type = string
validation {
condition = can(regex("^[0-9]{12}$", var.account_id))
error_message = "Account ID must be a 12-digit number."
}
}
variable "regions" {
description = "List of AWS regions to pre-provision buckets"
type = list(string)
default = ["us-east-1"]
}
resource "aws_s3_bucket" "glue_assets" {
for_each = toset(var.regions)
bucket = "aws-glue-assets-${var.account_id}-${each.value}"
tags = {
Purpose = "Shadow Resource Prevention"
Service = "AWS Glue"
}
}
resource "aws_s3_bucket_public_access_block" "glue_assets" {
for_each = aws_s3_bucket.glue_assets
bucket = each.value.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "glue_assets" {
for_each = aws_s3_bucket.glue_assets
bucket = each.value.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
resource "aws_s3_bucket" "sagemaker" {
for_each = toset(var.regions)
bucket = "sagemaker-${each.value}-${var.account_id}"
tags = {
Purpose = "Shadow Resource Prevention"
Service = "Amazon SageMaker"
}
}
resource "aws_s3_bucket_public_access_block" "sagemaker" {
for_each = aws_s3_bucket.sagemaker
bucket = each.value.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
resource "aws_s3_bucket_server_side_encryption_configuration" "sagemaker" {
for_each = aws_s3_bucket.sagemaker
bucket = each.value.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}
Usage
module "shadow_resource_prevention" {
source = "./modules/shadow-resource-prevention"
account_id = "123456789012"
regions = ["us-east-1", "us-west-2", "eu-west-1"]
}
Verification
After creating the buckets, verify your remediation:
- In the AWS Console: Navigate to S3 and confirm each bucket exists with your account as the owner
- Re-run Prowler: Execute the specific check to confirm the finding is resolved:
Verification commands
# List your buckets and verify ownership
aws s3api list-buckets --query "Buckets[?contains(Name, 'glue-assets') || contains(Name, 'sagemaker')]" --region us-east-1
# Verify bucket ownership (should show your account)
aws s3api get-bucket-acl --bucket aws-glue-assets-123456789012-us-east-1 --region us-east-1
# Re-run Prowler check
prowler aws --check s3_bucket_shadow_resource_vulnerability
Additional Resources
- AWS S3 Security Best Practices
- Bucket Monopoly Attack Research
- AWS Glue Default Bucket Names
- SageMaker Default Bucket
Notes
- Proactive defense: The best defense is to pre-create these buckets before you need them. Consider automating this as part of your account provisioning process.
- Region coverage: Create buckets in ALL regions you might use, not just your primary region. Attackers often target less-used regions.
- Service-specific patterns: Different AWS services use different naming patterns. Review the services you use and identify their bucket naming conventions.
- Monitoring: Set up CloudTrail logging for S3 bucket creation events to detect if someone attempts to create buckets matching your naming patterns.
- Existing compromised buckets: If you discover a bucket with a predictable name that you do not own, do NOT write data to it. Contact AWS Support and investigate potential data exposure.