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.arnstored inuser_idand correlate with thesource.ipsstored inip_listandunique_ipscount to determine how widely the token was used. - Correlate Additional Detection Context: Examine
activity_typeandfidelity_scoreto determine additional cities, networks or user agents associated with the token usage. - Determine Access Key Type: Examine the
access_key_idto 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
- AWS IAM Long-Term Access Key Correlated with Elevated Detection Alerts
- AWS IAM Virtual MFA Device Registration Attempt with Session Token
- AWS Bedrock Foundation Model Enumeration Followed by Invocation via Long-Term Key
- AWS Credentials Used from GitHub Actions and Non-CI/CD Infrastructure
- AWS IAM Long-Term Access Key First Seen from Source IP