AWS IAM Long-Term Access Key Correlated with Elevated Detection Alerts

Correlates open detection alerts that share the same long-term IAM access key ID ( prefix AKIA). It fires when the rule AWS Long-Term Access Key First Seen from Source IP (rule_id: 9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f) has triggered for that key and at least one other open alert for the same key is medium, high, or critical severity. This higher-order rule helps prioritize long-term key novelty when it co-occurs with elevated detections that may indicate post-compromise activity.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/04/06"
  3integration = ["aws"]
  4maturity = "production"
  5updated_date = "2026/04/06"
  6min_stack_comments = "New entity classification fields added: user.entity.id"
  7min_stack_version = "9.2.0"
  8
  9[rule]
 10author = ["Elastic"]
 11description = """
 12Correlates open detection alerts that share the same long-term IAM access key ID ( prefix AKIA). It fires when the rule
 13AWS Long-Term Access Key First Seen from Source IP (rule_id: 9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f) has triggered for
 14that key and at least one other open alert for the same key is medium, high, or critical severity. This higher-order
 15rule helps prioritize long-term key novelty when it co-occurs with elevated detections that may indicate post-compromise
 16activity.
 17"""
 18false_positives = [
 19    """
 20    The same automation identity may legitimately trigger a first-seen-IP alert and unrelated medium-or-higher findings
 21    in the same window (for example, a noisy compliance rule). Review sibling `kibana.alert.rule.name` values, rule
 22    tags, and CloudTrail context for the access key before escalating.
 23    """,
 24]
 25from = "now-6m"
 26language = "esql"
 27license = "Elastic License v2"
 28name = "AWS IAM Long-Term Access Key Correlated with Elevated Detection Alerts"
 29note = """## Triage and analysis
 30
 31### Investigating AWS IAM Long-Term Access Key Correlated with Elevated Detection Alerts
 32
 33This is a [higher-order](https://www.elastic.co/guide/en/security/current/rules-ui-create.html) rule. It evaluates **open** signals in `.alerts-security.*` grouped by
 34`aws.cloudtrail.user_identity.access_key_id`. A match requires:
 35
 361. At least one alert from **AWS Long-Term Access Key First Seen from Source IP** (`kibana.alert.rule.rule_id` **9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f**).
 372. At least one **different** alert on the **same access key** with **medium or higher** severity, implemented as
 38   `kibana.alert.risk_score >= 47` or `kibana.alert.severity` in `medium`, `high`, or `critical`.
 39
 40### Possible investigation steps
 41
 42- **`Esql.alert_count_long_term_key_new_ip_rule`**: Confirm the first-seen-IP rule contributed to the correlation.
 43- **`Esql.alert_count_other_elevated`**: Count of sibling alerts meeting the elevated threshold (excluding the long-term-key new-IP rule).
 44- **`Esql.kibana_alert_rule_name_values`** / **`Esql.kibana_alert_rule_id_values`**: Identify which rules fired; map tactics and whether activity looks like post-compromise follow-through.
 45- **`aws.cloudtrail.user_identity.access_key_id`**: Pivot in CloudTrail and IAM (last used, attached policies, user or root context).
 46- **`Esql.aws_cloudtrail_user_identity_arn_values`**, **`Esql.source_ip_values`**: Reconstruct session context across alerts.
 47
 48### False positive analysis
 49
 50- Overlapping scheduled jobs, penetration tests, or purple-team runs that reuse the same IAM access key across many detectors.
 51- Custom rules calibrated with **medium** severity but **low** risk scores still qualify via `kibana.alert.severity`.
 52
 53### Response and remediation
 54
 55- Treat as elevated priority versus the standalone first-seen-IP rule: rotate or disable the access key if abuse is suspected,
 56  scope CloudTrail for the key, and review open sibling alerts to closure.
 57
 58### Additional information
 59
 60- Default risk score to severity mapping in Elastic Security is commonly **21** low, **47** medium, **73** high, **99** critical; confirm in your deployment if customized.
 61
 62"""
 63references = [
 64    "https://kudelskisecurity.com/research/investigating-two-variants-of-the-trivy-supply-chain-compromise",
 65]
 66risk_score = 73
 67rule_id = "98cfaa44-83f0-4aba-90c4-363fb9d51a75"
 68severity = "high"
 69tags = [
 70    "Domain: Cloud",
 71    "Data Source: AWS",
 72    "Data Source: Amazon Web Services",
 73    "Data Source: AWS CloudTrail",
 74    "Data Source: AWS IAM",
 75    "Use Case: Threat Detection",
 76    "Tactic: Credential Access",
 77    "Tactic: Initial Access",
 78    "Resources: Investigation Guide",
 79    "Rule Type: Higher-Order Rule",
 80]
 81timestamp_override = "event.ingested"
 82type = "esql"
 83
 84query = '''
 85from .alerts-security.* METADATA _id, _version, _index
 86
 87// Sibling rule: AWS Long-Term Access Key First Seen from Source IP
 88// rule_id = 9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f
 89
 90| where kibana.alert.workflow_status == "open"
 91    and event.kind == "signal"
 92    and source.ip is not null
 93    and kibana.alert.rule.name is not null
 94    and not kibana.alert.rule.type in ("threat_match", "machine_learning")
 95    and not kibana.alert.rule.name like "Deprecated - *"
 96    and not KQL("""kibana.alert.rule.tags : "Rule Type: Higher-Order Rule" """)
 97    and (
 98        kibana.alert.rule.rule_id == "9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f"
 99        or kibana.alert.risk_score >= 47
100        or kibana.alert.severity in ("medium", "high", "critical")
101    )
102
103| eval Esql.is_long_term_key_new_ip_rule = kibana.alert.rule.rule_id == "9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f"
104| eval Esql.is_other_elevated_rule = kibana.alert.rule.rule_id != "9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f"
105    and (
106        kibana.alert.risk_score >= 47
107        or kibana.alert.severity in ("medium", "high", "critical")
108    )
109
110| stats
111    Esql.alert_count_long_term_key_new_ip_rule = SUM(CASE(Esql.is_long_term_key_new_ip_rule, 1, 0)),
112    Esql.alert_count_other_elevated_rule = SUM(CASE(Esql.is_other_elevated_rule, 1, 0)),
113    Esql.kibana_alert_rule_name_values = VALUES(kibana.alert.rule.name),
114    Esql.kibana_alert_rule_id_values = VALUES(kibana.alert.rule.rule_id),
115    Esql.kibana_alert_risk_score_values = VALUES(kibana.alert.risk_score),
116    Esql.kibana_alert_severity_values = VALUES(kibana.alert.severity),
117    Esql.user_entity_id_values = VALUES(user.entity.id),
118    Esql.timestamp_min = MIN(@timestamp),
119    Esql.timestamp_max = MAX(@timestamp)
120  by source.ip
121
122| where Esql.alert_count_long_term_key_new_ip_rule > 0
123    and Esql.alert_count_other_elevated_rule > 0
124
125| keep
126    source.ip,
127    Esql.alert_count_long_term_key_new_ip_rule,
128    Esql.alert_count_other_elevated_rule,
129    Esql.kibana_alert_rule_name_values,
130    Esql.kibana_alert_rule_id_values,
131    Esql.kibana_alert_risk_score_values,
132    Esql.kibana_alert_severity_values,
133    Esql.user_entity_id_values,
134    Esql.timestamp_min,
135    Esql.timestamp_max
136'''
137
138
139[[rule.threat]]
140framework = "MITRE ATT&CK"
141[[rule.threat.technique]]
142id = "T1552"
143name = "Unsecured Credentials"
144reference = "https://attack.mitre.org/techniques/T1552/"
145
146
147[rule.threat.tactic]
148id = "TA0006"
149name = "Credential Access"
150reference = "https://attack.mitre.org/tactics/TA0006/"
151
152[[rule.threat]]
153framework = "MITRE ATT&CK"
154[[rule.threat.technique]]
155id = "T1078"
156name = "Valid Accounts"
157reference = "https://attack.mitre.org/techniques/T1078/"
158[[rule.threat.technique.subtechnique]]
159id = "T1078.004"
160name = "Cloud Accounts"
161reference = "https://attack.mitre.org/techniques/T1078/004/"
162
163[rule.threat.tactic]
164id = "TA0001"
165name = "Initial Access"
166reference = "https://attack.mitre.org/tactics/TA0001/"
167
168[rule.investigation_fields]
169field_names = [
170    "source.ip",
171    "Esql.user_entity_id_values",
172    "Esql.alert_count_long_term_key_new_ip_rule",
173    "Esql.alert_count_other_elevated_rule",
174    "Esql.kibana_alert_rule_name_values",
175    "Esql.kibana_alert_rule_id_values",
176    "Esql.kibana_alert_risk_score_values",
177    "Esql.kibana_alert_severity_values",
178    "Esql.timestamp_min",
179    "Esql.timestamp_max",
180]

