Cloud Breach Playbook (AWS / Azure / GCP)¶
Preserve Logs Before Any Containment Action
Cloud provider logs (CloudTrail, Activity Log, Audit Log) may be overwritten or may not be retained by default. Preserve logs to immutable storage immediately — before disabling accounts or modifying resources.
Metadata¶
| Field | Value |
|---|---|
| Playbook ID | IR-PB-003 |
| Severity | Critical (P1) |
| RTO — Containment | < 2 hours |
| Owner | IR Lead + Cloud Security Engineer |
| Co-owners | DevOps / Platform, Legal, Compliance |
| Last Reviewed | 2025-01-01 |
Trigger Conditions¶
Activate this playbook on any of the following:
- [ ] CloudTrail / Activity Log: IAM user/role created by unexpected principal
- [ ] CloudTrail:
AssumeRole,GetCallerIdentity, orCreateAccessKeyfrom unusual IP or region - [ ] S3/GCS/Blob: mass
GetObjector download activity (>1GB in short window) - [ ] Unusual region activity — resources created in regions not in approved baseline
- [ ] Security Hub / Defender for Cloud / Security Command Center: critical finding
- [ ] GuardDuty:
UnauthorizedAccess:IAMUser/ConsoleLoginSuccess.Bor cryptocurrency mining alert - [ ] SIEM: access key used from IP not associated with known developer/CI pipeline
- [ ] Billing alert spike — unexpected EC2/GPU/egress charges (crypto mining indicator)
- [ ] DLP alert on cloud storage bucket — unexpected public ACL or presigned URL generation
Decision Tree¶
flowchart TD
A([Cloud Breach Trigger]) --> B{Which cloud\nplatform?}
B -- AWS --> C[Pull CloudTrail\nGuardDuty findings]
B -- Azure --> D[Pull Activity Log\nMicrosoft Defender alerts]
B -- GCP --> E[Pull Cloud Audit Logs\nSCC findings]
C & D & E --> F{Are credentials\nconfirmed compromised?}
F -- Yes --> G[Rotate/disable keys\nRevoke active sessions\nPreserve logs first]
F -- Investigating --> H[Snapshot logs\nto immutable storage]
G --> I{Is data exfiltration\nconfirmed?}
H --> I
I -- Yes --> J[Notify Legal\nRegulatory clock starts\nQuantify data scope]
I -- No / Unknown --> K[Audit all actions\nby compromised principal]
J --> K
K --> L{Persistence\nmechanisms found?}
L -- Yes --> M[Remove backdoors\nLambda/Functions\nEC2 user-data\nSSM params]
L -- No --> N[Validate IAM\nleast privilege\nRe-enable with MFA]
M --> N
N --> O([Post-Incident\nRegulatory Notifications]) Phase 1 — Contain (0–2 Hours)¶
1.1 Preserve Logs to Immutable Storage (Do This First)¶
AWS:
# Copy CloudTrail logs to isolated S3 bucket with Object Lock (WORM)
aws s3 cp s3://your-cloudtrail-bucket/ s3://ir-evidence-bucket/ \
--recursive \
--region us-east-1 \
--sse aws:kms
# Enable Object Lock on evidence bucket (if not already set)
aws s3api put-object-lock-configuration \
--bucket ir-evidence-bucket \
--object-lock-configuration '{"ObjectLockEnabled":"Enabled","Rule":{"DefaultRetention":{"Mode":"COMPLIANCE","Days":365}}}'
# Export GuardDuty findings to S3
aws guardduty list-findings --detector-id <DETECTOR_ID> --query 'FindingIds' \
| xargs aws guardduty get-findings --detector-id <DETECTOR_ID> --finding-ids \
> guardduty_findings_$(date +%Y%m%d_%H%M%S).json
Azure:
# Export Activity Log to Log Analytics or Storage Account
az monitor activity-log list \
--start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ) \
--end-time $(date -u +%Y-%m-%dT%H:%M:%SZ) \
--output json > azure_activity_log_$(date +%Y%m%d).json
# Export Defender for Cloud alerts
az security alert list --output json > defender_alerts_$(date +%Y%m%d).json
GCP:
# Export Cloud Audit Logs
gcloud logging read "logName=projects/PROJECT_ID/logs/cloudaudit.googleapis.com" \
--freshness=7d \
--format=json > gcp_audit_logs_$(date +%Y%m%d).json
1.2 Identify the Compromised Principal¶
AWS:
# Determine current caller identity (run from attacker session if accessible, or from your own)
aws sts get-caller-identity
# Search CloudTrail for the compromised access key activity
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA... \
--start-time $(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ) \
--output json | jq '.Events[] | {time: .EventTime, name: .EventName, ip: .CloudTrailEvent | fromjson | .sourceIPAddress}'
# List all access keys for a user
aws iam list-access-keys --user-name <USERNAME>
1.3 Disable / Rotate Compromised Credentials¶
AWS:
# Immediately deactivate compromised access key
aws iam update-access-key \
--access-key-id AKIA... \
--status Inactive \
--user-name <USERNAME>
# Attach deny-all policy to compromised IAM user (belt-and-suspenders)
aws iam put-user-policy --user-name <USERNAME> \
--policy-name EmergencyDenyAll \
--policy-document '{"Version":"2012-10-17","Statement":[{"Effect":"Deny","Action":"*","Resource":"*"}]}'
# If role-based: revoke all active sessions by updating the trust policy
# (Add a condition that requires a specific tag or session name not used by attacker)
# Rotate the key (create new, update CI/CD secrets, then delete old)
aws iam create-access-key --user-name <USERNAME>
# Update all consumers of this key with new credentials
aws iam delete-access-key --access-key-id AKIA... --user-name <USERNAME>
Azure:
# Disable compromised service principal
az ad sp update --id <SP_OBJECT_ID> --set "accountEnabled=false"
# Revoke all tokens for a user
az rest --method POST \
--uri "https://graph.microsoft.com/v1.0/users/<USER_ID>/revokeSignInSessions"
# Reset service principal secret
az ad sp credential reset --id <SP_OBJECT_ID>
GCP:
# Disable compromised service account
gcloud iam service-accounts disable \
compromised-sa@project-id.iam.gserviceaccount.com
# Delete compromised key
gcloud iam service-accounts keys delete KEY_ID \
--iam-account=compromised-sa@project-id.iam.gserviceaccount.com
Phase 2 — Eradicate (2–24 Hours)¶
2.1 Audit All Actions by Compromised Principal¶
AWS — Comprehensive CloudTrail Query:
# Pull all API calls by compromised access key (last 30 days)
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=AccessKeyId,AttributeValue=AKIA... \
--max-results 1000 \
--output json > compromised_key_activity.json
# Extract unique event names (what did the attacker do?)
jq -r '.Events[].EventName' compromised_key_activity.json | sort | uniq -c | sort -rn
# Check for IAM changes made by attacker
jq '.Events[] | select(.EventName | test("Create|Attach|Put|Add")) |
{time: .EventTime, action: .EventName, resources: .Resources}' compromised_key_activity.json
2.2 Persistence Mechanism Hunting¶
AWS Persistence Checklist:
- [ ] Lambda functions: check for new Lambda functions or modified existing ones
# List Lambda functions modified in last 7 days
aws lambda list-functions --query \
"Functions[?LastModified >= '$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S)'].{Name:FunctionName,Modified:LastModified}" \
--output table
# Review Lambda environment variables for exfil mechanisms
aws lambda get-function-configuration --function-name <FUNCTION_NAME> \
| jq '.Environment.Variables'
- [ ] EC2 User Data: check for malicious startup scripts
# Get user data for all running instances
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,Tags]' --output json
aws ec2 describe-instance-attribute --instance-id i-xxxx --attribute userData \
| jq -r '.UserData.Value' | base64 --decode
- [ ] SSM Parameter Store: check for exfiltrated secrets or malicious parameters
aws ssm describe-parameters --output json | jq '.Parameters[] | {Name, LastModifiedDate, LastModifiedUser}'
- [ ] IAM backdoor roles/users created by attacker:
# List IAM users created in last 7 days
aws iam list-users | jq --arg date "$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S)" \
'.Users[] | select(.CreateDate >= $date) | {User: .UserName, Created: .CreateDate}'
# List IAM roles created in last 7 days
aws iam list-roles | jq --arg date "$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%S)" \
'.Roles[] | select(.CreateDate >= $date) | {Role: .RoleName, Created: .CreateDate}'
- [ ] CloudFormation / Terraform: review stacks modified by attacker
- [ ] EventBridge / CloudWatch Events rules: check for new rules
- [ ] Secrets Manager: check for access to secrets and rotation status
2.3 Cloud Artifacts to Collect¶
| Artifact | AWS | Azure | GCP |
|---|---|---|---|
| API call logs | CloudTrail (all regions) | Activity Log + Diagnostic Logs | Cloud Audit Logs |
| Network flow logs | VPC Flow Logs | NSG Flow Logs | VPC Flow Logs |
| Object storage access | S3 Server Access Logs / S3 CloudTrail | Storage Account Log Analytics | GCS Data Access Logs |
| Infrastructure changes | AWS Config history | Azure Resource Graph | Cloud Asset Inventory |
| Container logs | CloudWatch / ECS/EKS logs | Container Insights | Cloud Logging (GKE) |
| DNS queries | Route53 Resolver logs | Azure DNS Analytics | Cloud DNS Logs |
| Identity events | IAM Access Analyzer | Entra ID Sign-in Logs | Cloud Identity Logs |
Multi-Cloud Variant Steps¶
AWS-Specific¶
# Enable AWS Config (if not already enabled) to capture all resource changes
aws configservice start-configuration-recorder --configuration-recorder-name default
# Use Access Analyzer to find resources shared externally
aws accessanalyzer list-findings --analyzer-arn arn:aws:access-analyzer:... \
--filter '{"resourceType":{"eq":["AWS::S3::Bucket"]}}'
# Check for public S3 buckets
aws s3api list-buckets --query 'Buckets[].Name' --output text | \
xargs -I {} aws s3api get-bucket-acl --bucket {} 2>/dev/null
Azure-Specific¶
# Check for new role assignments (last 7 days)
az role assignment list --all --query \
"[?createdOn >= '$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)']" \
--output table
# Check for new app registrations
az ad app list --query "[?createdDateTime >= '$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)']" \
--output table
GCP-Specific¶
# Check for new service account key creations
gcloud logging read \
'protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
--freshness=7d --format=json
# Check for IAM policy changes
gcloud logging read \
'protoPayload.methodName="SetIamPolicy"' \
--freshness=7d --format=json | jq '.[] | {time: .timestamp, resource: .resource.labels}'
Phase 3 — Recover¶
3.1 Credential Rotation¶
- [ ] Rotate ALL access keys for affected IAM users/service accounts
- [ ] Rotate all secrets in Secrets Manager / Key Vault / Secret Manager touched by attacker
- [ ] Update all CI/CD pipelines with new credentials
- [ ] Rotate database passwords if RDS/Cloud SQL was in scope
- [ ] Re-issue TLS certificates if certificate authorities were accessed
3.2 Re-enable with Least Privilege¶
# AWS: Review and tighten IAM policies using Access Advisor
aws iam generate-service-last-accessed-details --arn arn:aws:iam::ACCOUNT:user/USERNAME
# Wait for job to complete, then retrieve
aws iam get-service-last-accessed-details --job-id <JOB_ID>
# Remove permissions for services not accessed in last 90 days
- [ ] Enable MFA requirement for all human IAM users
- [ ] Enforce
aws:MultiFactorAuthPresent: truecondition on sensitive policy actions - [ ] Enable SCPs (AWS) / Azure Policy / Org Policy (GCP) to enforce guardrails
Regulatory Notification Requirements¶
| Regulation | Trigger | Deadline | Recipient |
|---|---|---|---|
| GDPR | PII of EU subjects in compromised storage | 72 hours | Lead DPA (Supervisory Authority) |
| HIPAA | PHI in compromised cloud storage or compute | 60 days | HHS OCR + affected individuals |
| SOC 2 | Customer data breach — notify auditor | Per contract | Auditor + affected customers |
| NYDFS 500 | Cybersecurity event on covered entity | 72 hours | NYDFS via online portal |
| SEC (public) | Material cybersecurity incident | 4 business days | Form 8-K Item 1.05 |
| PCI DSS | Cardholder data environment compromised | Immediately | Card brands (Visa/MC) + acquirer |
Recovery Checklist¶
Detection & Preservation¶
- [ ] Logs preserved to immutable storage before any changes
- [ ] Compromised principal identified (user, role, or service account)
- [ ] Blast radius determined (regions, services, data)
Contain¶
- [ ] Compromised credentials disabled/rotated
- [ ] Active sessions revoked
- [ ] Deny-all policy applied to compromised principal
Eradicate¶
- [ ] All attacker-created IAM resources removed
- [ ] Persistence mechanisms identified and removed (Lambda, EC2 user-data, SSM, EventBridge)
- [ ] All attacker-accessed secrets rotated
- [ ] CloudFormation/Terraform state verified clean
Recover¶
- [ ] Least-privilege IAM policy review complete
- [ ] MFA enforced on all human identities
- [ ] CI/CD pipelines updated with new credentials
- [ ] Security posture baselines re-validated
Post-Incident¶
- [ ] Regulatory notifications sent (with dates logged)
- [ ] Cloud CSPM tool (Security Hub / Defender for Cloud / SCC) findings resolved
- [ ] Architecture review — eliminate long-lived access keys
- [ ] Threat model updated based on attack path