Back to Intelligence

Exposed AWS IAM Credentials: How AWS Quarantine Policies Work — and the Detection and Remediation Playbook Defenders Still Need

SA
Security Arsenal Team
September 21, 2026
13 min read

Unit 42 recently published a detailed look at a mechanism every cloud defender should understand cold: when AWS detects that an IAM access key has been exposed publicly — most commonly via GitHub's secret scanning partnership — it doesn't just email the account owner and hope for the best. AWS attaches a restrictive managed policy, AWSCompromisedKeyQuarantine (and its successor versions V2/V3), directly to the affected principal, effectively putting the leaked credential in a chokehold within minutes of exposure.

This is genuinely good news, and it reflects a mature ecosystem response to one of the oldest problems in cloud security: developers committing AKIA... keys to public repositories, where automated criminal scraping infrastructure finds them in seconds. But here's the uncomfortable truth I've seen play out in real incident response engagements: organizations treat the AWS quarantine notification as the end of the incident when it should be the beginning. A quarantined key is still a valid key. The quarantine policy blocks a curated list of dangerous actions — it is not a comprehensive deny. And critically, the exposure event itself tells you something far more important than the key's status: secrets are leaking out of your development pipeline, and the attacker may have had a head start of minutes, hours, or days before the quarantine landed.

This post breaks down how the AWS neutralization mechanism actually works, what it does and doesn't block, and — most importantly — the detection, hunting, and remediation workflow your SOC should execute the moment a quarantine event fires.

Technical Analysis: How AWS Neutralizes Exposed Credentials

The Detection Pipeline: GitHub Secret Scanning → AWS

The front half of this story is GitHub's secret scanning service, which continuously scans public repositories (and private repos with GitHub Advanced Security) for known credential formats. AWS access key IDs follow a highly regex-able pattern — the AKIA prefix for long-term IAM user keys, ASIA for temporary STS credentials, followed by 16 alphanumeric characters — making them one of the most reliably detected secret types on the platform.

When GitHub's scanner identifies a string matching the AWS access key pattern, it forwards the candidate to AWS through a partner verification API. AWS then validates whether the key is real and active. If it is, two things happen:

  1. The compromised principal gets a quarantine policy attached. AWS attaches the AWSCompromisedKeyQuarantine managed policy to the IAM user whose key was exposed. This happens via IAM AttachUserPolicy and is visible in CloudTrail with AWS itself as the actor.
  2. The account owner is notified via email to the account's registered contact and through AWS Health Dashboard / Personal Health Dashboard events.

What the Quarantine Policy Actually Blocks

The AWSCompromisedKeyQuarantine policy is an explicit-deny policy targeting the actions most commonly abused by attackers holding a stolen key. Across its versions, the deny list covers the highest-impact abuse primitives:

  • IAM mutations — creating new users, access keys, login profiles, attaching policies (blocks the classic persistence path)
  • EC2 instance launches (ec2:RunInstances and related) — kills the cryptomining playbook, which is the single most common monetization of stolen keys
  • Lambda invocation and creation — blocks serverless cryptomining and backdooring
  • Lightsail, ECS/EKS compute launches — alternate cryptomining surfaces
  • Organizations and billing changes — protects against account manipulation
  • SNS/SQS abuse paths used for spam infrastructure

What it generally does not fully block: broad read access (attackers can still enumerate and exfiltrate from S3 buckets, read Secrets Manager values if separately permitted, snapshot and copy data, and recon the environment). Version V3 tightened coverage further as attackers adapted, adding restrictions around services adversaries had pivoted to when earlier quarantine versions blocked their first-choice abuse path.

The Defender's Threat Model

Understand the timeline asymmetry here, because it drives your entire response posture:

  1. T+0: Developer pushes a key to a public repo (or the key leaks via a compromised package, CI log, paste site, etc.)
  2. T+seconds: Criminal scrapers monitoring GitHub's public event firehose harvest the key. This is fully automated and has been measured in under a minute in published research.
  3. T+minutes: Attackers validate the key, run GetCallerIdentity, enumerate permissions, and begin monetization — typically EC2/Lambda cryptomining or data theft.
  4. T+variable: GitHub's scanner picks up the key, AWS validates it, and the quarantine attaches.

The gap between step 3 and step 4 is your exposure window. The quarantine caps the damage — it does not rewind it. Every response plan must assume the key was used maliciously before the quarantine event, and your CloudTrail analysis window must extend backward from the exposure timestamp, not from the quarantine timestamp.

Also note the blind spot: this pipeline only covers exposure channels AWS/GitHub can see. Keys leaked in private Slack messages, compromised developer workstations, malicious npm packages, or stolen via SSRF against the instance metadata service (169.254.169.254) never trigger a quarantine. This mechanism is a safety net for one leak vector, not a credential-exposure detection strategy.

