Signals
- An AWS Support case or Health event about exposed credentials (AWS may attach a quarantine policy to the user).
- GuardDuty findings such as
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltrationor unusual API calls. - A key spotted in a public repository, a container image or a ticket.
- Unexpected EC2 instances in unused regions (crypto-mining), or a cost spike.
1. Contain the key — now
aws iam list-access-keys --user-name ci-deploy
aws iam get-access-key-last-used --access-key-id AKIAEXAMPLEKEY
aws iam update-access-key --user-name ci-deploy --access-key-id AKIAEXAMPLEKEY --status Inactive
Deactivate first (reversible), delete later after the investigation. If you don't know which user owns a key:
aws sts get-access-key-info --access-key-id AKIAEXAMPLEKEY # returns the account ID
2. Kill sessions the attacker already has
Temporary credentials obtained with the key (via sts:AssumeRole or GetSessionToken) stay valid after you deactivate it. For each role the user could assume, revoke sessions issued before now:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": { "DateLessThan": { "aws:TokenIssueTime": "2026-10-24T09:30:00Z" } }
}]
}
aws iam put-role-policy --role-name deploy-role --policy-name AWSRevokeOlderSessions --policy-document file://revoke.json
The console's Revoke active sessions button on a role does the same thing.
3. Investigate with CloudTrail
aws cloudtrail lookup-events --lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIAEXAMPLEKEY \
--start-time 2026-10-17 --max-results 50 \
--query "Events[].{t:EventTime, e:EventName, src:EventSource}" --output table
lookup-events covers management events in one region for 90 days. Run it in every region, or query a CloudTrail Lake / Athena table over your organisation trail:
SELECT eventtime, awsregion, eventname, sourceipaddress, errorcode
FROM cloudtrail_logs
WHERE useridentity.accesskeyid = 'AKIAEXAMPLEKEY'
ORDER BY eventtime;
Look for: CreateUser, CreateAccessKey, AttachUserPolicy, CreateLoginProfile (persistence), RunInstances in odd regions, PutBucketPolicy, GetObject bursts (data theft), DeleteTrail / StopLogging (covering tracks).
4. Remove what they created
| Found | Action |
|---|---|
| New IAM users, keys, login profiles | Delete after capturing details |
| Policies attached to existing principals | Detach |
| EC2 instances, Lambda functions, GPU fleets | Snapshot if needed for forensics, then terminate |
| Changed bucket policies or public ACLs | Revert; turn on Block Public Access |
| Disabled CloudTrail or GuardDuty | Re-enable immediately |
5. Recover and rotate
- Issue a replacement for the legitimate workload — ideally not a key (see below).
- Delete the compromised key:
aws iam delete-access-key --user-name ci-deploy --access-key-id AKIAEXAMPLEKEY. - If data was accessed, involve your security, legal and privacy teams.
Prevent it next time
- Replace IAM user keys with roles: IAM Identity Center for people, OIDC federation for CI (GitHub Actions, GitLab), instance profiles and IRSA/EKS Pod Identity for workloads.
- Turn on GuardDuty in every region and account, with findings routed to on-call.
- Secret scanning on repositories and container images.
- An SCP that denies
iam:CreateAccessKeyexcept for a break-glass role.