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.
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"]}}}'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
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...
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.
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.
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":"*"}]}'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"}}}]}'Set the date to the current timestamp. All sessions issued before this time will be denied.
Once forensics is complete, permanently remove the compromised key.
aws iam delete-access-key \ --access-key-id AKIA... \ --user-name <username>
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>
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
After all backdoors are removed, clean up the temporary deny policy.
aws iam delete-user-policy \ --user-name <username> \ --policy-name DenyAll-IncidentResponse
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.
Rotate the key in all CI/CD pipelines, applications, and configuration files that referenced the old key.
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 10Set 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"}"
}'Replace long-term access keys with IAM roles wherever possible. Use OIDC federation for CI/CD (GitHub Actions, GitLab CI).
Ensure GuardDuty findings flow to Security Hub and trigger SNS notifications for critical findings.
aws securityhub enable-security-hub
aws guardduty create-detector --enable
When an incident strikes, every minute counts. We help AWS teams prepare, detect, and respond to security incidents with proven expertise.