Exploitation Status

This is not a theoretical concern. Exposed AWS keys are among the most reliably monetized secrets in the underground — automated scanning and abuse is continuous and industrialized. The quarantine mechanism exists precisely because exploitation was faster than human response. There is no CVE here; this is an architectural defense against a human-factor failure mode that remains one of the top cloud intrusion vectors in 2026.

Detection & Response

The highest-fidelity signal in this entire story is the quarantine event itself: AWS attaching AWSCompromisedKeyQuarantine is AWS telling you, with certainty, that a key is burned. If your SOC isn't alerting on this event today, you are flying blind on AWS's own compromise notification.

Sigma Rules

YAML
---
title: AWS Compromised Key Quarantine Policy Attached
description: Detects attachment of the AWSCompromisedKeyQuarantine managed policy to an IAM principal, indicating AWS has confirmed the principal's access key was publicly exposed. This is a high-fidelity compromise notification from AWS itself.
references:
  - https://unit42.paloaltonetworks.com/detecting-exposed-aws-iam-credentials/
  - https://attack.mitre.org/techniques/T1552/
author: Security Arsenal
date: 2026/04/06
id: 3f9c1a72-8b4e-4d21-9c6a-2e7f5b0a91d4
status: experimental
tags:
  - attack.credential_access
  - attack.t1552.001
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    eventSource: iam.amazonaws.com
    eventName:
      - AttachUserPolicy
      - AttachRolePolicy
      - PutUserPolicy
    requestParameters.policyArn|contains: 'AWSCompromisedKeyQuarantine'
  condition: selection
falsepositives:
  - None expected; AWS attaches this policy only after confirming key exposure
level: critical
---
title: AWS IAM Access Key Created Outside Expected Provisioning Path
description: Detects CreateAccessKey operations, which attackers use to establish persistence after gaining access with a compromised credential. Correlate with quarantine events and identity source IPs to identify malicious key generation.
references:
  - https://attack.mitre.org/techniques/T1098/
  - https://unit42.paloaltonetworks.com/detecting-exposed-aws-iam-credentials/
author: Security Arsenal
date: 2026/04/06
id: 6b2e8d41-3c7a-4f19-a5d2-9e0c4b7f3a86
status: experimental
tags:
  - attack.persistence
  - attack.t1098.001
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    eventSource: iam.amazonaws.com
    eventName: CreateAccessKey
  filter_aws_internal:
    sourceIPAddress|endswith:
      - '.amazonaws.com'
  condition: selection and not filter_aws_internal
falsepositives:
  - Legitimate programmatic key rotation by administrators or CI/CD pipelines; baseline expected key-creation identities and source networks
level: medium
---
title: AWS CloudTrail Logging Disabled or Tampered
description: Detects StopLogging, DeleteTrail, or PutEventSelectors changes that reduce CloudTrail visibility. Common attacker behavior following credential compromise to evade detection during cryptomining or data theft.
references:
  - https://attack.mitre.org/techniques/T1562/
  - https://unit42.paloaltonetworks.com/detecting-exposed-aws-iam-credentials/
author: Security Arsenal
date: 2026/04/06
id: 91c4f7b2-5d8e-4a36-b7c1-0f3a6d9e2b58
status: experimental
tags:
  - attack.defense_evasion
  - attack.t1562.008
logsource:
  product: aws
  service: cloudtrail
detection:
  selection:
    eventSource: cloudtrail.amazonaws.com
    eventName:
      - StopLogging
      - DeleteTrail
      - PutEventSelectors
      - UpdateTrail
  condition: selection
falsepositives:
  - Legitimate trail reconfiguration during infrastructure maintenance; alert on the identity and source IP performing the action
level: high

KQL (Microsoft Sentinel)

These queries assume AWS CloudTrail is ingested into Sentinel via the AWS connector (the AWSCloudTrail table). The first query is your tripwire — treat any hit as a confirmed credential compromise and open an incident immediately.

KQL — Microsoft Sentinel / Defender
// Hunt 1: AWS quarantine policy attachment — AWS-confirmed key exposure
AWSCloudTrail
| where EventSource == "iam.amazonaws.com"
| where EventName in ("AttachUserPolicy", "AttachRolePolicy", "PutUserPolicy")
| where tostring(RequestParameters) contains "AWSCompromisedKeyQuarantine"
| extend TargetUser = tostring(parse_json(RequestParameters).userName),
         PolicyArn = tostring(parse_json(RequestParameters).policyArn)
| project TimeGenerated, EventName, TargetUser, PolicyArn, SourceIpAddress, UserIdentityArn, AwsRegion, UserAgent
| sort by TimeGenerated desc;

