AWS STS GetCallerIdentity Bulk Account Enumeration

Identifies three or more distinct AWS principals calling STS "GetCallerIdentity" from the same source IP and autonomous system within a 10-minute window. "GetCallerIdentity" returns the account and identity behind a credential and requires no permissions, so a burst of different non-role identities from one external address is a common credential-validation and account-enumeration step. Assumed roles, Vault OIDC users, and calls from major cloud and carrier networks are excluded.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/11/02"
  3integration = ["aws"]
  4maturity = "production"
  5updated_date = "2026/11/02"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies three or more distinct AWS principals calling STS "GetCallerIdentity" from the same source IP and
 11autonomous system within a 10-minute window. "GetCallerIdentity" returns the account and identity behind a credential
 12and requires no permissions, so a burst of different non-role identities from one external address is a common
 13credential-validation and account-enumeration step. Assumed roles, Vault OIDC users, and calls from major cloud and
 14carrier networks are excluded.
 15"""
 16false_positives = [
 17    """
 18    A corporate NAT, jump host, or CI runner that is not in the excluded ASN list can send `GetCallerIdentity` for
 19    several IAM users from one IP. Confirm the source IP, user agent, and user list against approved automation
 20    before treating the activity as malicious.
 21    """,
 22]
 23from = "now-10m"
 24interval = "5m"
 25language = "esql"
 26license = "Elastic License v2"
 27name = "AWS STS GetCallerIdentity Bulk Account Enumeration"
 28note = """## Triage and analysis
 29
 30### Investigating AWS STS GetCallerIdentity Bulk Account Enumeration
 31
 32`GetCallerIdentity` returns the account and principal for the credentials that called it. No IAM permission is
 33required. This rule groups successful calls that are not `AssumedRole` by source IP and source ASN. It alerts when
 34that group contains at least three distinct `user.name` values. The rule runs every 5 minutes and looks back 10
 35minutes, so the threshold is the last 10 minutes of activity. Alerts for the same `source.ip` are suppressed for
 3624 hours.
 37
 38This complements **AWS STS GetCallerIdentity API Called for the First Time**, which alerts on the first call from
 39one identity. This rule alerts when several identities are checked from one external address.
 40
 41### Possible investigation steps
 42
 43- Review `source.ip`, `source.as.organization.name`, and `Esql.user_agent_original_values`. An AWS CLI, SDK, or
 44  unknown client from an unfamiliar ASN is higher priority than a known management host.
 45- Review `Esql.user_name_values` and `Esql.aws_cloudtrail_user_identity_arn_values`. Check whether the identities
 46  are unrelated IAM users, include privileged users, or share one access key.
 47- Compare `Esql.user_name_count_distinct` with `Esql.aws_cloudtrail_user_identity_arn_count_distinct`. A higher ARN
 48  count than user count can mean several keys or federation sessions for the same user names.
 49- Search CloudTrail for those identities in the surrounding hour for `AssumeRole`, `GetSessionToken`, IAM changes,
 50  and discovery calls such as `ListUsers`, `ListBuckets`, and `DescribeInstances`.
 51- Determine whether any of the credentials were used successfully after the identity check.
 52
 53### False positive analysis
 54
 55- Users named `vault-oidc-*` are excluded.
 56- `AssumedRole` sessions are excluded. IAM users, federated users, and the root user remain in scope.
 57- Calls whose source ASN is Amazon, Google, Microsoft, Verizon, or AT&T are excluded.
 58- Shared egress that is not in that ASN list can still reach three users. Exclude the source IP or user agent when
 59  the behavior is documented.
 60
 61### Response and remediation
 62
 63- If the activity is not authorized, disable the access keys for the listed users and revoke active sessions.
 64- Rotate credentials that were validated from the source IP.
 65- Review CloudTrail for follow-on use of those identities and contain any resources they touched.
 66"""
 67references = [
 68    "https://docs.aws.amazon.com/STS/latest/APIReference/API_GetCallerIdentity.html",
 69    "https://www.secureworks.com/research/detecting-the-use-of-stolen-aws-lambda-credentials",
 70    "https://detectioninthe.cloud/ttps/discovery/sts_get_caller_identity",
 71]
 72risk_score = 73
 73rule_id = "dad4a0c7-f529-4d3d-b4f0-a2c97b8f7dfa"
 74setup = """## Setup
 75
 76The AWS integration must collect CloudTrail management events into `logs-aws.cloudtrail-*`.
 77`GetCallerIdentity` is a management event logged by default. `source.as.organization.name` must be populated,
 78because events missing it are excluded.
 79"""
 80severity = "high"
 81tags = [
 82    "Domain: Cloud",
 83    "Data Source: AWS",
 84    "Data Source: Amazon Web Services",
 85    "Data Source: AWS CloudTrail",
 86    "Platform: AWS",
 87    "Service: AWS STS",
 88    "Use Case: Threat Detection",
 89    "Tactic: Discovery",
 90    "Resources: Investigation Guide",
 91    "Rule Type: ES|QL",
 92]
 93timestamp_override = "event.ingested"
 94type = "esql"
 95
 96query = '''
 97from logs-aws.cloudtrail-*
 98| where KQL("""data_stream.dataset: "aws.cloudtrail" and event.provider: "sts.amazonaws.com" and event.action: "GetCallerIdentity" and event.outcome: "success" and not aws.cloudtrail.user_identity.type: "AssumedRole" and source.as.organization.name: * and not user.name: vault-oidc-*""")
 99  and source.ip is not null
100  and not source.as.organization.name in (
101    "Amazon.com, Inc.",
102    "AMAZON-02",
103    "AMAZON-AES",
104    "Google LLC",
105    "Microsoft Corporation",
106    "Verizon Business",
107    "AT&T Enterprises, LLC"
108  )
109| stats
110    Esql.aws_cloudtrail_user_identity_arn_count_distinct = count_distinct(aws.cloudtrail.user_identity.arn),
111    Esql.user_name_count_distinct = count_distinct(user.name),
112    Esql.user_name_values = values(user.name),
113    Esql.user_agent_original_values = values(user_agent.original),
114    Esql.aws_cloudtrail_user_identity_arn_values = values(aws.cloudtrail.user_identity.arn)
115  by source.ip, source.as.organization.name
116| where Esql.user_name_count_distinct >= 3
117| keep
118    Esql.user_name_count_distinct,
119    Esql.aws_cloudtrail_user_identity_arn_count_distinct,
120    source.ip,
121    source.as.organization.name,
122    Esql.user_name_values,
123    Esql.user_agent_original_values,
124    Esql.aws_cloudtrail_user_identity_arn_values
125'''
126
127
128[[rule.threat]]
129framework = "MITRE ATT&CK"
130[[rule.threat.technique]]
131id = "T1087"
132name = "Account Discovery"
133reference = "https://attack.mitre.org/techniques/T1087/"
134[[rule.threat.technique.subtechnique]]
135id = "T1087.004"
136name = "Cloud Account"
137reference = "https://attack.mitre.org/techniques/T1087/004/"
138
139[[rule.threat.technique]]
140id = "T1033"
141name = "System Owner/User Discovery"
142reference = "https://attack.mitre.org/techniques/T1033/"
143
144
145[rule.threat.tactic]
146id = "TA0007"
147name = "Discovery"
148reference = "https://attack.mitre.org/tactics/TA0007/"
149
150[rule.investigation_fields]
151field_names = [
152    "Esql.user_name_count_distinct",
153    "Esql.aws_cloudtrail_user_identity_arn_count_distinct",
154    "source.ip",
155    "source.as.organization.name",
156    "Esql.user_name_values",
157    "Esql.user_agent_original_values",
158    "Esql.aws_cloudtrail_user_identity_arn_values",
159]
160
161[rule.alert_suppression]
162group_by = ["source.ip"]
163missing_fields_strategy = "suppress"
164
165[rule.alert_suppression.duration]
166unit = "h"
167value = 24

Triage and analysis

Investigating AWS STS GetCallerIdentity Bulk Account Enumeration

GetCallerIdentity returns the account and principal for the credentials that called it. No IAM permission is required. This rule groups successful calls that are not AssumedRole by source IP and source ASN. It alerts when that group contains at least three distinct user.name values. The rule runs every 5 minutes and looks back 10 minutes, so the threshold is the last 10 minutes of activity. Alerts for the same source.ip are suppressed for 24 hours.

This complements AWS STS GetCallerIdentity API Called for the First Time, which alerts on the first call from one identity. This rule alerts when several identities are checked from one external address.

Possible investigation steps

  • Review source.ip, source.as.organization.name, and Esql.user_agent_original_values. An AWS CLI, SDK, or unknown client from an unfamiliar ASN is higher priority than a known management host.
  • Review Esql.user_name_values and Esql.aws_cloudtrail_user_identity_arn_values. Check whether the identities are unrelated IAM users, include privileged users, or share one access key.
  • Compare Esql.user_name_count_distinct with Esql.aws_cloudtrail_user_identity_arn_count_distinct. A higher ARN count than user count can mean several keys or federation sessions for the same user names.
  • Search CloudTrail for those identities in the surrounding hour for AssumeRole, GetSessionToken, IAM changes, and discovery calls such as ListUsers, ListBuckets, and DescribeInstances.
  • Determine whether any of the credentials were used successfully after the identity check.

False positive analysis

  • Users named vault-oidc-* are excluded.
  • AssumedRole sessions are excluded. IAM users, federated users, and the root user remain in scope.
  • Calls whose source ASN is Amazon, Google, Microsoft, Verizon, or AT&T are excluded.
  • Shared egress that is not in that ASN list can still reach three users. Exclude the source IP or user agent when the behavior is documented.

Response and remediation

  • If the activity is not authorized, disable the access keys for the listed users and revoke active sessions.
  • Rotate credentials that were validated from the source IP.
  • Review CloudTrail for follow-on use of those identities and contain any resources they touched.

References

Related rules

to-top