CRITICALCredential Compromise15-30 min containment17 steps across 5 phases

    Compromised IAM Access Keys

    An IAM access key pair has been leaked via a public GitHub repo, compromised CI/CD pipeline, malware, or insider threat. The attacker may be using the keys for reconnaissance, data exfiltration, or resource provisioning (crypto mining). This is the #1 most common AWS security incident.

    Phase 1: Detection

    $ tail -f /var/log/cloudtrail/events.log
    1

    Check GuardDuty for credential abuse findings

    GuardDuty detects when IAM credentials are used from unusual IPs, Tor exit nodes, or known malicious infrastructure.

    aws guardduty list-findings --detector-id <detector-id> \
      --finding-criteria '{"Criterion":{"type":{"Eq":["UnauthorizedAccess:IAMUser/MaliciousIPCaller"]}}}'
    GuardDuty:UnauthorizedAccess:IAMUser/MaliciousIPCallerUnauthorizedAccess:IAMUser/TorIPCallerRecon:IAMUser/MaliciousIPCallerDiscovery:IAMUser/AnomalousBehavior
    2

    Search CloudTrail for the compromised key

    Look up all API calls made with the suspected access key to determine scope of compromise.

    aws cloudtrail lookup-events \
      --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA... \
      --start-time <incident-start> \
      --max-items 50
    CloudTrail:GetCallerIdentityListBucketsDescribeInstancesListRoles
    3

    Identify the key owner and creation date

    Determine which IAM user owns the key and when it was created to understand the exposure window.

    aws iam list-access-keys --user-name <username>
    aws iam get-access-key-last-used --access-key-id AKIA...
    4

    Check for GitHub exposure via AWS Health

    AWS automatically detects access keys published on GitHub and creates Health events.

    aws health describe-events \
      --filter '{"eventTypeCategories":["accountNotification"],"eventTypeCodes":["AWS_RISK_CREDENTIALS_EXPOSED"]}'

    AWS automatically quarantines keys exposed on GitHub by attaching an AWSCompromisedKeyQuarantineV2 policy.

    Phase 2: Containment

    $ ./containment.sh --isolate --immediate
    1

    Deactivate the compromised access key immediately

    Disable the key to prevent further use. Do NOT delete yet - you need it for forensic analysis.

    aws iam update-access-key \
      --access-key-id AKIA... \
      --status Inactive \
      --user-name <username>

    Deactivate first, delete later. You need the key ID to trace CloudTrail events.

    2

    Attach an explicit deny-all policy to the user

    Belt and suspenders. Even if the key is deactivated, block all actions in case other credentials exist.

    aws iam put-user-policy \
      --user-name <username> \
      --policy-name DenyAll-IncidentResponse \
      --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*"}]}'
    3

    Revoke all active temporary sessions

    If the key was used to assume roles (via STS), those temporary credentials are still valid. Revoke them.

    aws iam put-role-policy \
      --role-name <assumed-role> \
      --policy-name RevokeOldSessions \
      --policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*","Condition":{"DateLessThan":{"aws:TokenIssueTime":"2026-03-15T00:00:00Z"}}}]}'
    CloudTrail:AssumeRoleAssumeRoleWithSAMLAssumeRoleWithWebIdentity

    Set the date to the current timestamp. All sessions issued before this time will be denied.

    Phase 3: Eradication

    $ ./eradicate.sh --purge --verify
    1

    Delete the compromised access key

    Once forensics is complete, permanently remove the compromised key.

    aws iam delete-access-key \
      --access-key-id AKIA... \
      --user-name <username>
    2

    Audit for attacker-created backdoors

    Attackers often create new IAM users, access keys, or login profiles for persistence.

    # Check for new IAM users created during the incident
    aws cloudtrail lookup-events \
      --lookup-attributes AttributeKey=EventName,AttributeValue=CreateUser \
      --start-time <incident-start>
    # Check for new access keys on other users
    aws cloudtrail lookup-events \
      --lookup-attributes AttributeKey=EventName,AttributeValue=CreateAccessKey \
      --start-time <incident-start>
    # Check for new login profiles (console access)
    aws cloudtrail lookup-events \
      --lookup-attributes AttributeKey=EventName,AttributeValue=CreateLoginProfile \
      --start-time <incident-start>
    CloudTrail:CreateUserCreateAccessKeyCreateLoginProfileAttachUserPolicyPutUserPolicy
    3

    Remove any attacker-created resources

    Delete EC2 instances, Lambda functions, IAM entities, or any other resources the attacker created.

    # List resources created by the compromised key
    aws cloudtrail lookup-events \
      --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA... \
      --start-time <incident-start> | \
      jq '.Events[].EventName' | sort | uniq -c | sort -rn
    4

    Remove the deny-all incident response policy

    After all backdoors are removed, clean up the temporary deny policy.

    aws iam delete-user-policy \
      --user-name <username> \
      --policy-name DenyAll-IncidentResponse

    Phase 4: Recovery

    $ ./recovery.sh --restore --validate
    1

    Generate new access keys if still needed

    Only create new keys if the workload truly requires long-term credentials. Prefer IAM roles instead.

    aws iam create-access-key --user-name <username>

    Best practice: migrate to IAM roles with temporary credentials instead of long-term access keys.

    2

    Update all systems using the old key

    Rotate the key in all CI/CD pipelines, applications, and configuration files that referenced the old key.

    3

    Verify normal operations

    Monitor CloudTrail and GuardDuty for 24-48 hours to confirm no further unauthorized activity.

    aws guardduty list-findings --detector-id <detector-id> \
      --sort-criteria '{"AttributeName":"updatedAt","OrderBy":"DESC"}' \
      --max-results 10

    Phase 5: Lessons Learned

    $ cat POST_INCIDENT_REVIEW.md
    1

    Enable mandatory key rotation

    Set up an AWS Config rule to detect access keys older than 90 days.

    aws configservice put-config-rule --config-rule '{
      "ConfigRuleName": "access-keys-rotated",
      "Source": {"Owner": "AWS", "SourceIdentifier": "ACCESS_KEYS_ROTATED"},
      "InputParameters": "{"maxAccessKeyAge":"90"}"
    }'
    2

    Migrate workloads to IAM roles

    Replace long-term access keys with IAM roles wherever possible. Use OIDC federation for CI/CD (GitHub Actions, GitLab CI).

    3

    Enable GuardDuty and Security Hub alerts

    Ensure GuardDuty findings flow to Security Hub and trigger SNS notifications for critical findings.

    aws securityhub enable-security-hub
    aws guardduty create-detector --enable
    access-keyscredential-leaklateral-movementexfiltration

    Need Help with Incident Response?

    When an incident strikes, every minute counts. We help AWS teams prepare, detect, and respond to security incidents with proven expertise.