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/09/18"
  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    "Noise: Low",
 76    "Performance: Fast",
 77    "Rule Type: ES|QL",
 78    "Platform: AWS",
 79    "Service: AWS IAM",
 80]
 81timestamp_override = "event.ingested"
 82type = "esql"
 83
 84query = '''
 85FROM logs-aws.cloudtrail-*
 86| WHERE data_stream.dataset == "aws.cloudtrail"
 87    AND (
 88      (
 89        event.provider == "iam.amazonaws.com"
 90        AND event.action == "CreateAccessKey"
 91        AND event.outcome == "success"
 92        AND aws.cloudtrail.user_identity.type == "IAMUser"
 93      )
 94      OR (
 95        aws.cloudtrail.user_identity.type == "IAMUser"
 96        AND aws.cloudtrail.user_identity.access_key_id LIKE "AKIA*"
 97        AND NOT (
 98          event.provider == "iam.amazonaws.com"
 99          AND event.action == "CreateAccessKey"
100        )
101      )
102    )
103| GROK aws.cloudtrail.response_elements """.*accessKeyId=(?<Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id>AKIA[A-Z0-9]{16}).*"""
104| EVAL
105    Esql.aws_cloudtrail_self_access_key_creation_flag = CASE(
106      event.provider == "iam.amazonaws.com"
107        AND event.action == "CreateAccessKey"
108        AND event.outcome == "success"
109        AND aws.cloudtrail.user_identity.type == "IAMUser"
110        AND (
111          user.target.name IS NULL
112          OR user.name == user.target.name
113        )
114        AND Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id IS NOT NULL,
115      1,
116      0
117    ),
118    Esql.aws_cloudtrail_created_access_key_use_flag = CASE(
119      aws.cloudtrail.user_identity.type == "IAMUser"
120        AND aws.cloudtrail.user_identity.access_key_id LIKE "AKIA*"
121        AND NOT (
122          event.provider == "iam.amazonaws.com"
123          AND event.action == "CreateAccessKey"
124        ),
125      1,
126      0
127    )
128| EVAL
129    Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator = CASE(
130      Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
131      aws.cloudtrail.user_identity.access_key_id,
132      null
133    ),
134    Esql_priv.aws_cloudtrail_access_key_id_correlation = CASE(
135      Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
136      Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id,
137      aws.cloudtrail.user_identity.access_key_id
138    )
139| WHERE Esql_priv.aws_cloudtrail_access_key_id_correlation IS NOT NULL
140    AND aws.cloudtrail.user_identity.arn IS NOT NULL
141| STATS
142    Esql.aws_cloudtrail_self_access_key_creation_count =
143      COUNT(*) WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
144    Esql.aws_cloudtrail_created_access_key_use_count =
145      COUNT(*) WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
146    Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator_values =
147      VALUES(Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator)
148      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
149    Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id_values =
150      VALUES(Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id)
151      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
152    Esql_priv.aws_cloudtrail_user_identity_access_key_id_created_key_use_values =
153      VALUES(aws.cloudtrail.user_identity.access_key_id)
154      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
155    Esql.timestamp_self_access_key_creation_min =
156      MIN(@timestamp)
157      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
158    Esql.timestamp_created_access_key_use_min =
159      MIN(@timestamp)
160      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
161    Esql.source_ip_self_access_key_creation_values =
162      VALUES(source.ip)
163      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
164    Esql.source_ip_created_access_key_use_values =
165      VALUES(source.ip)
166      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
167    Esql.user_agent_original_self_access_key_creation_values =
168      VALUES(user_agent.original)
169      WHERE Esql.aws_cloudtrail_self_access_key_creation_flag == 1,
170    Esql.user_agent_original_created_access_key_use_values =
171      VALUES(user_agent.original)
172      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1,
173    Esql.event_action_created_access_key_use_values =
174      VALUES(event.action)
175      WHERE Esql.aws_cloudtrail_created_access_key_use_flag == 1
176  BY cloud.account.id,
177     aws.cloudtrail.user_identity.arn,
178     Esql_priv.aws_cloudtrail_access_key_id_correlation
179| WHERE Esql.aws_cloudtrail_self_access_key_creation_count > 0
180    AND Esql.aws_cloudtrail_created_access_key_use_count > 0
181    AND Esql.timestamp_created_access_key_use_min >
182        Esql.timestamp_self_access_key_creation_min
183    AND DATE_DIFF(
184      "seconds",
185      Esql.timestamp_self_access_key_creation_min,
186      Esql.timestamp_created_access_key_use_min
187    ) <= 3600
188| KEEP
189    cloud.account.id,
190    aws.cloudtrail.user_identity.arn,
191    Esql.*,
192    Esql_priv.*
193'''
194
195
196[[rule.threat]]
197framework = "MITRE ATT&CK"
198[[rule.threat.technique]]
199id = "T1098"
200name = "Account Manipulation"
201reference = "https://attack.mitre.org/techniques/T1098/"
202[[rule.threat.technique.subtechnique]]
203id = "T1098.001"
204name = "Additional Cloud Credentials"
205reference = "https://attack.mitre.org/techniques/T1098/001/"
206
207
208
209[rule.threat.tactic]
210id = "TA0003"
211name = "Persistence"
212reference = "https://attack.mitre.org/tactics/TA0003/"
213
214[rule.alert_suppression]
215group_by = ["aws.cloudtrail.user_identity.arn","cloud.account.id"]
216missing_fields_strategy = "suppress"
217[rule.alert_suppression.duration]
218unit = "m"
219value = 35
220
221[rule.investigation_fields]
222field_names = [
223    "cloud.account.id",
224    "aws.cloudtrail.user_identity.arn",
225    "Esql_priv.aws_cloudtrail_user_identity_access_key_id_creator_values",
226    "Esql_priv.aws_cloudtrail_response_elements_access_key_access_key_id_values",
227    "Esql_priv.aws_cloudtrail_user_identity_access_key_id_created_key_use_values",
228    "Esql.timestamp_self_access_key_creation_min",
229    "Esql.timestamp_created_access_key_use_min",
230    "Esql.source_ip_self_access_key_creation_values",
231    "Esql.source_ip_created_access_key_use_values",
232    "Esql.user_agent_original_self_access_key_creation_values",
233    "Esql.user_agent_original_created_access_key_use_values",
234    "Esql.event_action_created_access_key_use_values",
235]

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