Triage and analysis

Investigating AWS IAM Long-Term Access Key Correlated with Elevated Detection Alerts

This is a higher-order rule. It evaluates open signals in .alerts-security.* grouped by aws.cloudtrail.user_identity.access_key_id. A match requires:

  1. At least one alert from AWS Long-Term Access Key First Seen from Source IP (kibana.alert.rule.rule_id 9f8e3c5e-f72e-4e91-93f6-e98a4fae3e4f).
  2. At least one different alert on the same access key with medium or higher severity, implemented as kibana.alert.risk_score >= 47 or kibana.alert.severity in medium, high, or critical.

Possible investigation steps

  • Esql.alert_count_long_term_key_new_ip_rule: Confirm the first-seen-IP rule contributed to the correlation.
  • Esql.alert_count_other_elevated: Count of sibling alerts meeting the elevated threshold (excluding the long-term-key new-IP rule).
  • Esql.kibana_alert_rule_name_values / Esql.kibana_alert_rule_id_values: Identify which rules fired; map tactics and whether activity looks like post-compromise follow-through.
  • aws.cloudtrail.user_identity.access_key_id: Pivot in CloudTrail and IAM (last used, attached policies, user or root context).
  • Esql.aws_cloudtrail_user_identity_arn_values, Esql.source_ip_values: Reconstruct session context across alerts.

False positive analysis

  • Overlapping scheduled jobs, penetration tests, or purple-team runs that reuse the same IAM access key across many detectors.
  • Custom rules calibrated with medium severity but low risk scores still qualify via kibana.alert.severity.

Response and remediation

  • Treat as elevated priority versus the standalone first-seen-IP rule: rotate or disable the access key if abuse is suspected, scope CloudTrail for the key, and review open sibling alerts to closure.

Additional information

  • Default risk score to severity mapping in Elastic Security is commonly 21 low, 47 medium, 73 high, 99 critical; confirm in your deployment if customized.

References

Related rules

to-top