// Hunt 2: Full activity timeline for a principal in the 24h BEFORE quarantine
// Replace the UPN/ARN after Hunt 1 fires — this is where you find pre-quarantine abuse
let SuspiciousPrincipal = "arn:aws:iam::123456789012:user/EXAMPLE_USER";
AWSCloudTrail
| where UserIdentityArn == SuspiciousPrincipal
| where TimeGenerated > ago(24h)
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated), SourceIPs = make_set(SourceIpAddress) by EventName, EventSource, AwsRegion
| sort by EventCount desc;

// Hunt 3: Cryptomining / monetization indicators — instance launches and Lambda abuse from external IPs
AWSCloudTrail
| where EventName in ("RunInstances", "CreateFunction20150331", "InvokeFunction", "CreateCluster", "CreateService")
| where not (SourceIpAddress endswith "amazonaws.com")
| extend InstanceCount = tostring(parse_json(RequestParameters).instancesSet.items[0].maxCount)
| project TimeGenerated, EventName, UserIdentityArn, SourceIpAddress, AwsRegion, InstanceCount, UserAgent
| sort by TimeGenerated desc;

Velociraptor VQL

Endpoint-side hunting matters here because the root cause is a developer machine or build system leaking keys. This artifact hunts for AWS credential files on endpoints and processes that recently touched them — useful both for finding where the leak originated and for detecting attacker tooling harvesting credentials from compromised hosts.

VQL — Velociraptor
-- Hunt for AWS credential files and processes accessing them (Windows/Linux/macOS)
-- Use for: leak source investigation and detection of credential theft tooling
LET cred_paths = SELECT FullPath, Size, Mtime, Btime
FROM glob(globs=['C:/Users/*/.aws/credentials', '/home/*/.aws/credentials', '/root/.aws/credentials', '/Users/*/.aws/credentials'])
WHERE Mtime > now() - 86400 * 30

LET suspicious_access = SELECT Pid, Name, CommandLine, Exe, Username, CreateTime
FROM pslist()
WHERE CommandLine =~ '(?i)(\.aws[\\/]credentials|aws_access_key_id|aws_secret|AKIA[0-9A-Z]{16})'

SELECT * FROM cred_paths
UNION ALL
SELECT NULL AS FullPath, NULL AS Size, NULL AS Mtime, NULL AS Btime, Pid, Name, CommandLine, Exe, Username, CreateTime FROM suspicious_access

A companion artifact for infrastructure hosts: hunt for unexpected processes reaching the instance metadata service, which is how SSRF and RCE payloads harvest temporary credentials:

VQL — Velociraptor
-- Detect non-standard processes connecting to the EC2 Instance Metadata Service (IMDS)
SELECT Pid, Name, Path, Status, RemoteAddr, RemotePort
FROM netstat()
WHERE RemoteAddr =~ '169.254.169.254'
  AND NOT (Path =~ '(?i)(amazon|ssm|cloud-init|ec2)')

Remediation Script

When a quarantine event fires, this Bash script executes the full containment and evidence-preservation sequence. Run it with credentials that have iam:* read/write and CloudTrail read access — never the compromised principal's credentials.

Bash / Shell
#!/bin/bash
# AWS Compromised Key Containment & Evidence Collection
# Usage: ./aws_key_containment.sh <IAM_USERNAME>
set -euo pipefail
TARGET_USER="$1"
EVIDENCE_DIR="./ir_${TARGET_USER}_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$EVIDENCE_DIR"

echo "[1/6] Checking for AWSCompromisedKeyQuarantine policy attachment..."
aws iam list-attached-user-policies --user-name "$TARGET_USER" \
  --query 'AttachedPolicies[?contains(PolicyArn, `AWSCompromisedKeyQuarantine`)]' \
  | tee "$EVIDENCE_DIR/quarantine_policy.json"

echo "[2/6] Enumerating all access keys for $TARGET_USER..."
KEYS=$(aws iam list-access-keys --user-name "$TARGET_USER" \
  --query 'AccessKeyMetadata[].AccessKeyId' --output text)
echo "$KEYS" | tr '\t' '\n' > "$EVIDENCE_DIR/access_keys.txt"
cat "$EVIDENCE_DIR/access_keys.txt"

echo "[3/6] Deactivating ALL access keys (quarantine is NOT sufficient)..."
for KEY in $KEYS; do
  aws iam get-access-key-last-used --access-key-id "$KEY" \
    > "$EVIDENCE_DIR/last_used_${KEY}.json" 2>/dev/null || true
  aws iam update-access-key --user-name "$TARGET_USER" \
    --access-key-id "$KEY" --status Inactive
  echo "  Deactivated: $KEY"
done

echo "[4/6] Preserving CloudTrail evidence — last 72h of activity for this principal..."
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=Username,AttributeValue="$TARGET_USER" \
  --start-time "$(date -u -d '72 hours ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-72H +%Y-%m-%dT%H:%M:%SZ)" \
  --max-results 50 > "$EVIDENCE_DIR/cloudtrail_events.json"
