Skip to content
← Rule library
high KQL AWS

S3 Bucket Policy Opened to Public

Public-ACL or wildcard-principal changes on a bucket. Usually a misconfiguration, occasionally staged exfiltration.

Rule source

s3-bucket-made-public.kql
// S3 Bucket Policy Opened to Public
// T1530 - Data from Cloud Storage
// Usually a misconfiguration, occasionally staged exfiltration.
// Either way you want it in minutes, not at the next audit.

let approved_public_buckets = dynamic([
    "detectionops-public-assets",
    "detectionops-docs-site"
]);

AWSCloudTrail
| where EventName in ("PutBucketPolicy", "PutBucketAcl", "DeletePublicAccessBlock")
| where isempty(ErrorCode)
| extend Params     = parse_json(RequestParameters)
| extend BucketName = tostring(Params.bucketName)
| extend PolicyText = tostring(Params.bucketPolicy)
| where PolicyText has_any ('"Principal":"*"', '"AWS":"*"')
     or tostring(Params.x_amz_acl) has_any ("public-read", "public-read-write")
     or EventName == "DeletePublicAccessBlock"
| where BucketName !in (approved_public_buckets)
| project
    TimeGenerated,
    EventName,
    BucketName,
    Actor = UserIdentityArn,
    SourceIpAddress,
    AWSRegion
| order by TimeGenerated desc

Required telemetry

AWS CloudTrail S3 management events

Confirm this source is arriving before you judge the rule. A detection built on telemetry you do not collect looks identical to one that simply never fires.

Known false positives

  • Intentional static-website buckets - maintain an allowlist by name
  • CDN origin configuration during initial setup

Validation

Create and immediately revert a public bucket in the sandbox account

Re-run quarterly. Rules decay because the environment moves underneath them - agent upgrades, schema changes and config edits all break detections silently.