AWS Access Token Used from Multiple Addresses

This rule identifies potentially suspicious activity by detecting instances where a single IAM user's temporary session token is accessed from multiple IP addresses within a short time frame. Such behavior may suggest that an adversary has compromised temporary credentials and is utilizing them from various locations. To enhance detection accuracy and minimize false positives, the rule incorporates criteria that evaluate unique IP addresses, user agents, cities, and networks. These additional checks help distinguish between legitimate distributed access patterns and potential credential misuse. Detected activities are classified into different types based on the combination of unique indicators, with each classification assigned a fidelity score reflecting the likelihood of malicious behavior. High fidelity scores are given to patterns most indicative of threats, such as multiple unique IPs, networks, cities, and user agents. Medium and low fidelity scores correspond to less severe patterns, enabling security teams to effectively prioritize alerts.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2025/04/11"
  3integration = ["aws"]
  4maturity = "production"
  5min_stack_version = "9.3.0"
  6min_stack_comments = "Changing min stack to 9.3.0, the latest minimum supported version for 9.X releases."
  7updated_date = "2026/07/20"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12This rule identifies potentially suspicious activity by detecting instances where a single IAM user's temporary session
 13token is accessed from multiple IP addresses within a short time frame. Such behavior may suggest that an adversary has
 14compromised temporary credentials and is utilizing them from various locations. To enhance detection accuracy and
 15minimize false positives, the rule incorporates criteria that evaluate unique IP addresses, user agents, cities, and
 16networks. These additional checks help distinguish between legitimate distributed access patterns and potential
 17credential misuse. Detected activities are classified into different types based on the combination of unique
 18indicators, with each classification assigned a fidelity score reflecting the likelihood of malicious behavior. High
 19fidelity scores are given to patterns most indicative of threats, such as multiple unique IPs, networks, cities, and
 20user agents. Medium and low fidelity scores correspond to less severe patterns, enabling security teams to effectively
 21prioritize alerts.
 22"""
 23false_positives = [
 24    """
 25    Highly distributed environments (e.g., globally deployed automation or edge nodes) may cause a single IAM user to
 26    appear from multiple IPs. Review the geolocation, network context, and user agent patterns to rule out benign use.
 27    This rule automatically excludes console login sessions, reducing false positives from legitimate console-based access across VPN or network changes.
 28    """,
 29]
 30from = "now-32m"
 31interval = "15m"
 32language = "esql"
 33license = "Elastic License v2"
 34name = "AWS Access Token Used from Multiple Addresses"
 35note = """## Triage and analysis
 36
 37### Investigating AWS Access Token Used from Multiple Addresses
 38
 39Access tokens are bound to a single user. Usage from multiple IP addresses may indicate the token was stolen and used elsewhere. By correlating this with additional detection criteria like multiple user agents, different cities, and different networks, we can improve the fidelity of the rule and help to eliminate false positives associated with expected behavior, like dual-stack IPV4/IPV6 usage.
 40
 41### Possible investigation steps
 42
 43- **Identify the IAM User**: Examine the `aws.cloudtrail.user_identity.arn` stored in `user_id` and correlate with the `source.ips` stored in `ip_list` and `unique_ips` count to determine how widely the token was used.
 44- **Correlate Additional Detection Context**: Examine `activity_type` and `fidelity_score` to determine additional cities, networks or user agents associated with the token usage.
 45- **Determine Access Key Type**: Examine the `access_key_id` to determine whether the token is short-term (beginning with ASIA) or long-term (beginning with AKIA).
 46- **Check Recent MFA Events**: Determine whether the user recently enabled MFA, registered devices, or assumed a role using this token.
 47- **Review Workload Context**: Confirm whether the user was expected to be active across multiple cities, networks or user agent environments.
 48- **Trace Adversary Movement**: Pivot to related actions (e.g., `s3:ListBuckets`, `iam:ListUsers`, `sts:GetCallerIdentity`) to track further enumeration.
 49
 50### False positive analysis
 51
 52- Automation frameworks that rotate through multiple IPs or cloud functions with dynamic egress IPs may cause this alert to fire.
 53- Confirm geolocation and workload context before escalating.
 54
 55### Response and remediation
 56
 57- **Revoke the Token**: Disable or rotate the IAM credentials and invalidate the temporary session token.
 58- **Audit the Environment**: Look for signs of lateral movement or data access during the token's validity.
 59- **Strengthen Controls**: Require MFA for high-privilege actions, restrict access via policy conditions (e.g., IP range or device).
 60
 61### Additional information
 62
 63- [IAM Long-Term Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html)
 64- [STS Temporary Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp.html)
 65- [Using MFA with Temporary Credentials](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_enable-regions.html)
 66- [AWS Threat Detection Use Cases](https://docs.aws.amazon.com/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html)
 67"""
 68references = ["https://www.sygnia.co/blog/sygnia-investigation-bybit-hack/"]
 69risk_score = 47
 70rule_id = "0d92d30a-5f3e-4b71-bc3d-4a0c4914b7e0"
 71severity = "medium"
 72tags = [
 73    "Domain: Cloud",
 74    "Data Source: AWS",
 75    "Data Source: Amazon Web Services",
 76    "Data Source: AWS IAM",
 77    "Data Source: AWS CloudTrail",
 78    "Tactic: Initial Access",
 79    "Use Case: Identity and Access Audit",
 80    "Resources: Investigation Guide",
 81]
 82timestamp_override = "event.ingested"
 83type = "esql"
 84
 85query = '''
 86from logs-aws.cloudtrail* metadata _id, _version, _index
 87| where data_stream.dataset == "aws.cloudtrail"
 88  and aws.cloudtrail.user_identity.arn is not null
 89  and aws.cloudtrail.user_identity.type == "IAMUser"
 90  and source.ip is not null
 91  and aws.cloudtrail.user_identity.access_key_id is not null
 92  and not aws.cloudtrail.session_credential_from_console == "true"
 93  and not (
 94    user_agent.original like "*Terraform*" or
 95    user_agent.original like "*Ansible*" or
 96    user_agent.original like "*Pulumi*"
 97  )
 98  and `source.as.organization.name` != "AMAZON-AES"
 99  and not ((
100    `source.as.organization.name` == "AMAZON-02" and aws.cloudtrail.event_category == "Data"))
101  and event.provider not in (
102    "health.amazonaws.com", "monitoring.amazonaws.com", "notifications.amazonaws.com",
103    "ce.amazonaws.com", "cost-optimization-hub.amazonaws.com",
104    "servicecatalog-appregistry.amazonaws.com", "securityhub.amazonaws.com", 
105    "account.amazonaws.com", "budgets.amazonaws.com", "freetier.amazonaws.com", "support.amazonaws.com",
106    "support-console.amazonaws.com"
107  )
108
109| eval
110  Esql.time_window_date_trunc = date_trunc(30 minutes, @timestamp),
111  Esql.aws_cloudtrail_user_identity_arn = aws.cloudtrail.user_identity.arn,
112  Esql.aws_cloudtrail_user_identity_access_key_id = aws.cloudtrail.user_identity.access_key_id,
113  Esql.source_ip = source.ip,
114  Esql.user_agent_original = user_agent.original,
115  Esql.source_ip_string = to_string(source.ip),
116  Esql.source_ip_user_agent_pair = concat(Esql.source_ip_string, " - ", user_agent.original),
117  Esql.source_ip_city_pair = concat(Esql.source_ip_string, " - ", source.geo.city_name),
118  Esql.source_geo_city_name = source.geo.city_name,
119  Esql.source_network_org_name = `source.as.organization.name`,
120  Esql.source_ip_network_pair = concat(Esql.source_ip_string, "-", `source.as.organization.name`),
121  Esql.event_timestamp = @timestamp,
122  Esql.data_stream_namespace = data_stream.namespace
123
124| stats
125  Esql.event_action_values = values(event.action),
126  Esql.event_provider_count_distinct = count_distinct(event.provider),
127  Esql.event_provider_values = values(event.provider),
128  Esql.aws_cloudtrail_user_identity_access_key_id_values = values(Esql.aws_cloudtrail_user_identity_access_key_id),
129  Esql.aws_cloudtrail_user_identity_arn_values = values(Esql.aws_cloudtrail_user_identity_arn),
130  Esql.source_ip_values = values(Esql.source_ip),
131  Esql.user_agent_original_values = values(Esql.user_agent_original),
132  Esql.source_ip_user_agent_pair_values = values(Esql.source_ip_user_agent_pair),
133  Esql.source_geo_city_name_values = values(Esql.source_geo_city_name),
134  Esql.source_ip_city_pair_values = values(Esql.source_ip_city_pair),
135  Esql.source_network_org_name_values = values(Esql.source_network_org_name),
136  Esql.source_ip_network_pair_values = values(Esql.source_ip_network_pair),
137  Esql.source_ip_count_distinct = count_distinct(Esql.source_ip),
138  Esql.user_agent_original_count_distinct = count_distinct(Esql.user_agent_original),
139  Esql.source_geo_city_name_count_distinct = count_distinct(Esql.source_geo_city_name),
140  Esql.source_network_org_name_count_distinct = count_distinct(Esql.source_network_org_name),
141  Esql.data_stream_namespace_values = values(Esql.data_stream_namespace),
142  Esql.timestamp_first_seen = min(Esql.event_timestamp),
143  Esql.timestamp_last_seen = max(Esql.event_timestamp),
144  Esql.event_count = count()
145  by Esql.time_window_date_trunc, Esql.aws_cloudtrail_user_identity_access_key_id
146
147| eval
148  Esql.activity_type = case(
149    Esql.source_ip_count_distinct >= 2 and Esql.source_network_org_name_count_distinct >= 2 and Esql.source_geo_city_name_count_distinct >= 2 and Esql.user_agent_original_count_distinct >= 2, "multiple_ip_network_city_user_agent",
150    Esql.source_ip_count_distinct >= 2 and Esql.source_network_org_name_count_distinct >= 2 and Esql.source_geo_city_name_count_distinct >= 2, "multiple_ip_network_city",
151    Esql.source_ip_count_distinct >= 2 and Esql.source_geo_city_name_count_distinct >= 2, "multiple_ip_and_city",
152    Esql.source_ip_count_distinct >= 2 and Esql.source_network_org_name_count_distinct >= 2, "multiple_ip_and_network",
153    Esql.source_ip_count_distinct >= 2 and Esql.user_agent_original_count_distinct >= 2, "multiple_ip_and_user_agent",
154    "normal_activity"
155  ),
156  Esql.activity_fidelity_score = case(
157    Esql.activity_type == "multiple_ip_network_city_user_agent", "high",
158    Esql.activity_type == "multiple_ip_network_city", "high",
159    Esql.activity_type == "multiple_ip_and_city", "medium",
160    Esql.activity_type == "multiple_ip_and_network", "medium",
161    Esql.activity_type == "multiple_ip_and_user_agent", "low"
162  )
163
164| keep
165  Esql.time_window_date_trunc,
166  Esql.activity_type,
167  Esql.activity_fidelity_score,
168  Esql.event_count,
169  Esql.timestamp_first_seen,
170  Esql.timestamp_last_seen,
171  Esql.aws_cloudtrail_user_identity_arn_values,
172  Esql.aws_cloudtrail_user_identity_access_key_id_values,
173  Esql.event_provider_count_distinct,
174  Esql.event_action_values,
175  Esql.event_provider_values,
176  Esql.source_ip_values,
177  Esql.user_agent_original_values,
178  Esql.source_ip_user_agent_pair_values,
179  Esql.source_geo_city_name_values,
180  Esql.source_ip_city_pair_values,
181  Esql.source_network_org_name_values,
182  Esql.source_ip_network_pair_values,
183  Esql.source_ip_count_distinct,
184  Esql.user_agent_original_count_distinct,
185  Esql.source_geo_city_name_count_distinct,
186  Esql.source_network_org_name_count_distinct,
187  Esql.data_stream_namespace_values
188
189| where Esql.activity_fidelity_score == "high" and Esql.event_provider_count_distinct > 1
190
191// this rule only alerts for "high" fidelity cases, to broaden the rule scope to include all activity
192// change the final condition to 
193// | where Esql.activity_type != "normal_activity" and Esql.event_provider_count_distinct > 1
194
195'''
196
197[rule.investigation_fields]
198field_names = [
199      "Esql.timestamp_first_seen",
200      "Esql.timestamp_last_seen",
201      "Esql.activity_type",
202      "Esql.activity_fidelity_score",
203      "Esql.event_count",
204      "Esql.aws_cloudtrail_user_identity_arn_values",
205      "Esql.aws_cloudtrail_user_identity_access_key_id_values",
206      "Esql.event_action_values",
207      "Esql.event_provider_values",
208      "Esql.source_ip_values",
209      "Esql.user_agent_original_values",
210      "Esql.source_ip_user_agent_pair_values",
211      "Esql.source_geo_city_name_values",
212      "Esql.source_ip_city_pair_values",
213      "Esql.source_network_org_name_values",
214      "Esql.source_ip_network_pair_values",
215      "Esql.source_ip_count_distinct",
216      "Esql.user_agent_original_count_distinct",
217      "Esql.source_geo_city_name_count_distinct",
218      "Esql.source_network_org_name_count_distinct",
219      "Esql.data_stream_namespace_values"
220]
221
222
223[[rule.threat]]
224framework = "MITRE ATT&CK"
225
226[[rule.threat.technique]]
227id = "T1078"
228name = "Valid Accounts"
229reference = "https://attack.mitre.org/techniques/T1078/"
230
231[[rule.threat.technique.subtechnique]]
232id = "T1078.004"
233name = "Cloud Accounts"
234reference = "https://attack.mitre.org/techniques/T1078/004/"
235
236[rule.threat.tactic]
237id = "TA0001"
238name = "Initial Access"
239reference = "https://attack.mitre.org/tactics/TA0001/"
240
241[[rule.threat]]
242framework = "MITRE ATT&CK"
243
244[[rule.threat.technique]]
245id = "T1078"
246name = "Valid Accounts"
247reference = "https://attack.mitre.org/techniques/T1078/"
248
249[[rule.threat.technique.subtechnique]]
250id = "T1078.004"
251name = "Cloud Accounts"
252reference = "https://attack.mitre.org/techniques/T1078/004/"
253
254[rule.threat.tactic]
255id = "TA0005"
256name = "Defense Evasion"
257reference = "https://attack.mitre.org/tactics/TA0005/"

Triage and analysis

Investigating AWS Access Token Used from Multiple Addresses

Access tokens are bound to a single user. Usage from multiple IP addresses may indicate the token was stolen and used elsewhere. By correlating this with additional detection criteria like multiple user agents, different cities, and different networks, we can improve the fidelity of the rule and help to eliminate false positives associated with expected behavior, like dual-stack IPV4/IPV6 usage.

Possible investigation steps

  • Identify the IAM User: Examine the aws.cloudtrail.user_identity.arn stored in user_id and correlate with the source.ips stored in ip_list and unique_ips count to determine how widely the token was used.
  • Correlate Additional Detection Context: Examine activity_type and fidelity_score to determine additional cities, networks or user agents associated with the token usage.
  • Determine Access Key Type: Examine the access_key_id to determine whether the token is short-term (beginning with ASIA) or long-term (beginning with AKIA).
  • Check Recent MFA Events: Determine whether the user recently enabled MFA, registered devices, or assumed a role using this token.
  • Review Workload Context: Confirm whether the user was expected to be active across multiple cities, networks or user agent environments.
  • Trace Adversary Movement: Pivot to related actions (e.g., s3:ListBuckets, iam:ListUsers, sts:GetCallerIdentity) to track further enumeration.

False positive analysis

  • Automation frameworks that rotate through multiple IPs or cloud functions with dynamic egress IPs may cause this alert to fire.
  • Confirm geolocation and workload context before escalating.

Response and remediation

  • Revoke the Token: Disable or rotate the IAM credentials and invalidate the temporary session token.
  • Audit the Environment: Look for signs of lateral movement or data access during the token's validity.
  • Strengthen Controls: Require MFA for high-privilege actions, restrict access via policy conditions (e.g., IP range or device).

Additional information

References

Related rules

to-top