AWS S3 Rapid Bucket Posture API Calls from a Single Principal

Identifies when the same AWS principal, from the same source IP, successfully invokes read-only S3 control-plane APIs that reveal bucket posture across many buckets in a short period. This pattern can indicate automated reconnaissance or security scanning, similar to CSPM tools and post-compromise enumeration. The rule excludes AWS service principals, requires programmatic-style sessions (not Management Console credentials), and requires populated resource and identity fields so nulls do not skew cardinality.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/04/02"
  3integration = ["aws"]
  4maturity = "production"
  5min_stack_comments = "aws.cloudtrail.session_credential_from_console field introduced in AWS integration version 4.6.0"
  6min_stack_version = "9.2.0"
  7updated_date = "2026/04/10"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12Identifies when the same AWS principal, from the same source IP, successfully invokes read-only S3 control-plane APIs
 13that reveal bucket posture across many buckets in a short period. This pattern can indicate automated reconnaissance or
 14security scanning, similar to CSPM tools and post-compromise enumeration. The rule excludes AWS service principals,
 15requires programmatic-style sessions (not Management Console credentials), and requires populated resource and identity
 16fields so nulls do not skew cardinality.
 17"""
 18false_positives = [
 19    """
 20    Legitimate security scanners, CSPM products, compliance jobs, and inventory automation may call the same read-only
 21    bucket APIs across many buckets quickly. Verify the principal ARN, source IP, user agent, and schedule against known
 22    approved tooling before treating the activity as malicious.
 23    """,
 24]
 25from = "now-6m"
 26language = "esql"
 27license = "Elastic License v2"
 28name = "AWS S3 Rapid Bucket Posture API Calls from a Single Principal"
 29note = """## Triage and analysis
 30
 31### Investigating AWS S3 Rapid Bucket Posture API Calls from a Single Principal
 32
 33This rule detects when the same AWS principal (`aws.cloudtrail.user_identity.arn`), from the same `source.ip`, successfully invokes read-only S3 control-plane APIs that reveal bucket posture across more than 15 distinct `aws.cloudtrail.resources.arn` values within a 10-second window.
 34
 35Security scanners, compliance tools, and post-compromise reconnaissance often walk many buckets quickly to map public access, policies, and versioning. Bursts of distinct buckets in seconds are less typical of one-off console administration or single-bucket troubleshooting. This could be indicative of reconnaissance as seen by threat actors like Team PCP. 
 36
 37### Possible investigation steps
 38
 39**Identify the actor and session context**
 40- **Actor ARN (`aws.cloudtrail.user_identity.arn`)**: Determine which IAM user, role, or federated principal performed the reads. Confirm whether this identity is approved for broad S3 read or security auditing. Unusual types or unfamiliar roles may warrant deeper review.
 41- **Access key (`Esql.aws_cloudtrail_user_identity_access_key_id_values`)**: Identify which access key or temporary credential was used. Correlate with IAM last-used metadata for the key or role session.
 42
 43**Characterize the bucket sweep**
 44- **Distinct bucket count (`Esql.bucket_arn_count_distinct`)**: Compare to normal baselines for this identity; values at or just above the threshold may still warrant review for new automation.
 45- **Bucket ARNs (`Esql.aws_cloudtrail_resources_arn_values`)**: Identify which buckets were touched. Prioritize buckets that store logs, backups, credentials, or regulated data. Search the same time range for write or policy-change APIs (`PutBucket*`, `DeleteBucket*`) on the same buckets.
 46
 47**Analyze source and client**
 48- **Source IP (`Esql.source_ip_values`)**: Map to corporate egress, a known runner or bastion, an EC2 instance, or an unfamiliar ASN. Compare with VPC Flow Logs or proxy logs when available.
 49- **User agent (`Esql.user_agent_original_values`, `Esql.user_agent_name_values`)**: Identify the AWS CLI, Boto3, a specific scanner, or custom scripts. Unusual or minimal user agents may align with tooling and require investigation.
 50
 51**Correlate in time**
 52- Query CloudTrail for the same `aws.cloudtrail.user_identity.arn` and `source.ip` within approximately ±30 minutes for follow-on patterns: `ListBuckets`, `GetObject`, `PutBucketPolicy`, `AssumeRole`, or IAM changes.
 53- Check for overlapping alerts related to credential access, unusual geolocation, or new external bucket policy grants.
 54
 55### False positive analysis
 56
 57Legitimate causes can include:
 58- **Security and compliance scanners** (for example CSPM or assessment tools) using API credentials with `s3:Get*` permissions across many buckets.
 59- **Inventory or backup catalog tools** that enumerate bucket metadata for reporting.
 60- **CI/CD or infrastructure-as-code validation** jobs that verify bucket settings across environments.
 61
 62Validate whether the principal is a documented service account, the IP belongs to known infrastructure, and the timing matches scheduled jobs. If behavior is expected, consider raising the distinct-bucket threshold, adding `user_agent` filters, or documenting exception identities.
 63
 64### Response and remediation
 65
 66**Contain**
 67- If activity is unexpected, rotate or disable keys for the affected identity, revoke active role sessions where possible, and restrict the source IP at the network layer if it is not authorized.
 68
 69**Investigate**
 70- Export CloudTrail for the window around `Esql.time_window_date_trunc` and review all S3 and IAM events for the same actor.
 71- Review IAM policies attached to the principal for excessive `s3:Get*` or `s3:List*` scope.
 72
 73**Harden**
 74- Enforce least privilege on S3 read APIs; use permission boundaries or service control policies where appropriate.
 75- Ensure sensitive buckets are not unnecessarily reachable from the observed network context.
 76- Document approved scanning accounts and tune the rule to reduce noise from those identities.
 77
 78### Additional information
 79
 80- [AWS Security Incident Response Guide](https://docs.aws.amazon.com/whitepapers/latest/aws-security-incident-response-guide/aws-security-incident-response-guide.pdf)
 81- [AWS Incident Response Playbooks](https://github.com/aws-samples/aws-incident-response-playbooks/)
 82- [AWS Customer Playbook Framework](https://github.com/aws-samples/aws-customer-playbook-framework)
 83
 84"""
 85references = [
 86    "https://kudelskisecurity.com/research/investigating-two-variants-of-the-trivy-supply-chain-compromise",
 87]
 88risk_score = 21
 89rule_id = "a7577205-88a1-4a08-85d4-7b72a9a2e969"
 90severity = "low"
 91tags = [
 92    "Domain: Cloud",
 93    "Data Source: AWS",
 94    "Data Source: Amazon Web Services",
 95    "Data Source: AWS S3",
 96    "Data Source: AWS CloudTrail",
 97    "Use Case: Threat Detection",
 98    "Tactic: Discovery",
 99    "Tactic: Collection",
100    "Resources: Investigation Guide",
101]
102timestamp_override = "event.ingested"
103type = "esql"
104
105query = '''
106from logs-aws.cloudtrail-* metadata _id, _version, _index
107| eval Esql.time_window_date_trunc = date_trunc(10 seconds, @timestamp)
108
109| where
110    data_stream.dataset == "aws.cloudtrail"
111    and event.provider == "s3.amazonaws.com"
112    and event.outcome == "success"
113    and event.action in (
114      "GetBucketAcl",
115      "GetBucketPublicAccessBlock",
116      "GetBucketPolicy",
117      "GetBucketPolicyStatus",
118      "GetBucketVersioning"
119    )
120    and aws.cloudtrail.user_identity.type != "AWSService"
121    and source.ip IS NOT NULL
122    and aws.cloudtrail.resources.arn IS NOT NULL
123    and aws.cloudtrail.user_identity.arn IS NOT NULL
124    and aws.cloudtrail.session_credential_from_console IS NULL
125
126| keep
127    @timestamp,
128    Esql.time_window_date_trunc,
129    event.action,
130    aws.cloudtrail.user_identity.arn,
131    aws.cloudtrail.user_identity.type,
132    aws.cloudtrail.user_identity.access_key_id,
133    source.ip,
134    aws.cloudtrail.resources.arn,
135    cloud.account.id,
136    cloud.region,
137    user_agent.original,
138    source.as.organization.name,
139    data_stream.namespace
140
141| stats
142    Esql.bucket_arn_count_distinct = count_distinct(aws.cloudtrail.resources.arn),
143    Esql.aws_cloudtrail_resources_arn_values = VALUES(aws.cloudtrail.resources.arn),
144    Esql.event_action_values = VALUES(event.action),
145    Esql.timestamp_values = VALUES(@timestamp),
146    Esql.aws_cloudtrail_user_identity_type_values = VALUES(aws.cloudtrail.user_identity.type),
147    Esql.aws_cloudtrail_user_identity_access_key_id_values = VALUES(aws.cloudtrail.user_identity.access_key_id),
148    Esql.cloud_account_id_values = VALUES(cloud.account.id),
149    Esql.cloud_region_values = VALUES(cloud.region),
150    Esql.user_agent_original_values = VALUES(user_agent.original),
151    Esql.source_as_organization_name_values = VALUES(source.as.organization.name),
152    Esql.data_stream_namespace_values = VALUES(data_stream.namespace)
153  by Esql.time_window_date_trunc, aws.cloudtrail.user_identity.arn, source.ip
154
155| where Esql.bucket_arn_count_distinct > 15
156'''
157
158
159[[rule.threat]]
160framework = "MITRE ATT&CK"
161[[rule.threat.technique]]
162id = "T1526"
163name = "Cloud Service Discovery"
164reference = "https://attack.mitre.org/techniques/T1526/"
165
166[[rule.threat.technique]]
167id = "T1580"
168name = "Cloud Infrastructure Discovery"
169reference = "https://attack.mitre.org/techniques/T1580/"
170
171[[rule.threat.technique]]
172id = "T1619"
173name = "Cloud Storage Object Discovery"
174reference = "https://attack.mitre.org/techniques/T1619/"
175
176
177[rule.threat.tactic]
178id = "TA0007"
179name = "Discovery"
180reference = "https://attack.mitre.org/tactics/TA0007/"
181[[rule.threat]]
182framework = "MITRE ATT&CK"
183[[rule.threat.technique]]
184id = "T1530"
185name = "Data from Cloud Storage"
186reference = "https://attack.mitre.org/techniques/T1530/"
187
188
189[rule.threat.tactic]
190id = "TA0009"
191name = "Collection"
192reference = "https://attack.mitre.org/tactics/TA0009/"
193
194[rule.investigation_fields]
195field_names = [
196    "Esql.bucket_arn_count_distinct",
197    "Esql.time_window_date_trunc",
198    "aws.cloudtrail.user_identity.arn",
199    "source.ip",
200    "Esql.aws_cloudtrail_resources_arn_values",
201    "Esql.event_action_values",
202    "Esql.aws_cloudtrail_user_identity_type_values",
203    "Esql.aws_cloudtrail_user_identity_access_key_id_values",
204    "Esql.cloud_account_id_values",
205    "Esql.cloud_region_values",
206    "Esql.user_agent_original_values",
207    "Esql.source_as_organization_name_values",
208    "Esql.data_stream_namespace_values",
209]

Triage and analysis

Investigating AWS S3 Rapid Bucket Posture API Calls from a Single Principal

This rule detects when the same AWS principal (aws.cloudtrail.user_identity.arn), from the same source.ip, successfully invokes read-only S3 control-plane APIs that reveal bucket posture across more than 15 distinct aws.cloudtrail.resources.arn values within a 10-second window.

Security scanners, compliance tools, and post-compromise reconnaissance often walk many buckets quickly to map public access, policies, and versioning. Bursts of distinct buckets in seconds are less typical of one-off console administration or single-bucket troubleshooting. This could be indicative of reconnaissance as seen by threat actors like Team PCP.

Possible investigation steps

Identify the actor and session context

  • Actor ARN (aws.cloudtrail.user_identity.arn): Determine which IAM user, role, or federated principal performed the reads. Confirm whether this identity is approved for broad S3 read or security auditing. Unusual types or unfamiliar roles may warrant deeper review.
  • Access key (Esql.aws_cloudtrail_user_identity_access_key_id_values): Identify which access key or temporary credential was used. Correlate with IAM last-used metadata for the key or role session.

Characterize the bucket sweep

  • Distinct bucket count (Esql.bucket_arn_count_distinct): Compare to normal baselines for this identity; values at or just above the threshold may still warrant review for new automation.
  • Bucket ARNs (Esql.aws_cloudtrail_resources_arn_values): Identify which buckets were touched. Prioritize buckets that store logs, backups, credentials, or regulated data. Search the same time range for write or policy-change APIs (PutBucket*, DeleteBucket*) on the same buckets.

Analyze source and client

  • Source IP (Esql.source_ip_values): Map to corporate egress, a known runner or bastion, an EC2 instance, or an unfamiliar ASN. Compare with VPC Flow Logs or proxy logs when available.
  • User agent (Esql.user_agent_original_values, Esql.user_agent_name_values): Identify the AWS CLI, Boto3, a specific scanner, or custom scripts. Unusual or minimal user agents may align with tooling and require investigation.

Correlate in time

  • Query CloudTrail for the same aws.cloudtrail.user_identity.arn and source.ip within approximately ±30 minutes for follow-on patterns: ListBuckets, GetObject, PutBucketPolicy, AssumeRole, or IAM changes.
  • Check for overlapping alerts related to credential access, unusual geolocation, or new external bucket policy grants.

False positive analysis

Legitimate causes can include:

  • Security and compliance scanners (for example CSPM or assessment tools) using API credentials with s3:Get* permissions across many buckets.
  • Inventory or backup catalog tools that enumerate bucket metadata for reporting.
  • CI/CD or infrastructure-as-code validation jobs that verify bucket settings across environments.

Validate whether the principal is a documented service account, the IP belongs to known infrastructure, and the timing matches scheduled jobs. If behavior is expected, consider raising the distinct-bucket threshold, adding user_agent filters, or documenting exception identities.

Response and remediation

Contain

  • If activity is unexpected, rotate or disable keys for the affected identity, revoke active role sessions where possible, and restrict the source IP at the network layer if it is not authorized.

Investigate

  • Export CloudTrail for the window around Esql.time_window_date_trunc and review all S3 and IAM events for the same actor.
  • Review IAM policies attached to the principal for excessive s3:Get* or s3:List* scope.

Harden

  • Enforce least privilege on S3 read APIs; use permission boundaries or service control policies where appropriate.
  • Ensure sensitive buckets are not unnecessarily reachable from the observed network context.
  • Document approved scanning accounts and tune the rule to reduce noise from those identities.

Additional information

References

Related rules

to-top