AWS IAM User Self-Created Access Key Subsequently Used

Detects an AWS IAM user using an existing credential to create a new access key for itself and subsequently using the new key within one hour. This behavior can indicate an adversary converting compromised credentials into an additional long-term credential for persistence. Unlike a standalone self-service key creation alert, requiring subsequent use of the new key reduces noise from unused or abandoned credential-rotation operations.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/07/13"
  3integration = ["aws"]
  4maturity = "production"
  5updated_date = "2026/07/13"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Detects an AWS IAM user using an existing credential to create a new access key for itself and subsequently using the
 11new key within one hour. This behavior can indicate an adversary converting compromised credentials into an additional
 12long-term credential for persistence. Unlike a standalone self-service key creation alert, requiring subsequent use of
 13the new key reduces noise from unused or abandoned credential-rotation operations.
 14"""
 15false_positives = [
 16    """
 17    Approved credential-rotation automation may create and immediately validate or use a replacement access key. Confirm
 18    the principal, source addresses, user agents, timing, and change records. Exclude only verified rotation workflows.
 19    """,
 20]
 21from = "now-35m"
 22language = "esql"
 23license = "Elastic License v2"
 24name = "AWS IAM User Self-Created Access Key Subsequently Used"
 25note = """## Triage and analysis
 26
 27### Investigating AWS IAM User Self-Created Access Key Subsequently Used
 28
 29This rule detects an IAM user using an existing access key (Token A) to create a new access key for itself (Token B), followed by an AWS API request authenticated with Token B within one hour. The creation event proves that Token A was used to request the additional credential, while the subsequent CloudTrail event confirms that Token B became active.
 30
 31This sequence can indicate persistence after an IAM user's credentials are compromised. An adversary can use the compromised key to create another long-term credential, then continue accessing the account through the new key even after the original credential is identified and disabled.
 32
 33### Possible investigation steps
 34
 35- Identify the IAM user and AWS account using `aws.cloudtrail.user_identity.arn` and `cloud.account.id`.
 36- Review Token A in `Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator_values`. Determine whether it is an expected credential for the IAM user and whether it was previously observed from unusual infrastructure.
 37- Confirm that the key in `Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id_values` matches the subsequently used key in `Esql_priv.aws_cloudtrail_user_identity_access_key_id_created_key_use_values`.
 38- Compare `Esql.source_ip_self_access_key_creation_values` with `Esql.source_ip_created_access_key_use_values`. A rapid change in source IP, ASN, geography, or hosting provider increases suspicion.
 39- Compare `Esql.user_agent_original_self_access_key_creation_values` with `Esql.user_agent_original_created_access_key_use_values` to identify changes between the creation and use clients.
 40- Review `Esql.event_action_created_access_key_use_values` to determine what Token B accessed or modified after creation.
 41- Examine nearby CloudTrail activity for IAM reconnaissance, MFA changes, login-profile changes, policy modifications, or attempts to create additional credentials.
 42- Confirm whether the activity corresponds to an approved credential-rotation workflow and whether the user is permitted to create its own long-term access keys.
 43
 44### False positive analysis
 45
 46- Approved credential-rotation automation may create and immediately test or use a replacement access key.
 47- Developer onboarding or recovery workflows may create a key and validate it with an initial AWS API request.
 48- Validate the principal, source addresses, user agents, timing, and change records before excluding the activity. Prefer narrowly scoped exceptions for verified automation rather than excluding the IAM user broadly.
 49
 50### Response and remediation
 51
 52- If unauthorized, deactivate or delete Token B immediately.
 53- Rotate or disable Token A and investigate how it was exposed or compromised.
 54- Review every CloudTrail event authenticated with Token B and determine whether it accessed data, changed IAM permissions, created resources, or established additional persistence.
 55- Review the IAM user's policies and remove unnecessary `iam:CreateAccessKey` permissions.
 56- Prefer temporary credentials and IAM Identity Center for human access instead of long-term IAM user access keys.
 57- Preserve the relevant CloudTrail events, access key IDs, source addresses, and user-agent values for incident response.
 58"""
 59references = [
 60    "https://hackingthe.cloud/aws/exploitation/iam_privilege_escalation/#iamcreateaccesskey",
 61    "https://docs.aws.amazon.com/IAM/latest/APIReference/API_CreateAccessKey.html",
 62    "https://github.com/RhinoSecurityLabs/pacu/tree/master/pacu/modules/iam__backdoor_users_keys",
 63]
 64risk_score = 47
 65rule_id = "dfb7dc0b-2caf-4186-9b59-5752bc59bc19"
 66severity = "medium"
 67tags = [
 68    "Domain: Cloud",
 69    "Data Source: AWS",
 70    "Data Source: Amazon Web Services",
 71    "Data Source: AWS IAM",
 72    "Use Case: Identity and Access Audit",
 73    "Tactic: Persistence",
 74    "Resources: Investigation Guide",
 75]
 76timestamp_override = "event.ingested"
 77type = "esql"
 78
 79query = '''
 80FROM logs-aws.cloudtrail-*
 81| WHERE data_stream.dataset == "aws.cloudtrail"
 82    AND (
 83      (
 84        event.provider == "iam.amazonaws.com"
 85        AND event.action == "CreateAccessKey"
 86        AND event.outcome == "success"
 87        AND aws.cloudtrail.user_identity.type == "IAMUser"
 88      )
 89      OR (
 90        aws.cloudtrail.user_identity.type == "IAMUser"
 91        AND aws.cloudtrail.user_identity.access_key_id LIKE "AKIA*"
 92        AND NOT (
 93          event.provider == "iam.amazonaws.com"
 94          AND event.action == "CreateAccessKey"
 95        )
 96      )
 97    )
 98| GROK aws.cloudtrail.response_elements """.*accessKeyId=(?<Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id>AKIA[A-Z0-9]{16}).*"""
 99| EVAL
100    Esql.aws_cloudtrail_self_access_key_creation_flag = CASE(
101      event.provider == "iam.amazonaws.com"
102        AND event.action == "CreateAccessKey"
103        AND event.outcome == "success"
104        AND aws.cloudtrail.user_identity.type == "IAMUser"
105        AND (
106          user.target.name IS NULL
107          OR user.name == user.target.name
108        )
109        AND Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id IS NOT NULL,
110      1,
111      0
112    ),
113    Esql.aws_cloudtrail_created_access_key_use_flag = CASE(
114      aws.cloudtrail.user_identity.type == "IAMUser"
115        AND aws.cloudtrail.user_identity.access_key_id LIKE "AKIA*"
116        AND NOT (
117          event.provider == "iam.amazonaws.com"
118          AND event.action == "CreateAccessKey"
119        ),
120      1,
121      0
122    )
123| EVAL
124    Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator = CASE(
125      Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
126      aws.cloudtrail.user_identity.access_key_id,
127      null
128    ),
129    Esql_priv.aws_cloudtrail_access_key_id_correlation = CASE(
130      Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
131      Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id,
132      aws.cloudtrail.user_identity.access_key_id
133    )
134| WHERE Esql_priv.aws_cloudtrail_access_key_id_correlation IS NOT NULL
135    AND aws.cloudtrail.user_identity.arn IS NOT NULL
136| STATS
137    Esql.aws_cloudtrail_self_access_key_creation_count =
138      COUNT(*) WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
139    Esql.aws_cloudtrail_created_access_key_use_count =
140      COUNT(*) WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
141    Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator_values =
142      VALUES(Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator)
143      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
144    Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id_values =
145      VALUES(Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id)
146      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
147    Esql_priv.aws_cloudtrail_user_identity_access_key_id_created_key_use_values =
148      VALUES(aws.cloudtrail.user_identity.access_key_id)
149      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
150    Esql.timestamp_self_access_key_creation_min =
151      MIN(@timestamp)
152      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
153    Esql.timestamp_created_access_key_use_min =
154      MIN(@timestamp)
155      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
156    Esql.source_ip_self_access_key_creation_values =
157      VALUES(source.ip)
158      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
159    Esql.source_ip_created_access_key_use_values =
160      VALUES(source.ip)
161      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
162    Esql.user_agent_original_self_access_key_creation_values =
163      VALUES(user_agent.original)
164      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
165    Esql.user_agent_original_created_access_key_use_values =
166      VALUES(user_agent.original)
167      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
168    Esql.event_action_created_access_key_use_values =
169      VALUES(event.action)
170      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1
171  BY cloud.account.id,
172     aws.cloudtrail.user_identity.arn,
173     Esql_priv.aws_cloudtrail_access_key_id_correlation
174| WHERE Esql.aws_cloudtrail_self_access_key_creation_count > 0
175    AND Esql.aws_cloudtrail_created_access_key_use_count > 0
176    AND Esql.timestamp_created_access_key_use_min >
177        Esql.timestamp_self_access_key_creation_min
178    AND DATE_DIFF(
179      "seconds",
180      Esql.timestamp_self_access_key_creation_min,
181      Esql.timestamp_created_access_key_use_min
182    ) <= 3600
183| KEEP
184    cloud.account.id,
185    aws.cloudtrail.user_identity.arn,
186    Esql.*,
187    Esql_priv.*
188'''
189
190
191[[rule.threat]]
192framework = "MITRE ATT&CK"
193[[rule.threat.technique]]
194id = "T1098"
195name = "Account Manipulation"
196reference = "https://attack.mitre.org/techniques/T1098/"
197[[rule.threat.technique.subtechnique]]
198id = "T1098.001"
199name = "Additional Cloud Credentials"
200reference = "https://attack.mitre.org/techniques/T1098/001/"
201
202
203
204[rule.threat.tactic]
205id = "TA0003"
206name = "Persistence"
207reference = "https://attack.mitre.org/tactics/TA0003/"
208
209[rule.alert_suppression]
210group_by = ["aws.cloudtrail.user_identity.arn","cloud.account.id"]
211missing_fields_strategy = "suppress"
212[rule.alert_suppression.duration]
213unit = "m"
214value = 35
215
216[rule.investigation_fields]
217field_names = [
218    "cloud.account.id",
219    "aws.cloudtrail.user_identity.arn",
220    "Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator_values",
221    "Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id_values",
222    "Esql_priv.aws_cloudtrail_user_identity_access_key_id_created_key_use_values",
223    "Esql.timestamp_self_access_key_creation_min",
224    "Esql.timestamp_created_access_key_use_min",
225    "Esql.source_ip_self_access_key_creation_values",
226    "Esql.source_ip_created_access_key_use_values",
227    "Esql.user_agent_original_self_access_key_creation_values",
228    "Esql.user_agent_original_created_access_key_use_values",
229    "Esql.event_action_created_access_key_use_values",
230]

Triage and analysis

Investigating AWS IAM User Self-Created Access Key Subsequently Used

This rule detects an IAM user using an existing access key (Token A) to create a new access key for itself (Token B), followed by an AWS API request authenticated with Token B within one hour. The creation event proves that Token A was used to request the additional credential, while the subsequent CloudTrail event confirms that Token B became active.

This sequence can indicate persistence after an IAM user's credentials are compromised. An adversary can use the compromised key to create another long-term credential, then continue accessing the account through the new key even after the original credential is identified and disabled.

Possible investigation steps

  • Identify the IAM user and AWS account using aws.cloudtrail.user_identity.arn and cloud.account.id.
  • Review Token A in Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator_values. Determine whether it is an expected credential for the IAM user and whether it was previously observed from unusual infrastructure.
  • Confirm that the key in Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id_values matches the subsequently used key in Esql_priv.aws_cloudtrail_user_identity_access_key_id_created_key_use_values.
  • Compare Esql.source_ip_self_access_key_creation_values with Esql.source_ip_created_access_key_use_values. A rapid change in source IP, ASN, geography, or hosting provider increases suspicion.
  • Compare Esql.user_agent_original_self_access_key_creation_values with Esql.user_agent_original_created_access_key_use_values to identify changes between the creation and use clients.
  • Review Esql.event_action_created_access_key_use_values to determine what Token B accessed or modified after creation.
  • Examine nearby CloudTrail activity for IAM reconnaissance, MFA changes, login-profile changes, policy modifications, or attempts to create additional credentials.
  • Confirm whether the activity corresponds to an approved credential-rotation workflow and whether the user is permitted to create its own long-term access keys.

False positive analysis

  • Approved credential-rotation automation may create and immediately test or use a replacement access key.
  • Developer onboarding or recovery workflows may create a key and validate it with an initial AWS API request.
  • Validate the principal, source addresses, user agents, timing, and change records before excluding the activity. Prefer narrowly scoped exceptions for verified automation rather than excluding the IAM user broadly.

Response and remediation

  • If unauthorized, deactivate or delete Token B immediately.
  • Rotate or disable Token A and investigate how it was exposed or compromised.
  • Review every CloudTrail event authenticated with Token B and determine whether it accessed data, changed IAM permissions, created resources, or established additional persistence.
  • Review the IAM user's policies and remove unnecessary iam:CreateAccessKey permissions.
  • Prefer temporary credentials and IAM Identity Center for human access instead of long-term IAM user access keys.
  • Preserve the relevant CloudTrail events, access key IDs, source addresses, and user-agent values for incident response.

References

Related rules

to-top