#!/usr/bin/env python3 """Cloud Incident Response Agent - Automates AWS/Azure cloud IR containment and evidence collection.""" import json import logging import argparse import subprocess from datetime import datetime, timedelta logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") logger = logging.getLogger(__name__) def aws_disable_access_key(username, access_key_id): """Disable a compromised IAM access key via AWS CLI.""" cmd = [ "aws", "iam", "update-access-key", "--user-name", username, "--access-key-id", access_key_id, "--status", "Inactive", ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) if result.returncode == 0: logger.info("Disabled access key %s for user %s", access_key_id, username) else: logger.error("Failed to disable key: %s", result.stderr) return result.returncode == 0 def aws_attach_deny_all(username): """Attach AWSDenyAll policy to a compromised IAM user.""" cmd = [ "aws", "iam", "attach-user-policy", "--user-name", username, "--policy-arn", "arn:aws:iam::aws:policy/AWSDenyAll", ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) if result.returncode == 0: logger.info("Attached AWSDenyAll to user %s", username) return result.returncode == 0 def aws_isolate_ec2(instance_id, forensic_sg): """Isolate an EC2 instance by changing its security group to forensic isolation SG.""" cmd = [ "aws", "ec2", "modify-instance-attribute", "--instance-id", instance_id, "--groups", forensic_sg, ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) if result.returncode == 0: logger.info("Isolated EC2 %s with security group %s", instance_id, forensic_sg) return result.returncode == 0 def aws_snapshot_ebs(instance_id): """Create EBS snapshots of all volumes attached to an EC2 instance.""" cmd = [ "aws", "ec2", "describe-volumes", "--filters", f"Name=attachment.instance-id,Values={instance_id}", "--query", "Volumes[*].VolumeId", "--output", "text", ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) volume_ids = result.stdout.strip().split() snapshots = [] for vol_id in volume_ids: snap_cmd = [ "aws", "ec2", "create-snapshot", "--volume-id", vol_id, "--description", f"IR evidence - {instance_id} - {datetime.utcnow().isoformat()}", ] snap_result = subprocess.run(snap_cmd, capture_output=True, text=True, timeout=120) if snap_result.returncode == 0: snap_data = json.loads(snap_result.stdout) snapshots.append(snap_data.get("SnapshotId")) logger.info("Created snapshot %s for volume %s", snap_data.get("SnapshotId"), vol_id) return snapshots def aws_query_cloudtrail(username, hours_back=24): """Query CloudTrail for API calls made by a specific IAM user.""" start_time = (datetime.utcnow() - timedelta(hours=hours_back)).strftime("%Y-%m-%dT%H:%M:%SZ") cmd = [ "aws", "cloudtrail", "lookup-events", "--lookup-attributes", f"AttributeKey=Username,AttributeValue={username}", "--start-time", start_time, "--output", "json", ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) if result.returncode == 0: events = json.loads(result.stdout).get("Events", []) logger.info("CloudTrail: %d events for user %s in last %d hours", len(events), username, hours_back) parsed = [] for event in events: ct_event = json.loads(event.get("CloudTrailEvent", "{}")) parsed.append({ "time": event.get("EventTime", ""), "event_name": event.get("EventName", ""), "source_ip": ct_event.get("sourceIPAddress", ""), "user_agent": ct_event.get("userAgent", ""), "resources": event.get("Resources", []), }) return parsed return [] def aws_list_attacker_resources(username, events): """Identify resources created by the attacker from CloudTrail events.""" create_events = [ e for e in events if e["event_name"].startswith(("Create", "Run", "Put", "Attach")) ] logger.info("Identified %d resource creation events", len(create_events)) return create_events def aws_check_all_regions_instances(): """Check all AWS regions for unauthorized EC2 instances.""" cmd = ["aws", "ec2", "describe-regions", "--query", "Regions[*].RegionName", "--output", "text"] result = subprocess.run(cmd, capture_output=True, text=True, timeout=120) regions = result.stdout.strip().split() all_instances = {} for region in regions: cmd = [ "aws", "ec2", "describe-instances", "--region", region, "--query", "Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name]", "--output", "json", ] r = subprocess.run(cmd, capture_output=True, text=True, timeout=120) if r.returncode == 0: instances = json.loads(r.stdout) running = [i for reservation in instances for i in reservation if i[2] == "running"] if running: all_instances[region] = running logger.info("Found instances in %d regions", len(all_instances)) return all_instances def generate_ir_report(incident_id, username, events, snapshots, containment_actions): """Generate a cloud incident response report.""" report = { "incident_id": incident_id, "timestamp": datetime.utcnow().isoformat(), "compromised_identity": username, "cloudtrail_events": len(events), "evidence_snapshots": snapshots, "containment_actions": containment_actions, "attacker_activity": events[:20], } print(json.dumps(report, indent=2)) return report def main(): parser = argparse.ArgumentParser(description="Cloud Incident Response Agent") parser.add_argument("--incident-id", required=True, help="Incident ID") parser.add_argument("--username", required=True, help="Compromised IAM username") parser.add_argument("--access-key-id", help="Compromised access key to disable") parser.add_argument("--instance-id", help="EC2 instance to isolate") parser.add_argument("--forensic-sg", default="sg-forensic-isolate", help="Forensic SG ID") parser.add_argument("--output", default="cloud_ir_report.json") args = parser.parse_args() containment = [] if args.access_key_id: aws_disable_access_key(args.username, args.access_key_id) containment.append(f"Disabled access key {args.access_key_id}") aws_attach_deny_all(args.username) containment.append(f"Attached AWSDenyAll to {args.username}") snapshots = [] if args.instance_id: aws_isolate_ec2(args.instance_id, args.forensic_sg) containment.append(f"Isolated EC2 {args.instance_id}") snapshots = aws_snapshot_ebs(args.instance_id) events = aws_query_cloudtrail(args.username, hours_back=72) attacker_actions = aws_list_attacker_resources(args.username, events) report = generate_ir_report(args.incident_id, args.username, events, snapshots, containment) with open(args.output, "w") as f: json.dump(report, f, indent=2) logger.info("IR report saved to %s", args.output) if __name__ == "__main__": main()