AWS Forensics
AWS investigations combine traditional EC2 instance forensics (essentially Linux forensics on a VM) with cloud-specific evidence sources: CloudTrail API logs, VPC Flow Logs for network visibility, S3 access logs, and GuardDuty findings. This chapter covers the complete AWS forensic workflow — from isolating the compromised instance to EBS snapshot analysis.
An EC2 instance running a public web application was compromised via a misconfigured instance metadata service (IMDS v1). The attacker retrieved the IAM role credentials, used them to access S3 buckets, and launched new EC2 instances for cryptomining. GuardDuty detected the cryptomining. CloudTrail shows the credential theft and S3 access. VPC Flow Logs show the lateral movement between EC2 instances. This chapter covers the full investigation workflow.
AWS's shared responsibility model draws a line: AWS is responsible for the security of the cloud (physical infrastructure, hypervisor, managed services); you are responsible for security in the cloud (OS configuration, IAM, network controls, application). This line directly shapes what evidence you can access. You own the EC2 instance OS, the EBS volume, and the application logs — those are yours to image and analyze. You do NOT have access to the hypervisor logs, the physical host audit trail, or the network fabric below the VPC level. What AWS provides instead: VPC Flow Logs (layer 4 metadata — no packet content), CloudTrail (API call audit), and GuardDuty (threat detection based on sources you can't directly access). A key insight: AWS managed services (RDS, Lambda, ECS) have less evidence than EC2 because you have less access to the underlying runtime. Lambda functions, for example, have no persistent filesystem — your entire evidence base is CloudWatch Logs and CloudTrail, nothing else.
Incident Response: Isolating a Compromised EC2 Instance
The instinct in IR is to isolate first, investigate second. In AWS, that order destroys evidence. Changing the security group to block all traffic is fast and reversible — but stopping the instance (which some IR playbooks do for isolation) ends the EC2 instance's ephemeral state: the instance store, any memory, and running process list are gone before you've captured them. The script below takes the EBS snapshot in Step 1, isolation in Step 2 — that order is deliberate. For full memory preservation, connect to the instance before isolation and run avml or use the AWS SSM Run Command to dump memory to S3 first. Only after evidence preservation is complete should you modify security groups or stop the instance.
PROFILE="--profile forensics-analyst"
REGION="--region us-east-1"
INSTANCE_ID="i-0abc1234567890def"
CASE="CASE-2026-009"
# Step 1: Take an EBS snapshot BEFORE any changes (preservation first)
echo "Creating EBS snapshot for forensic analysis..."
VOLUME_ID=$(aws ec2 describe-instances $PROFILE $REGION \
--instance-ids $INSTANCE_ID \
--query 'Reservations[0].Instances[0].BlockDeviceMappings[0].Ebs.VolumeId' \
--output text)
SNAPSHOT_ID=$(aws ec2 create-snapshot $PROFILE $REGION \
--volume-id $VOLUME_ID \
--description "Forensic snapshot - $CASE - $(date -u +%Y%m%dT%H%M%SZ)" \
--tag-specifications "ResourceType=snapshot,Tags=[{Key=Case,Value=$CASE},{Key=Type,Value=forensic}]" \
--query 'SnapshotId' --output text)
echo "Snapshot created: $SNAPSHOT_ID"
# Step 2: Isolate the instance (change security group to block all traffic)
# Create a forensic isolation security group with no inbound/outbound rules
ISOLATION_SG=$(aws ec2 create-security-group $PROFILE $REGION \
--group-name "forensic-isolation-$CASE" \
--description "No inbound or outbound traffic — forensic isolation" \
--query 'GroupId' --output text)
# Remove all existing security groups, assign isolation SG only
aws ec2 modify-instance-attribute $PROFILE $REGION \
--instance-id $INSTANCE_ID \
--groups $ISOLATION_SG
echo "Instance $INSTANCE_ID isolated with SG $ISOLATION_SG"
# Step 3: Tag the instance as under investigation
aws ec2 create-tags $PROFILE $REGION \
--resources $INSTANCE_ID \
--tags Key=ForensicStatus,Value=UnderInvestigation \
Key=CaseID,Value=$CASE \
Key=IsolatedAt,Value=$(date -u +%Y%m%dT%H%M%SZ)
AWS Evidence Sources and Default Retention
| Evidence source | What it contains | Default retention | Must be enabled? |
|---|---|---|---|
| CloudTrail (management events) | All API calls: who, what, when, from where | 90 days in console; S3 delivery is indefinite if configured | Enabled by default in new accounts; multi-region trail recommended |
| CloudTrail (data events) | S3 object-level operations (GetObject, PutObject), Lambda invocations | Same as management events if trail is configured for data events | NOT enabled by default — must explicitly configure per resource |
| VPC Flow Logs | Layer 4 network metadata (src/dst IP, port, protocol, bytes, action) | Dependent on CloudWatch Logs or S3 retention configuration | NOT enabled by default — must be configured per VPC/subnet/ENI |
| CloudWatch Logs (application) | EC2 instance OS logs, application logs (if CloudWatch agent configured) | Configurable per log group; default: never expire | NOT by default — requires CloudWatch agent on EC2 |
| GuardDuty findings | Threat detection findings based on CloudTrail, VPC Flow, DNS | 90 days in GuardDuty console | NOT enabled by default — must be enabled per region |
| S3 Server Access Logs | Every S3 request to a bucket (GET/PUT/DELETE, source IP, user agent) | Delivered to a target bucket; separate retention needed | NOT by default — must be enabled per bucket |
| EBS Snapshots | Point-in-time disk image of EBS volume | Until manually deleted | Manual creation or DLM policy |
EBS Snapshot Analysis
SNAPSHOT_ID="snap-0abc1234567890def"
PROFILE="--profile forensics-analyst"
REGION="--region us-east-1"
CASE_DIR="/cases/CASE-2026-009/aws"
# Option 1: Create a new EBS volume from the snapshot and attach to analysis instance
ANALYSIS_INSTANCE="i-0analysis123456789"
VOLUME_ID=$(aws ec2 create-volume $PROFILE $REGION \
--availability-zone us-east-1a \
--snapshot-id $SNAPSHOT_ID \
--volume-type gp3 \
--query 'VolumeId' --output text)
aws ec2 wait volume-available $PROFILE $REGION --volume-ids $VOLUME_ID
aws ec2 attach-volume $PROFILE $REGION \
--instance-id $ANALYSIS_INSTANCE \
--volume-id $VOLUME_ID \
--device /dev/xvdf
# On the analysis instance (SSH in):
# Mount read-only and analyze
sudo mount -o ro /dev/xvdf1 /mnt/evidence
# Run standard Linux forensic collection from /mnt/evidence
ls -la /mnt/evidence/var/log/
cat /mnt/evidence/var/log/auth.log | grep "Accepted password\|sudo"
find /mnt/evidence -newer /mnt/evidence/tmp -type f 2>/dev/null | grep -v proc
# Option 2: Copy snapshot to your analysis region and analyze
aws ec2 copy-snapshot $PROFILE \
--source-region us-east-1 \
--source-snapshot-id $SNAPSHOT_ID \
--destination-region eu-west-1 \
--description "Copy for analysis in EU region"
VPC Flow Log Analysis
INSTANCE_IP="10.0.1.50" # private IP of compromised instance
FLOW_LOG_DIR="/cases/CASE-2026-009/vpc-flow-logs/"
# VPC Flow Log format:
# version account-id interface-id srcaddr dstaddr srcport dstport protocol
# packets bytes start end action log-status
# Find all external connections from the compromised instance
grep "$INSTANCE_IP" $FLOW_LOG_DIR/*.log.gz | \
zcat - 2>/dev/null | \
awk '$4 == "'"$INSTANCE_IP"'" && $5 !~ /^10\./ && $12 == "ACCEPT"' | \
awk '{print $5, $7, $8}' | sort | uniq -c | sort -rn | head -20
# Find large outbound data transfers (exfiltration)
zcat $FLOW_LOG_DIR/*.log.gz 2>/dev/null | \
awk '$4 == "'"$INSTANCE_IP"'" && $5 !~ /^10\./' | \
awk '{bytes[$5] += $9} END {for (ip in bytes) print bytes[ip], ip}' | \
sort -rn | head -10
# Find IMDS access (metadata service at 169.254.169.254)
# This shows if an EC2 instance accessed its own metadata service
# (credential theft via SSRF or IMDS v1 vulnerability)
zcat $FLOW_LOG_DIR/*.log.gz 2>/dev/null | \
awk '$4 == "'"$INSTANCE_IP"'" && $5 == "169.254.169.254"' | head -20
Q & A
Q: The attacker deleted the CloudTrail logs from S3. What evidence remains?
Deleting CloudTrail logs from S3 is itself a logged event in CloudTrail — and that deletion event goes to CloudTrail's internal buffer, which has a 90-day retention in the console even if the S3 delivery is disrupted. The S3 deletion events (DeleteObject calls for the CloudTrail log files) appear as CloudTrail management events. Additionally: (1) CloudTrail cannot be retroactively cleared: the API call record exists in AWS's infrastructure for 90 days regardless of whether the S3 delivery was deleted. The attacker can delete the S3 copies but not the CloudTrail event history in the console. (2) If log streaming to CloudWatch Logs was enabled: CloudWatch Logs has its own retention separate from S3 delivery. (3) GuardDuty: operates independently from CloudTrail logs and generates its own findings based on VPC Flow Logs and DNS queries — GuardDuty findings survive CloudTrail log deletion. (4) Prevention: CloudTrail can be configured with S3 Object Lock (WORM — Write Once Read Many) to prevent deletion. Enable this for your CloudTrail S3 bucket to make log deletion impossible even for admin-level credentials.