incident response

Performing Cloud Incident Containment Procedures

Execute cloud-native incident containment across AWS, Azure, and GCP by isolating compromised resources, revoking credentials, preserving forensic evidence, and applying security group restrictions to prevent lateral movement.

awsazurecloud-forensicscloud-securitycredential-revocationgcpincident-containmentnetwork-isolation
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Cloud incident containment requires cloud-native approaches that differ significantly from traditional on-premises response. Containment procedures must leverage platform-specific controls including security groups, IAM policies, network ACLs, and service-level isolation to restrict compromised resources while preserving forensic evidence. According to the 2025 Unit 42 Global Incident Response Report, responding to cloud incidents requires understanding shared responsibility models, ephemeral infrastructure, and API-driven operations. Effective containment involves credential revocation, resource isolation, evidence snapshot creation, and automated response playbook execution.

When to Use

  • When conducting security assessments that involve performing cloud incident containment procedures
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Familiarity with incident response concepts and tools
  • Access to a test or lab environment for safe execution
  • Python 3.8+ with required dependencies installed
  • Appropriate authorization for any testing activities

AWS Containment Procedures

1. Credential Compromise Containment

# Disable compromised IAM user access keys
aws iam update-access-key --user-name compromised-user \
  --access-key-id AKIA... --status Inactive
 
# List and disable all access keys for user
aws iam list-access-keys --user-name compromised-user
aws iam delete-access-key --user-name compromised-user --access-key-id AKIA...
 
# Attach deny-all policy to compromised user
aws iam put-user-policy --user-name compromised-user \
  --policy-name DenyAll \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*"
    }]
  }'
 
# Revoke all active sessions for IAM role
aws iam put-role-policy --role-name compromised-role \
  --policy-name RevokeOldSessions \
  --policy-document '{
    "Version": "2012-10-17",
    "Statement": [{
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "DateLessThan": {"aws:TokenIssueTime": "'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}
      }
    }]
  }'
 
# Invalidate temporary credentials by updating role trust policy
aws iam update-assume-role-policy --role-name compromised-role \
  --policy-document '{"Version":"2012-10-17","Statement":[]}'

2. EC2 Instance Isolation

# Create quarantine security group (no inbound, no outbound)
aws ec2 create-security-group --group-name quarantine-sg \
  --description "Quarantine - No traffic allowed" --vpc-id vpc-xxxxx
 
# Remove all rules from quarantine SG (default allows outbound)
aws ec2 revoke-security-group-egress --group-id sg-quarantine \
  --ip-permissions '[{"IpProtocol":"-1","FromPort":-1,"ToPort":-1,"IpRanges":[{"CidrIp":"0.0.0.0/0"}]}]'
 
# Take forensic snapshot BEFORE containment
aws ec2 create-snapshot --volume-id vol-xxxxx \
  --description "Forensic snapshot - IR Case 2025-001" \
  --tag-specifications 'ResourceType=snapshot,Tags=[{Key=IR-Case,Value=2025-001}]'
 
# Apply quarantine security group to compromised instance
aws ec2 modify-instance-attribute --instance-id i-xxxxx \
  --groups sg-quarantine
 
# Tag instance as compromised
aws ec2 create-tags --resources i-xxxxx \
  --tags Key=IR-Status,Value=Contained Key=IR-Case,Value=2025-001
 
# Capture memory (if SSM agent available)
aws ssm send-command --instance-ids i-xxxxx \
  --document-name "AWS-RunShellScript" \
  --parameters 'commands=["dd if=/dev/mem of=/tmp/memory.dump bs=1M"]'

3. S3 Bucket Containment

# Block all public access
aws s3api put-public-access-block --bucket compromised-bucket \
  --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
 
# Apply deny policy to bucket
aws s3api put-bucket-policy --bucket compromised-bucket \
  --policy '{
    "Version": "2012-10-17",
    "Statement": [{
      "Sid": "DenyAllExceptForensics",
      "Effect": "Deny",
      "NotPrincipal": {"AWS": "arn:aws:iam::ACCOUNT:role/IR-Forensics"},
      "Action": "s3:*",
      "Resource": ["arn:aws:s3:::compromised-bucket","arn:aws:s3:::compromised-bucket/*"]
    }]
  }'
 
# Enable versioning to preserve evidence
aws s3api put-bucket-versioning --bucket compromised-bucket \
  --versioning-configuration Status=Enabled
 
# Enable Object Lock for evidence preservation
aws s3api put-object-lock-configuration --bucket evidence-bucket \
  --object-lock-configuration '{
    "ObjectLockEnabled": "Enabled",
    "Rule": {"DefaultRetention": {"Mode": "COMPLIANCE", "Days": 365}}
  }'

4. Lambda Function Containment

# Set reserved concurrency to 0 (stops all invocations)
aws lambda put-function-concurrency --function-name compromised-function \
  --reserved-concurrent-executions 0
 
# Remove all event source mappings
aws lambda list-event-source-mappings --function-name compromised-function
aws lambda delete-event-source-mapping --uuid mapping-uuid

Azure Containment Procedures

1. Identity Containment

# Revoke all user sessions
Revoke-AzureADUserAllRefreshToken -ObjectId "user-object-id"
 
