Skip to main content

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

  1. Review the Prowler finding to identify which bucket names are flagged as vulnerable
  2. Open the S3 Console at https://console.aws.amazon.com/s3/
  3. Click "Create bucket"
  4. Enter the exact bucket name from the Prowler finding (e.g., aws-glue-assets-123456789012-us-east-1)
  5. Select the correct region that matches the finding
  6. Enable "Block all public access" (checked by default)
  7. Enable default encryption using SSE-S3 (AES-256)
  8. Click "Create bucket"
  9. Repeat for each flagged bucket name and region combination

Important: The bucket name must match exactly what the AWS service expects. Common patterns include:

ServiceBucket Name Pattern
AWS Glueaws-glue-assets-<account-id>-<region>
SageMakersagemaker-<region>-<account-id>
Athenaaws-athena-query-results-<account-id>-<region>
CloudTrailaws-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:

  1. In the AWS Console: Navigate to S3 and confirm each bucket exists with your account as the owner
  2. 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

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.