Skip to content

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, or CreateAccessKey from unusual IP or region
  • [ ] S3/GCS/Blob: mass GetObject or 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.B or 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: true condition 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