# Disable user account
Set-AzureADUser -ObjectId "user-object-id" -AccountEnabled $false
 
# Reset user password
Set-AzureADUserPassword -ObjectId "user-object-id" -Password (
  ConvertTo-SecureString "TempP@ss!" -AsPlainText -Force
) -ForceChangePasswordNextLogin $true
 
# Block sign-in via Conditional Access (emergency policy)
# Create policy blocking user from all cloud apps
 
# Revoke Azure AD application consent
Remove-AzureADServiceAppRoleAssignment -ObjectId "sp-object-id" \
  -AppRoleAssignmentId "assignment-id"

2. VM Isolation

# Create Network Security Group with deny-all rules
$nsg = New-AzNetworkSecurityGroup -ResourceGroupName "rg" -Location "eastus" `
  -Name "quarantine-nsg" `
  -SecurityRules @(
    New-AzNetworkSecurityRuleConfig -Name "DenyAllInbound" -Protocol * `
      -Direction Inbound -Priority 100 -SourceAddressPrefix * `
      -SourcePortRange * -DestinationAddressPrefix * `
      -DestinationPortRange * -Access Deny,
    New-AzNetworkSecurityRuleConfig -Name "DenyAllOutbound" -Protocol * `
      -Direction Outbound -Priority 100 -SourceAddressPrefix * `
      -SourcePortRange * -DestinationAddressPrefix * `
      -DestinationPortRange * -Access Deny
  )
 
# Take disk snapshot for forensics
$vm = Get-AzVM -ResourceGroupName "rg" -Name "compromised-vm"
$snapshotConfig = New-AzSnapshotConfig -SourceUri $vm.StorageProfile.OsDisk.ManagedDisk.Id `
  -Location "eastus" -CreateOption Copy
New-AzSnapshot -ResourceGroupName "rg" -SnapshotName "forensic-snap" -Snapshot $snapshotConfig
 
# Apply quarantine NSG to VM NIC
$nic = Get-AzNetworkInterface -ResourceGroupName "rg" -Name "compromised-nic"
$nic.NetworkSecurityGroup = $nsg
Set-AzNetworkInterface -NetworkInterface $nic

3. Storage Account Containment

# Remove network access
Update-AzStorageAccountNetworkRuleSet -ResourceGroupName "rg" `
  -Name "storageaccount" -DefaultAction Deny
 
# Regenerate access keys
New-AzStorageAccountKey -ResourceGroupName "rg" -Name "storageaccount" -KeyName key1
New-AzStorageAccountKey -ResourceGroupName "rg" -Name "storageaccount" -KeyName key2
 
# Revoke all SAS tokens (by rotating keys)
# Enable immutability for evidence preservation

GCP Containment Procedures

1. IAM Containment

# Remove all IAM bindings for compromised service account
gcloud projects get-iam-policy PROJECT_ID --format=json > policy.json
# Edit policy.json to remove compromised account bindings
gcloud projects set-iam-policy PROJECT_ID policy.json
 
# Disable service account
gcloud iam service-accounts disable SA_EMAIL
 
# Delete service account keys
gcloud iam service-accounts keys list --iam-account SA_EMAIL
gcloud iam service-accounts keys delete KEY_ID --iam-account SA_EMAIL

2. Compute Instance Isolation

# Create forensic snapshot
gcloud compute disks snapshot compromised-disk \
  --snapshot-names forensic-snap-$(date +%Y%m%d) \
  --zone us-central1-a
 
# Apply firewall rule to deny all traffic
gcloud compute firewall-rules create quarantine-deny-all \
  --network default --action DENY --rules all \
  --target-tags quarantine --priority 0
 
# Tag compromised instance
gcloud compute instances add-tags compromised-instance \
  --tags quarantine --zone us-central1-a
 
# Remove external IP
gcloud compute instances delete-access-config compromised-instance \
  --access-config-name "External NAT" --zone us-central1-a

Evidence Preservation Best Practices

  1. Always snapshot before containment - Create disk/volume snapshots before network isolation
  2. Preserve CloudTrail/Activity Logs - Copy logs to write-protected storage
  3. Document all actions - Timestamp every containment step taken
  4. Use break-glass procedures - Pre-establish emergency access for IR team
  5. Maintain forensic chain of custody - Hash all evidence artifacts

MITRE ATT&CK Cloud Techniques

Technique Containment Action
T1078 - Valid Accounts Disable accounts, revoke tokens
T1530 - Data from Cloud Storage Lock down bucket/storage policies
T1537 - Transfer to Cloud Account Block cross-account access
T1578 - Modify Cloud Compute Isolate instances, snapshot disks
T1552 - Unsecured Credentials Rotate all access keys and secrets

References

Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md5.3 KB

API Reference: AWS Cloud Incident Containment

Libraries Used

Library Purpose
boto3 AWS SDK for EC2, IAM, Security Groups, and CloudTrail
json Parse and log containment actions
datetime Timestamp containment events

Installation

pip install boto3

Authentication

import boto3
import os
 
session = boto3.Session(
    aws_access_key_id=os.environ.get("AWS_ACCESS_KEY_ID"),
    aws_secret_access_key=os.environ.get("AWS_SECRET_ACCESS_KEY"),
    region_name=os.environ.get("AWS_REGION", "us-east-1"),
)
 
