AWS quarantines a leaked access key in 10 seconds, but the key keeps working for anything not on the deny list
Unit 42 timed AWS attaching its compromised-key quarantine policy 10 seconds after a key hit public GitHub. The key stays valid, the policy denies a fixed list of 99 actions, and role pivots, secret reads and clean-up are left to the customer.
By Parminder Kumar Sharma · · 16 min read

Ten seconds to quarantine, and a key that still works
In a test run by Palo Alto Networks' Unit 42 on 19 December 2025, an AWS access key and its secret were pushed to a public GitHub repository at 18:50:05 UTC. At 18:50:15, CloudTrail recorded AWS attaching the managed policy AWSCompromisedKeyQuarantineV3 to the IAM user that owned the key. That is 10 seconds from exposure to what AWS's own email calls the "Quarantine Policy". A support case followed at 18:50:59, 54 seconds after the push. Unit 42 published the full timeline on 21 September 2026 in From Exposure to Lockdown.
Ten seconds is fast, and the automation is real. But be clear about what the number does not show. It comes from one test, one key, one public repository and one push to the main branch. It says nothing about how quickly an attacker who scrapes GitHub would have used the key in those 10 seconds. It says nothing about keys leaked anywhere AWS is not told about. Most of all, it does not mean the key stopped working. AWS did not disable the key. It attached a deny list to the user, and anything not on that list still works if the user's own policies allow it.
This briefing sets out what AWS does automatically, what Unit 42 measured, what the current policy blocks, what it leaves open to an attacker holding the key, and what the account owner still has to do. It also flags one thing Unit 42's write-up does not cover: AWS changed the V3 policy again on 16 March 2026, adding KMS, S3 encryption and AWS Organizations actions to the deny list. The version in force today is not the one Unit 42 describes.
What AWS does automatically, second by second
The trigger in Unit 42's test was GitHub's secret scanning partner programme. GitHub scans public repositories and public npm packages for known credential formats and, when it finds one belonging to a partner, sends the match to that partner's endpoint. AWS is one of those partners. Unit 42 says AWS also acts on "other notices" of exposure, but neither Unit 42 nor AWS lists which other sources trigger the process.
The key was not pushed by accident. At 18:46:57, GitHub push protection flagged both the access key and the secret and asked whether the push was deliberate. The tester allowed it, and the push succeeded at 18:50:05, 3 minutes 8 seconds later. Everything below is measured from that successful push.
Unit 42 exposure test, 19 December 2025, times in UTC. Seconds after the push are computed by pk-sharma.com from Unit 42's timestamps.
| Time (UTC) | Event | Seconds after push | In default CloudTrail? |
|---|---|---|---|
| 17:29:44 | Access key created for TestUser | n/a (1 h 20 min 21 s before) | Yes, CreateAccessKey |
| 18:46:57 | GitHub push protection warns; tester allows push | n/a (3 min 8 s before) | No |
| 18:50:05 | Key and secret land in public repo | 0 | No |
| 18:50:15 | AWSCompromisedKeyQuarantineV3 attached to TestUser | 10 | Yes, AttachUserPolicy |
| 18:50:16 | GitHub email about the detected secret | 11 | No |
| 18:50:17 | AWS Health alert "Risk IAM quarantine" | 12 | No |
| 18:50:26 | AWS email: key exposed, Quarantine Policy applied | 21 | No |
| 18:50:59 | AWS Support case opened | 54 | No |
Three details in Unit 42's screenshots matter for anyone who has to detect this.
The CloudTrail record names your user, not AWS. The AttachUserPolicy event lists TestUser in the userIdentity field, with a temporary ASIA access key ID, even though TestUser did nothing. Unit 42's text says the record did not indicate an automated AWS action. Its screenshot of the same record does show invokedBy, sourceIPAddress and userAgent all set to "AWS Internal". Either way, the reliable signal is the policyArn request parameter, which contains AWSCompromisedKeyQuarantineV3. The event was logged in us-east-1.
The Health alert and the support case leave no CloudTrail trace. The Health event sits under the service "Risk", marked "Action Required", for the personas "Operations, Security". Its end time is 2 January 2026, 14 days after it opened. The support case is titled "[Action Required] Your AWS Access Key is Exposed for AWS Account", with severity "Urgent business impacting question" and status "Pending customer action".
The email goes wherever the account's contacts point. Unit 42 notes that in some organisations the cloud engineering team reads these emails and the security team never sees them. If your account contacts are a shared engineering mailbox, the fastest human signal may never reach the security team.
What the quarantine policy denies, version by version
AWSCompromisedKeyQuarantine is an AWS managed policy made entirely of Deny statements. Under IAM policy evaluation, an explicit deny overrides any allow, so AWS can switch off specific actions without touching the user's existing permissions. AWS has made two kinds of change over the years: minor versions that edit the policy in place, and major versions with a new name (V2, V3). Unit 42 reports that once a new version ships, AWS stops attaching the older ones.
The counts below come from pk-sharma.com parsing the JSON policy documents on AWS's reference pages for V1, V2 and V3, cross-checked against Unit 42's change tables.
AWSCompromisedKeyQuarantine versions. Dates from AWS policy reference pages and Unit 42 tables; action and service counts computed by pk-sharma.com from AWS's published JSON.
| Policy and version | Date (UTC) | Denied actions | What changed |
|---|---|---|---|
| AWSCompromisedKeyQuarantine v1 | 11 Aug 2020 | 28 in 5 services | IAM, EC2, Organizations, Lambda, Lightsail |
| V2 v1 | 21 Apr 2021 | 55 | Adds 11 S3 actions plus 16 more IAM and Lambda actions |
| V2 v2 to v4 | Nov 2021 to Mar 2023 | 55 to 60 | Ownership-controls swap; cloudtrail:LookupEvents; Reserved Instance and Savings Plan purchases |
| V2 v5 | 2 Oct 2024 | 89 in 19 services | Bedrock, SageMaker, ECS, ECR, SES, STS tokens, Amplify, CodeBuild, Glue |
| V3 v1 and v2 | 21 Aug 2024; 2 Oct 2024 | Same as V2 v4, then V2 v5 | V3 v2 created 10 min 48 s after V2 v5 |
| V3 v3 (current default) | 16 Mar 2026 | 99 in 21 services, plus 1 conditional | KMS, logs:PutLogEvents, Organizations exit, s3:PutEncryptionConfiguration |
Unit 42's arithmetic holds: V2 added 61 actions to V1's 28, making 89, and those additions touched 17 services. Its V3 table stops at v2. The AWS reference page for V3 shows a third version, "v3 (default)", edited on 16 March 2026. That is 87 days after Unit 42's test, so the test almost certainly attached V3 v2; Unit 42 does not name the minor version. It also means the article, although dated 21 September 2026, describes a policy that has since changed.
V3 v3 adds ten actions: kms:PutKeyPolicy, kms:RetireGrant, kms:RevokeGrant, kms:ScheduleKeyDeletion, kms:DeleteImportedKeyMaterial, logs:PutLogEvents, organizations:LeaveOrganization, organizations:AcceptHandshake and s3:PutEncryptionConfiguration, plus a second statement that denies kms:CreateGrant whenever the request is not made through an AWS service. AWS gives no reason for any of these changes. Our reading, which is inference: they close routes to extortion by encryption (re-keying buckets, locking or deleting KMS keys) and a route out of organisational guardrails (leaving the organisation, and with it its service control policies). Unit 42 makes the same kind of link for earlier versions: s3:DeleteObject after data-theft extortion, and the Bedrock actions after Permiso's research on attackers invoking models with stolen keys.
One more sign of lag: AWS's own knowledge-centre article on unauthorised activity still tells readers to check that AWSCompromisedKeyQuarantineV2 is attached. It does not mention V3. If your runbook or detection rule matches only one policy name, check it against all three.
A quarantine is a deny list, not a lockdown
"Quarantine" suggests something sealed off. What AWS actually does is narrower, and Unit 42 says so plainly: AWS "purposefully denies specific actions rather than completely disabling the compromised access key or user password". A stricter AWS managed policy exists, AWSDenyAll, which denies everything. AWS chose not to use it because it would break any legitimate workload that uses the same key. That is a reasonable trade for an automatic action taken without the customer's consent. It is still a trade, and the customer carries what is left over.
What the word suggests against what the policy does. Sources: AWS policy reference, AWS support case text shown by Unit 42, AWS IAM and STS documentation.
| What 'quarantine' suggests | What actually happens | Stated or inferred |
|---|---|---|
| The key is switched off | The key stays valid; listed actions return access denied | Stated (Unit 42, AWS email) |
| The attacker is locked out | Anything not on the list still works if the user's policies allow it | Follows from IAM evaluation |
| It follows the stolen credential | It is attached to the IAM user, so it covers all that user's keys and password | Stated (AWS support case) |
| It covers everything the key can reach | Role sessions assumed with the key carry the role's permissions, not the user's | Inference from STS documentation |
| AWS cleans up | AWS attaches a policy and opens a case; rotation, investigation and clean-up are yours | Stated (AWS support case) |
| It ends when the risk ends | It stays until someone with iam:DetachUserPolicy removes it | Stated (AWS support case) |
The role-session point is the one most likely to catch teams out. According to the STS AssumeRole reference, a role session's permissions come from the role's identity-based policy, intersected with any session policy. The quarantine policy is attached to the user, not to any role the user can assume. So if the leaked user is allowed to assume a role, the policy does not deny sts:AssumeRole, and the resulting session is not restricted by the user's quarantine. This is our inference from AWS's documented evaluation model; neither Unit 42 nor AWS tested or described it.
Sessions created with GetSessionToken behave differently. AWS documents that they have the same permissions as the IAM user and that permissions on temporary credentials are evaluated each time they are used, so the user's deny list reaches those sessions too. V2 v5 also added sts:GetSessionToken and sts:GetFederationToken to the deny list, so the key cannot mint new ones.
What an attacker can still do while quarantined
The table below checks named actions against the JSON of the current V3 policy (v3, edited 16 March 2026). An action marked "Not listed" is not blocked by the quarantine. The attacker can use it only if the user's own policies already allow it. For a narrowly scoped deployment key that may be almost nothing. For an old administrator key, it is a lot.
Selected actions checked against the AWSCompromisedKeyQuarantineV3 v3 JSON policy, accessed 21 September 2026. Checked by pk-sharma.com.
| Action | On the V3 deny list? | Why it matters |
|---|---|---|
| sts:AssumeRole | Not listed | Pivot into a role whose session the quarantine does not touch |
| secretsmanager:GetSecretValue, ssm:GetParameter | Not listed | Read stored secrets and parameters |
| ssm:SendCommand, ssm:StartSession | Not listed | Run commands on instances already running |
| s3:GetObject | Denied | Blocks plain object reads |
| s3:GetObjectVersion, s3:ListBucketVersions | Not listed | Older object versions stay readable where versioning is on |
| s3:PutObject | Not listed | Overwrite objects in place |
| ses:GetSendQuota, ses:ListIdentities | Denied | Blocks SES reconnaissance |
| ses:SendEmail | Not listed | Send mail from verified identities |
| cloudtrail:LookupEvents | Denied | The attacker cannot read your event history |
| cloudtrail:StopLogging, cloudtrail:DeleteTrail | Not listed | Turn off your trail |
| iam:CreateAccessKey, iam:CreateUser, iam:CreateRole | Denied | Blocks the classic persistence moves |
| iam:DeleteUser, iam:DeleteLoginProfile, iam:DeactivateMFADevice | Not listed | Destructive or disruptive identity changes |
| ec2:CreateSnapshot, ec2:ModifySnapshotAttribute | Not listed | Copy disks and share snapshots out |
| lambda:InvokeFunction, lambda:UpdateFunctionConfiguration | Not listed | Run or reconfigure existing functions |
Two timing gaps sit either side of the quarantine. Before it: for 10 seconds in Unit 42's test, the key carried the user's full permissions. Anything done in that window, including creating a new key or user, is not undone by the quarantine. After it: the key stays valid with a partial deny list until a person acts. The Health event's 14-day end time is not an expiry for the key. Neither source says how long the key stays live.
The quarantine also restricts defenders. It denies iam:UpdateAccessKey, iam:DeleteAccessKey and iam:CreateAccessKey to the quarantined user, so the key cannot rotate itself. Rotation has to come from another identity with IAM rights. And cloudtrail:LookupEvents is denied, so any tooling that ran under that user will lose sight of event history.
What the customer still has to do
AWS's support case, shown in full in Unit 42's Figure 8, gives five steps. In summary: (1) replace the key: create a second key, move the application to it, make the exposed key inactive, then delete it; (2) check CloudTrail for unapproved IAM users, login profiles, access keys, policies, roles or temporary credentials, and delete them; (3) check for unwanted usage such as EC2 instances, Lambda functions or Spot bids in every Region, and review the bill; (4) detach the Quarantine Policy from the user; (5) reply to the case to confirm steps 1 to 3, "to restore access to your account, prevent suspension, and apply for a billing adjustment, if applicable". The case adds that rotating and deleting the key alone "may not be sufficient". AWS says it will consider billing adjustments only after the account is secured.
That list is sound but incomplete for a security team. Default CloudTrail event history covers 90 days of management events and does not show data events. Data events are not logged by default, and they include S3 object-level calls such as GetObject. If the leaked key read objects in its first 10 seconds, or read older object versions afterwards, you will not see it unless you already had data-event logging on. AWS's knowledge-centre article also points to revoking role sessions where temporary credentials are involved. IAM's revoke-sessions feature attaches an AWSRevokeOlderSessions deny to the role, covering sessions issued before the revocation time.
Remediation: what AWS's case asks for, set against what it leaves unstated. Sources: AWS support case text (Unit 42 Figure 8), AWS knowledge centre, IAM and CloudTrail documentation.
| Task | In AWS's support case | Not stated in the case |
|---|---|---|
| Rotate the key | Yes: create, switch, disable, delete | That the quarantined user cannot rotate its own key |
| Hunt persistence | Yes: users, login profiles, keys, policies, roles, temporary credentials | Trust policy edits, new policy versions, federation tokens, instance profiles |
| Check usage | Yes: EC2, Lambda, Spot, every Region, billing | Bedrock, SES, SageMaker, ECS, which later versions target |
| Role sessions | No | Revoke sessions for roles the user could assume |
| Data access | No | S3 reads are data events and absent from default history |
| Remove quarantine | Yes, after steps 1 to 3 | Who in your organisation holds iam:DetachUserPolicy |
Detecting a quarantine in your own logs
Do not rely on the email. Unit 42 suggests alerting on the CloudTrail event itself, and on GitHub's validity checks, which call sts:GetCallerIdentity with the leaked key from GitHub's network under a distinctive user agent. The user agent string contains GHAS-AWS_KEYID-validation; GitHub changed the rest of the string in early October 2025, so match on that fragment rather than the whole thing. Unit 42's indicator list gives both full forms.
Useful signals:
- CloudTrail:
eventSource=iam.amazonaws.com,eventName=AttachUserPolicy, andrequestParameters.policyArncontainingAWSCompromisedKeyQuarantine. Match the prefix so V1, V2 and V3 all fire. - AWS Health: the "Risk IAM quarantine" event. AWS documents that global events, including IAM, reach EventBridge only through a rule in US East (N. Virginia). We could not confirm the exact Health event type code for this alert, so match on the Risk service and test with your own rule.
- Standing audit: list which users carry any quarantine policy today. A forgotten attachment is an unfinished incident.
# Which IAM users currently carry an AWS quarantine policy?
for p in AWSCompromisedKeyQuarantine AWSCompromisedKeyQuarantineV2 AWSCompromisedKeyQuarantineV3; do
echo "== $p"
aws iam list-entities-for-policy --policy-arn arn:aws:iam::aws:policy/$p --entity-filter User
done
# Recent quarantine attachments in the last 90 days of management events (IAM events log in us-east-1)
aws cloudtrail lookup-events --region us-east-1 \
--lookup-attributes AttributeKey=EventName,AttributeValue=AttachUserPolicy \
--max-results 50 --output json | grep -o 'AWSCompromisedKeyQuarantine[A-Za-z0-9]*' | sort | uniq -c
GitHub's push protection is not a complete net either. GitHub's own troubleshooting page says push protection skips pushes to public repositories larger than 50 MB and shows at most five secrets at a time, and that very large pushes may time out without blocking. Unit 42 flags the same limits.
Method, interest and what the research does not show
Unit 42 is the threat research arm of Palo Alto Networks. The article ends by promoting Cortex Cloud, Idira privileged access management and Unit 42's assessment and incident response services. That does not undermine the test: the timeline is supported by screenshots of CloudTrail, the Health dashboard, the email and the support case. But note its limits. It is one controlled exposure, of a user whose only attached policy, in Unit 42's Figure 3, was the quarantine policy itself. It measures AWS's reaction, not an attacker's. It does not test keys leaked in private repositories, container images, CI logs or paste sites, and it does not cover the V3 v3 changes.
There are also small inconsistencies worth knowing about. The alt text for the Health dashboard screenshot says the event started in 2018; the screenshot shows 19 December 2025. The text calls the attach event unattributed to AWS; the screenshot shows "AWS Internal" markers. And the article calls EC2 "Elastic Cloud Compute". None of these changes the findings. They are a reminder to check the figures, not just the prose.
AWS deserves credit. An automatic, provider-side deny list that goes on within seconds, without breaking legitimate workloads, is a real safeguard. AWS has kept extending it as attacker behaviour shifted, from crypto-mining to S3 deletion, generative AI abuse and, now, KMS. The point is not that the quarantine is weak. The point is that it was built to stop fraudulent spend, and a team that reads "quarantined" as "contained" will stop investigating too early.
What to do, in order
Take this with you
Actions for AWS account owners
- Run list-entities-for-policy for all three quarantine policy names today, and treat any user still carrying one as an open incident.
- Create a CloudTrail or EventBridge alert on AttachUserPolicy where the policy ARN contains AWSCompromisedKeyQuarantine, routed to the security team, not only to engineering.
- Add an AWS Health EventBridge rule in us-east-1 for the Risk service, and check where your account's security contact emails actually land.
- Write the quarantine runbook now: rotate the key from an admin identity, hunt persistence in CloudTrail across every Region, then revoke sessions for every role the user could assume.
- Check whether each IAM user with long-term keys can call sts:AssumeRole, and into what; that is the part of the blast radius the quarantine does not cover.
- Turn on S3 data-event logging for buckets holding sensitive data, so a future leak's reads are visible.
- Update runbooks and detections that name only AWSCompromisedKeyQuarantineV2, as AWS's own knowledge-centre article still does.
- Replace long-term IAM user access keys with role-based, short-lived credentials wherever you can; the quarantine exists because those keys leak.
The question to ask
AWS will very likely catch a key pushed to public GitHub within seconds, and it will tell someone. The questions it cannot answer for you are these: if AWS quarantined one of your keys tonight, who would see it first, and which roles could that key still assume after the quarantine went on?
Key facts
Sources
- PrimaryFrom Exposure to Lockdown: primary research; exposure test timeline, screenshots of CloudTrail, Health, email and support case, policy version tables, GitHub user agentsUnit 42, Palo Alto Networksaccessed 2026-09-21
- PrimaryAWSCompromisedKeyQuarantineV3 managed policy reference: description, creation and edit dates, current v3 JSON used for action counts and absence checksAWSaccessed 2026-09-21
- PrimaryAWSCompromisedKeyQuarantineV2 managed policy reference: v5 JSON, dates, descriptionAWSaccessed 2026-09-21
- PrimaryAWSCompromisedKeyQuarantine (V1) managed policy reference: original 28-action JSONAWSaccessed 2026-09-21
- PrimaryWhat to do about unauthorised activity: remediation steps, still references V2AWS re:Post Knowledge Centeraccessed 2026-09-21
- PrimarySTS AssumeRole API reference: role session permissions come from the role's policiesAWSaccessed 2026-09-21
- PrimaryIAM guide: GetSessionToken credentials have the same permissions as the IAM userAWSaccessed 2026-09-21
- PrimaryIAM guide: permissions on temporary credentials are evaluated on each requestAWSaccessed 2026-09-21
- PrimaryIAM guide: revoking role sessions with AWSRevokeOlderSessionsAWSaccessed 2026-09-21
- PrimaryIAM policy evaluation logic: explicit deny overrides allowAWSaccessed 2026-09-21
- PrimaryCloudTrail event history: 90 days of management events, no data eventsAWSaccessed 2026-09-21
- PrimaryCloudTrail data events: not logged by default, include S3 GetObjectAWSaccessed 2026-09-21
- PrimaryAWS Health EventBridge rules: global events including IAM need a US East (N. Virginia) ruleAWSaccessed 2026-09-21
- PrimarySecret scanning partner programme: public repositories and npm packages, alerts to partnersGitHubaccessed 2026-09-21
- PrimaryPush protection limits: 50 MB public push skip, five secrets shown, large-push timeoutsGitHubaccessed 2026-09-21