echo "  Events preserved to $EVIDENCE_DIR/cloudtrail_events.json"

echo "[5/6] Flagging suspicious pre-quarantine API calls..."
grep -Eo '"(RunInstances|CreateAccessKey|CreateUser|AttachUserPolicy|CreateFunction20150331|StopLogging|DeleteTrail|GetCallerIdentity|ListBuckets|GetObject)"' \
  "$EVIDENCE_DIR/cloudtrail_events.json" | sort | uniq -c | sort -rn || echo "  None found in sample"

echo "[6/6] Listing inline policies and group memberships for privilege review..."
aws iam list-user-policies --user-name "$TARGET_USER" > "$EVIDENCE_DIR/inline_policies.json"
aws iam list-groups-for-user --user-name "$TARGET_USER" > "$EVIDENCE_DIR/groups.json"

echo ""
echo "CONTAINMENT COMPLETE. Next steps:"
echo "  1. Review cloudtrail_events.json for abuse BEFORE deactivation timestamp"
echo "  2. Audit last_used files — any usage after known exposure = confirmed misuse"
echo "  3. Terminate any unauthorized EC2/Lambda/ECS resources created by this principal"
echo "  4. Delete (not just deactivate) keys after forensics sign-off, then issue new ones via your secrets manager"
echo "  5. Root-cause the leak: find the commit/repo/system that exposed the key"

Remediation: The Full Playbook

Immediate (0–1 hour):

  1. Confirm the quarantine event in CloudTrail — verify AttachUserPolicy with the quarantine ARN. If AWS quarantined it, the exposure is confirmed; do not waste time debating validity.
  2. Deactivate the exposed key immediately. The quarantine policy is a partial deny, not revocation. aws iam update-access-key --status Inactive — then delete after forensics.
  3. Pull CloudTrail for the principal covering at least 72 hours before the quarantine timestamp. Look specifically for GetCallerIdentity (attacker validation), RunInstances, CreateAccessKey, and StopLogging.
  4. Terminate unauthorized resources. Sort EC2, Lambda, ECS, and Lightsail resources by launch time across all regions — cryptominers frequently deploy in unused regions.

Short-term (1–24 hours): 5. Root-cause the leak. Identify the commit, repo, CI log, or system that exposed the key. GitHub secret scanning alerts (if enabled) will pinpoint the exact file and commit. Force-push removal is not sufficient — the key must be rotated; assume the commit is archived by scrapers forever. 6. Rotate any secrets the compromised principal could read. Check whether the key's permissions allowed secretsmanager:GetSecretValue, ssm:GetParameter, or S3 reads of sensitive buckets. Assume those secrets are also compromised and rotate them. 7. Review the quarantine policy version. Confirm you're benefiting from the latest AWSCompromisedKeyQuarantineV3 protections and understand precisely what remains permitted for the quarantined principal.

Structural (the real fix): 8. Eliminate long-term IAM user keys wherever possible. Migrate to IAM Roles Anywhere, IAM Identity Center with short-lived credentials, or OIDC federation from GitHub Actions/GitLab CI. Temporary ASIA credentials expire in hours and dramatically shrink the monetization window. 9. Enable GitHub secret scanning with push protection on all repos — push protection blocks the commit before the key ever reaches the server, collapsing the exposure window to zero. 10. Deploy SCP guardrails at the AWS Organizations level: deny EC2 launches in unused regions, require MFA for IAM mutations, and alert on CreateAccessKey via EventBridge → your SIEM. 11. Alert on the quarantine event itself. Wire the CloudTrail AttachUserPolicy + AWSCompromisedKeyQuarantine pattern into your SIEM (the Sigma rule above) as a critical-severity, page-the-on-call alert. This is AWS handing you a confirmed incident — treat it like one.

The Bottom Line

AWS's quarantine mechanism and GitHub's secret scanning pipeline represent one of the better ecosystem-scale defenses in cloud security — they compress an exposure-to-lockdown timeline that used to be measured in days down to minutes. But as Unit 42's analysis makes clear, this is a containment floor, not a response. The organizations that get hurt are the ones who see the AWS notification, breathe a sigh of relief, and close the ticket. The ones that get it right treat every quarantine event as a confirmed intrusion: full CloudTrail forensics, key revocation, resource auditing, secret rotation, and pipeline root-cause — every time.

Related Resources

Security Arsenal Managed SOC Services AlertMonitor Platform Book a SOC Assessment soc-mdr Intel Hub

Is your security operations ready?

Get a free SOC assessment or see how AlertMonitor cuts through alert noise with automated triage.

Exposed AWS IAM Credentials: How AWS Quarantine Policies Work — and the Detection and Remediation Playbook Defenders Still Need | Security Arsenal | Security Arsenal