ec2 = session.client("ec2")
iam = session.client("iam")

Containment Actions

Isolate EC2 Instance (Security Group Quarantine)

def isolate_instance(instance_id):
    """Replace instance security groups with a quarantine SG that blocks all traffic."""
    # Create quarantine SG if it doesn't exist
    vpc_id = ec2.describe_instances(
        InstanceIds=[instance_id]
    )["Reservations"][0]["Instances"][0]["VpcId"]
 
    try:
        quarantine_sg = ec2.create_security_group(
            GroupName="quarantine-no-access",
            Description="IR Quarantine — blocks all inbound/outbound",
            VpcId=vpc_id,
        )
        sg_id = quarantine_sg["GroupId"]
        # Revoke default outbound rule
        ec2.revoke_security_group_egress(
            GroupId=sg_id,
            IpPermissions=[{"IpProtocol": "-1", "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}],
        )
    except ec2.exceptions.ClientError:
        # SG already exists
        sgs = ec2.describe_security_groups(
            Filters=[{"Name": "group-name", "Values": ["quarantine-no-access"]}]
        )
        sg_id = sgs["SecurityGroups"][0]["GroupId"]
 
    # Apply quarantine SG (replaces all existing SGs)
    ec2.modify_instance_attribute(
        InstanceId=instance_id,
        Groups=[sg_id],
    )
    return {"instance_id": instance_id, "quarantine_sg": sg_id, "action": "isolated"}

Disable IAM Access Keys

def disable_user_access_keys(username):
    """Disable all access keys for a compromised IAM user."""
    keys = iam.list_access_keys(UserName=username)
    disabled = []
    for key in keys["AccessKeyMetadata"]:
        if key["Status"] == "Active":
            iam.update_access_key(
                UserName=username,
                AccessKeyId=key["AccessKeyId"],
                Status="Inactive",
            )
            disabled.append(key["AccessKeyId"])
    return {"username": username, "keys_disabled": disabled}

Revoke IAM Role Sessions

def revoke_role_sessions(role_name):
    """Revoke all active sessions for an IAM role."""
    iam.put_role_policy(
        RoleName=role_name,
        PolicyName="RevokeOlderSessions",
        PolicyDocument=json.dumps({
            "Version": "2012-10-17",
            "Statement": [{
                "Effect": "Deny",
                "Action": "*",
                "Resource": "*",
                "Condition": {
                    "DateLessThan": {
                        "aws:TokenIssueTime": datetime.utcnow().strftime("%Y-%m-%dT%H:%M:%SZ")
                    }
                }
            }]
        }),
    )
    return {"role": role_name, "action": "sessions_revoked"}

Snapshot EBS Volume for Forensics

def snapshot_instance_volumes(instance_id):
    """Create forensic snapshots of all attached EBS volumes."""
    instance = ec2.describe_instances(InstanceIds=[instance_id])
    volumes = instance["Reservations"][0]["Instances"][0].get("BlockDeviceMappings", [])
    snapshots = []
    for vol in volumes:
        vol_id = vol["Ebs"]["VolumeId"]
        snap = ec2.create_snapshot(
            VolumeId=vol_id,
            Description=f"IR forensic snapshot — {instance_id}{vol_id}",
            TagSpecifications=[{
                "ResourceType": "snapshot",
                "Tags": [
                    {"Key": "Purpose", "Value": "incident-response"},
                    {"Key": "SourceInstance", "Value": instance_id},
                ]
            }],
        )
        snapshots.append({"volume_id": vol_id, "snapshot_id": snap["SnapshotId"]})
    return snapshots

Stop Instance (Preserve State)

def stop_instance(instance_id):
    """Stop instance without terminating to preserve memory and disk."""
    ec2.stop_instances(InstanceIds=[instance_id])
    return {"instance_id": instance_id, "action": "stopped"}

Block Public S3 Bucket Access

s3 = session.client("s3")
 
def block_public_bucket(bucket_name):
    s3.put_public_access_block(
        Bucket=bucket_name,
        PublicAccessBlockConfiguration={
            "BlockPublicAcls": True,
            "IgnorePublicAcls": True,
            "BlockPublicPolicy": True,
            "RestrictPublicBuckets": True,
        },
    )
    return {"bucket": bucket_name, "action": "public_access_blocked"}

Output Format

{
  "incident_id": "IR-2025-001",
  "containment_time": "2025-01-15T10:30:00Z",
  "actions_taken": [
    {"action": "isolate_instance", "target": "i-0abc123", "status": "success"},
    {"action": "disable_access_keys", "target": "compromised-user", "keys_disabled": 2},
    {"action": "snapshot_volumes", "target": "i-0abc123", "snapshots": 2},
    {"action": "stop_instance", "target": "i-0abc123", "status": "success"}
  ]
}
standards.md1.1 KB

Standards for Cloud Incident Containment

NIST SP 800-61 Rev 2 - Incident Handling Guide

  • Containment strategies for cloud environments
  • Evidence preservation in ephemeral infrastructure

CSA Cloud Incident Response Framework

  • Cloud Security Alliance incident response procedures
  • Shared responsibility model for incident handling
  • Multi-cloud containment strategies

AWS Well-Architected Framework - Security Pillar

Microsoft Cloud Security Benchmark

GCP Security Best Practices

  • Cloud Armor and VPC Service Controls
  • Security Command Center integration
  • Chronicle SIEM for cloud forensics

MITRE ATT&CK Cloud Matrix

workflows.md2.5 KB

Cloud Incident Containment Workflows

Workflow 1: AWS Credential Compromise Response

START: Compromised AWS Credentials Detected
  |
  v
[Identify Scope]
  |-- Which IAM user/role is compromised?
  |-- What permissions does it have?
  |-- Review CloudTrail for unauthorized actions
  |
  v
[Immediate Containment]
  |-- Disable all access keys
  |-- Attach deny-all inline policy
  |-- Revoke active sessions (date condition)
  |-- Update role trust policy if needed
  |
  v
[Evidence Preservation]
  |-- Export CloudTrail logs to S3 with Object Lock
  |-- Snapshot any accessed resources
  |-- Document all API calls by compromised identity
  |
  v
[Impact Assessment]
  |-- What resources were accessed?
  |-- Was data exfiltrated?
  |-- Were new resources created?
  |-- Were other accounts compromised?
  |
  v
[Remediation]
  |-- Rotate all credentials
  |-- Remove unauthorized resources
  |-- Update IAM policies
  |-- Enable MFA enforcement
  |
  v
END: Containment Complete

Workflow 2: Cloud VM Compromise Response

START: Compromised Cloud VM Detected
  |
  v
[Preserve Evidence First]
  |-- Create disk snapshot immediately
  |-- Capture instance metadata
  |-- Export relevant logs
  |
  v
[Network Isolation]
  |-- Apply quarantine security group/NSG
  |-- Remove public IP addresses
  |-- Block outbound traffic
  |-- Maintain forensic access only
  |
  v
[Assess Blast Radius]
  |-- Check lateral movement indicators
  |-- Review IAM role attached to instance
  |-- Check for data access to other services
  |
  v
[Forensic Analysis]
  |-- Mount snapshot to forensic workstation
  |-- Analyze disk for malware/tools
  |-- Review instance logs
  |
  v
[Recovery]
  |-- Rebuild from known-good image
  |-- Apply security hardening
  |-- Restore from clean backup
  |
  v
END: VM Contained and Rebuilt

Workflow 3: Multi-Cloud Containment

START: Multi-Cloud Incident
  |
  v
[Identify Affected Cloud Platforms]
  |-- AWS accounts affected?
  |-- Azure subscriptions affected?
  |-- GCP projects affected?
  |
  v
[Parallel Containment]
  |-- AWS: SecurityHub + GuardDuty response
  |-- Azure: Defender + Sentinel playbooks
  |-- GCP: SCC + Chronicle response
  |
  v
[Cross-Cloud Credential Check]
  |-- Shared credentials between platforms?
  |-- Federated identity compromise?
  |-- Third-party integrations affected?
  |
  v
[Unified Evidence Collection]
  |-- Centralize logs from all platforms
  |-- Normalize timestamps to UTC
  |-- Build cross-cloud timeline
  |
  v
END: Multi-Cloud Containment Complete

Scripts 2

agent.py12.4 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""AWS cloud incident containment agent.

Automates incident containment procedures in AWS environments including
EC2 instance isolation, IAM credential revocation, security group lockdown,
S3 bucket access restriction, and forensic snapshot creation using boto3.
"""
import argparse
import json
import os
import sys
from datetime import datetime, timezone

try:
    import boto3
    from botocore.exceptions import ClientError
except ImportError:
    print("[!] 'boto3' library required: pip install boto3", file=sys.stderr)
    sys.exit(1)


def get_session(profile=None, region=None):
    """Create a boto3 session."""
    kwargs = {}
    if profile:
        kwargs["profile_name"] = profile
    if region:
        kwargs["region_name"] = region
    return boto3.Session(**kwargs)


def isolate_ec2_instance(session, instance_id, vpc_id=None):
    """Isolate an EC2 instance by replacing security groups with a deny-all SG."""
    ec2 = session.client("ec2")
    findings = []
    print(f"[*] Isolating EC2 instance: {instance_id}")

    # Get current instance details
    try:
        resp = ec2.describe_instances(InstanceIds=[instance_id])
        instance = resp["Reservations"][0]["Instances"][0]
        current_sgs = [sg["GroupId"] for sg in instance.get("SecurityGroups", [])]
        instance_vpc = instance.get("VpcId", vpc_id)
        findings.append({"action": "describe_instance", "status": "OK",
                         "detail": f"Current SGs: {current_sgs}"})
    except ClientError as e:
        findings.append({"action": "describe_instance", "status": "FAIL",
                         "severity": "CRITICAL", "detail": str(e)})
        return findings

    # Create or find isolation security group
    isolation_sg_name = f"incident-isolation-{instance_id[:8]}"
    isolation_sg_id = None
    try:
        existing = ec2.describe_security_groups(
            Filters=[{"Name": "group-name", "Values": [isolation_sg_name]},
                     {"Name": "vpc-id", "Values": [instance_vpc]}]
        )
        if existing["SecurityGroups"]:
            isolation_sg_id = existing["SecurityGroups"][0]["GroupId"]
        else:
            resp = ec2.create_security_group(
                GroupName=isolation_sg_name,
                Description=f"Incident isolation SG for {instance_id}",
                VpcId=instance_vpc,
            )
            isolation_sg_id = resp["GroupId"]
            # Revoke default egress rule
            ec2.revoke_security_group_egress(
                GroupId=isolation_sg_id,
                IpPermissions=[{"IpProtocol": "-1", "IpRanges": [{"CidrIp": "0.0.0.0/0"}]}],
            )
        findings.append({"action": "create_isolation_sg", "status": "OK",
                         "detail": f"SG: {isolation_sg_id} (no ingress, no egress)"})
    except ClientError as e:
        findings.append({"action": "create_isolation_sg", "status": "FAIL",
                         "severity": "HIGH", "detail": str(e)})
        return findings

    # Replace security groups with isolation SG
    try:
        ec2.modify_instance_attribute(
            InstanceId=instance_id,
            Groups=[isolation_sg_id],
        )
        findings.append({"action": "apply_isolation_sg", "status": "OK",
                         "detail": f"Replaced {current_sgs} with [{isolation_sg_id}]"})
    except ClientError as e:
        findings.append({"action": "apply_isolation_sg", "status": "FAIL",
                         "severity": "CRITICAL", "detail": str(e)})

    # Tag instance as contained
    try:
        ec2.create_tags(
            Resources=[instance_id],
            Tags=[
                {"Key": "IncidentStatus", "Value": "CONTAINED"},
                {"Key": "ContainmentTime", "Value": datetime.now(timezone.utc).isoformat()},
                {"Key": "OriginalSecurityGroups", "Value": ",".join(current_sgs)},
            ],
        )
        findings.append({"action": "tag_instance", "status": "OK"})
    except ClientError as e:
        findings.append({"action": "tag_instance", "status": "FAIL", "detail": str(e)})

    return findings


def create_forensic_snapshot(session, instance_id):
    """Create EBS snapshots for forensic preservation."""
    ec2 = session.client("ec2")
    findings = []
    print(f"[*] Creating forensic snapshots for: {instance_id}")

    try:
        resp = ec2.describe_instances(InstanceIds=[instance_id])
        instance = resp["Reservations"][0]["Instances"][0]
        volumes = []
        for mapping in instance.get("BlockDeviceMappings", []):
            vol_id = mapping.get("Ebs", {}).get("VolumeId")
            if vol_id:
                volumes.append((mapping["DeviceName"], vol_id))
    except ClientError as e:
        findings.append({"action": "describe_volumes", "status": "FAIL", "detail": str(e)})
        return findings

    for device_name, vol_id in volumes:
        try:
            snap = ec2.create_snapshot(
                VolumeId=vol_id,
                Description=f"Forensic snapshot - {instance_id} {device_name} - "
                            f"{datetime.now(timezone.utc).isoformat()}",
                TagSpecifications=[{
                    "ResourceType": "snapshot",
                    "Tags": [
                        {"Key": "Purpose", "Value": "Forensic-Preservation"},
                        {"Key": "SourceInstance", "Value": instance_id},
                        {"Key": "SourceVolume", "Value": vol_id},
                        {"Key": "CreatedBy", "Value": "incident-containment-agent"},
                    ],
                }],
            )
            findings.append({
                "action": "create_snapshot",
                "status": "OK",
                "detail": f"{vol_id} ({device_name}) -> {snap['SnapshotId']}",
            })
        except ClientError as e:
            findings.append({"action": "create_snapshot", "status": "FAIL",
                             "detail": f"{vol_id}: {e}"})

    return findings


def revoke_iam_credentials(session, username):
    """Revoke all IAM credentials for a compromised user."""
    iam = session.client("iam")
    findings = []
    print(f"[*] Revoking credentials for IAM user: {username}")

    # Deactivate access keys
    try:
        keys = iam.list_access_keys(UserName=username)
        for key in keys.get("AccessKeyMetadata", []):
            key_id = key["AccessKeyId"]
            iam.update_access_key(
                UserName=username, AccessKeyId=key_id, Status="Inactive"
            )
            findings.append({"action": "deactivate_access_key", "status": "OK",
                             "detail": f"Key {key_id[:8]}... deactivated"})
    except ClientError as e:
        findings.append({"action": "deactivate_access_keys", "status": "FAIL", "detail": str(e)})

    # Invalidate console session by attaching deny-all inline policy
    deny_policy = json.dumps({
        "Version": "2012-10-17",
        "Statement": [{"Effect": "Deny", "Action": "*", "Resource": "*"}],
    })
    try:
        iam.put_user_policy(
            UserName=username,
            PolicyName="IncidentDenyAll",
            PolicyDocument=deny_policy,
        )
        findings.append({"action": "attach_deny_policy", "status": "OK",
                         "detail": "Deny-all policy attached"})
    except ClientError as e:
        findings.append({"action": "attach_deny_policy", "status": "FAIL", "detail": str(e)})

    # Delete login profile (console access)
    try:
        iam.delete_login_profile(UserName=username)
        findings.append({"action": "delete_console_access", "status": "OK"})
    except iam.exceptions.NoSuchEntityException:
        findings.append({"action": "delete_console_access", "status": "SKIP",
                         "detail": "No console access configured"})
    except ClientError as e:
        findings.append({"action": "delete_console_access", "status": "FAIL", "detail": str(e)})

    return findings


def restrict_s3_bucket(session, bucket_name):
    """Restrict S3 bucket access during incident containment."""
    s3 = session.client("s3")
    findings = []
    print(f"[*] Restricting S3 bucket: {bucket_name}")

    # Block public access
    try:
        s3.put_public_access_block(
            Bucket=bucket_name,
            PublicAccessBlockConfiguration={
                "BlockPublicAcls": True,
                "IgnorePublicAcls": True,
                "BlockPublicPolicy": True,
                "RestrictPublicBuckets": True,
            },
        )
        findings.append({"action": "block_public_access", "status": "OK"})
    except ClientError as e:
        findings.append({"action": "block_public_access", "status": "FAIL", "detail": str(e)})

    # Enable versioning (preserve evidence)
    try:
        s3.put_bucket_versioning(
            Bucket=bucket_name,
            VersioningConfiguration={"Status": "Enabled"},
        )
        findings.append({"action": "enable_versioning", "status": "OK"})
    except ClientError as e:
        findings.append({"action": "enable_versioning", "status": "FAIL", "detail": str(e)})

    return findings


def format_summary(all_actions):
    """Print containment summary."""
    print(f"\n{'='*60}")
    print(f"  Cloud Incident Containment Report")
    print(f"{'='*60}")

    success = sum(1 for a in all_actions if a.get("status") == "OK")
    failed = sum(1 for a in all_actions if a.get("status") == "FAIL")
    print(f"  Actions  : {len(all_actions)}")
    print(f"  Success  : {success}")
    print(f"  Failed   : {failed}")

    print(f"\n  Actions Taken:")
    for a in all_actions:
        icon = "OK" if a["status"] == "OK" else "!!" if a["status"] == "FAIL" else "--"
        print(f"    [{icon}] {a['action']:30s} {a.get('detail', '')[:50]}")


def main():
    parser = argparse.ArgumentParser(
        description="AWS cloud incident containment agent"
    )
    sub = parser.add_subparsers(dest="command")

    p_iso = sub.add_parser("isolate", help="Isolate EC2 instance")
    p_iso.add_argument("--instance-id", required=True)

    p_snap = sub.add_parser("snapshot", help="Create forensic snapshots")
    p_snap.add_argument("--instance-id", required=True)

    p_iam = sub.add_parser("revoke-iam", help="Revoke IAM user credentials")
    p_iam.add_argument("--username", required=True)

    p_s3 = sub.add_parser("restrict-s3", help="Restrict S3 bucket")
    p_s3.add_argument("--bucket", required=True)

    p_full = sub.add_parser("full-contain", help="Full containment: isolate + snapshot + IAM")
    p_full.add_argument("--instance-id", required=True)
    p_full.add_argument("--username", help="IAM user to revoke")
    p_full.add_argument("--bucket", help="S3 bucket to restrict")

    parser.add_argument("--profile", help="AWS CLI profile")
    parser.add_argument("--region", help="AWS region")
    parser.add_argument("--output", "-o", help="Output JSON report path")
    parser.add_argument("--verbose", "-v", action="store_true")
    args = parser.parse_args()

    if not args.command:
        parser.print_help()
        sys.exit(1)

    session = get_session(args.profile, args.region)
    all_actions = []

    if args.command == "isolate":
        all_actions.extend(isolate_ec2_instance(session, args.instance_id))
    elif args.command == "snapshot":
        all_actions.extend(create_forensic_snapshot(session, args.instance_id))
    elif args.command == "revoke-iam":
        all_actions.extend(revoke_iam_credentials(session, args.username))
    elif args.command == "restrict-s3":
        all_actions.extend(restrict_s3_bucket(session, args.bucket))
    elif args.command == "full-contain":
        all_actions.extend(isolate_ec2_instance(session, args.instance_id))
        all_actions.extend(create_forensic_snapshot(session, args.instance_id))
        if args.username:
            all_actions.extend(revoke_iam_credentials(session, args.username))
        if args.bucket:
            all_actions.extend(restrict_s3_bucket(session, args.bucket))

    format_summary(all_actions)

    report = {
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "tool": "AWS Incident Containment",
        "command": args.command,
        "actions": all_actions,
        "success_count": sum(1 for a in all_actions if a["status"] == "OK"),
        "fail_count": sum(1 for a in all_actions if a["status"] == "FAIL"),
    }

    if args.output:
        with open(args.output, "w") as f:
            json.dump(report, f, indent=2)
        print(f"\n[+] Report saved to {args.output}")
    elif args.verbose:
        print(json.dumps(report, indent=2))


if __name__ == "__main__":
    main()
process.py12.5 KB
Display-only source. This catalog never executes bundled scripts.
"""
Cloud Incident Containment Automation Script
Provides containment procedures for AWS, Azure, and GCP environments.
"""

import json
import os
from datetime import datetime, timezone
from pathlib import Path


class CloudContainmentManager:
    """Manages cloud incident containment across AWS, Azure, and GCP."""

    def __init__(self, output_dir="cloud_containment_results"):
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)
        self.actions_log = []
        self.contained_resources = []

    def log_action(self, platform, action, resource, status, details=""):
        """Log a containment action for audit trail."""
        entry = {
            "timestamp": datetime.now(timezone.utc).isoformat(),
            "platform": platform,
            "action": action,
            "resource": resource,
            "status": status,
            "details": details,
        }
        self.actions_log.append(entry)
        print(f"[{platform}] {action}: {resource} -> {status}")

    def generate_aws_containment_commands(self, incident_type, resource_details):
        """Generate AWS CLI containment commands based on incident type."""
        commands = []

        if incident_type == "credential_compromise":
            user = resource_details.get("username", "UNKNOWN")
            commands.extend([
                {
                    "step": 1,
                    "description": "Disable all access keys",
                    "command": f"aws iam list-access-keys --user-name {user} --query 'AccessKeyMetadata[].AccessKeyId' --output text | xargs -I {{}} aws iam update-access-key --user-name {user} --access-key-id {{}} --status Inactive",
                },
                {
                    "step": 2,
                    "description": "Attach deny-all policy",
                    "command": f'aws iam put-user-policy --user-name {user} --policy-name EmergencyDenyAll --policy-document \'{{"Version":"2012-10-17","Statement":[{{"Effect":"Deny","Action":"*","Resource":"*"}}]}}\'',
                },
                {
                    "step": 3,
                    "description": "Delete console password",
                    "command": f"aws iam delete-login-profile --user-name {user}",
                },
                {
                    "step": 4,
                    "description": "Deactivate MFA devices",
                    "command": f"aws iam list-mfa-devices --user-name {user}",
                },
            ])

        elif incident_type == "ec2_compromise":
            instance_id = resource_details.get("instance_id", "i-UNKNOWN")
            vpc_id = resource_details.get("vpc_id", "vpc-UNKNOWN")
            volume_ids = resource_details.get("volume_ids", [])

            commands.extend([
                {
                    "step": 1,
                    "description": "Create forensic snapshots of all volumes",
                    "command": " && ".join([
                        f'aws ec2 create-snapshot --volume-id {vol} --description "Forensic-IR-$(date +%Y%m%d)" --tag-specifications \'ResourceType=snapshot,Tags=[{{Key=IR-Case,Value=active}}]\''
                        for vol in volume_ids
                    ]) if volume_ids else f"aws ec2 describe-instance-attribute --instance-id {instance_id} --attribute blockDeviceMapping",
                },
                {
                    "step": 2,
                    "description": "Create quarantine security group",
                    "command": f"aws ec2 create-security-group --group-name quarantine-$(date +%s) --description 'IR Quarantine' --vpc-id {vpc_id}",
                },
                {
                    "step": 3,
                    "description": "Remove default egress rule from quarantine SG",
                    "command": "aws ec2 revoke-security-group-egress --group-id SG_ID --ip-permissions '[{\"IpProtocol\":\"-1\",\"FromPort\":-1,\"ToPort\":-1,\"IpRanges\":[{\"CidrIp\":\"0.0.0.0/0\"}]}]'",
                },
                {
                    "step": 4,
                    "description": "Apply quarantine SG to instance",
                    "command": f"aws ec2 modify-instance-attribute --instance-id {instance_id} --groups SG_ID",
                },
                {
                    "step": 5,
                    "description": "Tag instance as contained",
                    "command": f"aws ec2 create-tags --resources {instance_id} --tags Key=IR-Status,Value=Contained Key=IR-Date,Value=$(date +%Y%m%d)",
                },
            ])

        elif incident_type == "s3_exposure":
            bucket = resource_details.get("bucket_name", "UNKNOWN")
            commands.extend([
                {
                    "step": 1,
                    "description": "Block all public access",
                    "command": f"aws s3api put-public-access-block --bucket {bucket} --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true",
                },
                {
                    "step": 2,
                    "description": "Enable versioning for evidence",
                    "command": f"aws s3api put-bucket-versioning --bucket {bucket} --versioning-configuration Status=Enabled",
                },
                {
                    "step": 3,
                    "description": "Enable server access logging",
                    "command": f"aws s3api put-bucket-logging --bucket {bucket} --bucket-logging-status '{{\"LoggingEnabled\":{{\"TargetBucket\":\"ir-logs-bucket\",\"TargetPrefix\":\"{bucket}/\"}}}}'",
                },
            ])

        elif incident_type == "lambda_compromise":
            function_name = resource_details.get("function_name", "UNKNOWN")
            commands.extend([
                {
                    "step": 1,
                    "description": "Stop function invocations",
                    "command": f"aws lambda put-function-concurrency --function-name {function_name} --reserved-concurrent-executions 0",
                },
                {
                    "step": 2,
                    "description": "Remove event source mappings",
                    "command": f"aws lambda list-event-source-mappings --function-name {function_name}",
                },
                {
                    "step": 3,
                    "description": "Capture function configuration",
                    "command": f"aws lambda get-function --function-name {function_name} > lambda_evidence_{function_name}.json",
                },
            ])

        return commands

    def generate_azure_containment_commands(self, incident_type, resource_details):
        """Generate Azure CLI/PowerShell containment commands."""
        commands = []

        if incident_type == "identity_compromise":
            user_id = resource_details.get("user_id", "UNKNOWN")
            commands.extend([
                {
                    "step": 1,
                    "description": "Revoke all refresh tokens",
                    "command": f"az rest --method POST --url 'https://graph.microsoft.com/v1.0/users/{user_id}/revokeSignInSessions'",
                },
                {
                    "step": 2,
                    "description": "Disable user account",
                    "command": f"az ad user update --id {user_id} --account-enabled false",
                },
                {
                    "step": 3,
                    "description": "Force password reset",
                    "command": f"az ad user update --id {user_id} --force-change-password-next-sign-in true",
                },
            ])

        elif incident_type == "vm_compromise":
            vm_name = resource_details.get("vm_name", "UNKNOWN")
            rg = resource_details.get("resource_group", "UNKNOWN")
            commands.extend([
                {
                    "step": 1,
                    "description": "Create disk snapshot",
                    "command": f"az snapshot create -g {rg} -n forensic-snap-$(date +%s) --source $(az vm show -g {rg} -n {vm_name} --query 'storageProfile.osDisk.managedDisk.id' -o tsv)",
                },
                {
                    "step": 2,
                    "description": "Create quarantine NSG",
                    "command": f"az network nsg create -g {rg} -n quarantine-nsg",
                },
                {
                    "step": 3,
                    "description": "Add deny-all inbound rule",
                    "command": f"az network nsg rule create -g {rg} --nsg-name quarantine-nsg -n DenyAllInbound --priority 100 --direction Inbound --access Deny --protocol '*' --source-address-prefix '*' --destination-address-prefix '*'",
                },
                {
                    "step": 4,
                    "description": "Add deny-all outbound rule",
                    "command": f"az network nsg rule create -g {rg} --nsg-name quarantine-nsg -n DenyAllOutbound --priority 100 --direction Outbound --access Deny --protocol '*' --source-address-prefix '*' --destination-address-prefix '*'",
                },
            ])

        return commands

    def generate_containment_playbook(self, case_id, platform, incident_type, resource_details):
        """Generate complete containment playbook."""
        if platform == "aws":
            commands = self.generate_aws_containment_commands(incident_type, resource_details)
        elif platform == "azure":
            commands = self.generate_azure_containment_commands(incident_type, resource_details)
        else:
            commands = []

        playbook = {
            "case_id": case_id,
            "platform": platform,
            "incident_type": incident_type,
            "generated": datetime.now(timezone.utc).isoformat(),
            "resource_details": resource_details,
            "pre_containment_checklist": [
                "Verify incident is confirmed (not false positive)",
                "Notify incident commander",
                "Create forensic snapshots/backups BEFORE containment",
                "Document current state of affected resources",
                "Identify break-glass credentials for IR team",
            ],
            "containment_commands": commands,
            "post_containment_checklist": [
                "Verify containment is effective (test connectivity)",
                "Confirm forensic evidence is preserved",
                "Update incident ticket with containment actions",
                "Notify stakeholders of containment status",
                "Begin eradication planning",
            ],
        }

        playbook_file = self.output_dir / f"containment_playbook_{case_id}.json"
        with open(playbook_file, "w") as f:
            json.dump(playbook, f, indent=2)

        print(f"[+] Containment playbook generated: {playbook_file}")
        print(f"    Platform: {platform}")
        print(f"    Type: {incident_type}")
        print(f"    Steps: {len(commands)}")
        return playbook

    def generate_report(self):
        """Generate containment actions report."""
        report = {
            "title": "Cloud Incident Containment Report",
            "generated": datetime.now(timezone.utc).isoformat(),
            "actions_taken": self.actions_log,
            "resources_contained": self.contained_resources,
            "total_actions": len(self.actions_log),
        }

        report_file = self.output_dir / "containment_report.json"
        with open(report_file, "w") as f:
            json.dump(report, f, indent=2)

        print(f"[+] Containment report: {report_file}")
        return report


def main():
    import argparse

    parser = argparse.ArgumentParser(description="Cloud Incident Containment Tool")
    parser.add_argument("--platform", choices=["aws", "azure", "gcp"], required=True)
    parser.add_argument("--incident-type", choices=[
        "credential_compromise", "ec2_compromise", "s3_exposure",
        "lambda_compromise", "identity_compromise", "vm_compromise",
    ], required=True)
    parser.add_argument("--case-id", default="IR-2025-001")
    parser.add_argument("--resource-json", help="JSON file with resource details")
    parser.add_argument("-o", "--output", default="cloud_containment_results")

    args = parser.parse_args()

    resource_details = {}
    if args.resource_json:
        with open(args.resource_json) as f:
            resource_details = json.load(f)

    manager = CloudContainmentManager(output_dir=args.output)
    manager.generate_containment_playbook(
        args.case_id, args.platform, args.incident_type, resource_details
    )


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 1.4 KB
Keep exploring