Chapter 75

Cloud VPC Flow Walkthrough

This final walkthrough covers a cloud infrastructure compromise investigated entirely from AWS VPC Flow Logs. No packet sensors, no Zeek, no Suricata — just the flow records that AWS provides natively. The scenario demonstrates how much you can determine from flow-level data alone, and what gaps remain when packets aren't available.

Scenario

An AWS EC2 instance running a public-facing web application is compromised through a remote code execution vulnerability. The attacker pivots to other instances in the same VPC, harvests IAM credentials from the metadata service, then moves to S3 to exfiltrate data. You have VPC Flow Logs for all subnets for the last 30 days. You need to determine: when the compromise occurred, how far the attacker moved, and what data they accessed.

VPC Flow Investigation

  Cloud Compromise Investigation from VPC Flow Logs
  ═══════════════════════════════════════════════════════════════════

  VPC topology:
  ├── Public subnet: web-app-01 (10.0.1.10) ← internet-facing
  ├── Private subnet: db-01 (10.0.2.10), cache-01 (10.0.2.20)
  └── Management subnet: bastion-01 (10.0.3.10)

  Attack phases from VPC Flow Logs:

  Phase 1 — Exploitation (T+0):
  └── REJECT entries for unusual source IPs against web-app-01:443
      ACCEPT on TCP/443 from 185.220.101.1 (Tor exit node)
      → Initial compromise via HTTPS

  Phase 2 — Internal reconnaissance (T+5min):
  └── web-app-01 → 10.0.2.10:3306 (MySQL — never seen before!)
  └── web-app-01 → 10.0.2.20:6379 (Redis — unexpected!)
  └── web-app-01 → 169.254.169.254:80 (AWS metadata service!)
       169.254.169.254 is the IMDS — if app accesses it, IAM creds at risk

  Phase 3 — Lateral movement (T+15min):
  └── web-app-01 → db-01:22 (SSH — unusual, app servers don't SSH to DB)
  └── db-01 → cache-01:22 (SSH pivot — second hop)
  └── db-01 → external IP 192.168.200.1:443 (reverse shell!)
       Private IP in flow → NAT → appears as NAT GW IP externally

  Phase 4 — Data access (T+30min):
  └── VPC Flow Logs show ACCEPT for S3 endpoint traffic:
       Actually visible as flows through VPC endpoint if configured
       OR as connections to s3.amazonaws.com via NAT gateway
  └── Large bytes_toserver to s3.amazonaws.com (upload = exfil to attacker bucket)

  What Flow Logs CAN'T tell us:
  ├── What URLs were accessed (no HTTP payload)
  ├── Which IAM role credentials were harvested
  ├── Which S3 buckets were read/written
  └── Command content in the reverse shell
  (Need: CloudTrail API logs for IAM/S3 activity, S3 access logs for bucket access)

Cloud Investigation Queries

bashcloud-vpc-investigation.sh
#!/bin/bash
# Queries assume VPC Flow Logs loaded into Athena or local filesystem
FLOW_FILE="${1:-vpc-flows.tsv}"

echo "=== Step 1: Identify initial external connection to compromised host ==="
awk '
  $4 == "10.0.1.10" && $14 == "ACCEPT" &&
  $3 !~ /^10\./ && $3 !~ /^172\.(1[6-9]|2[0-9]|3[01])\./ &&
  $3 !~ /^192\.168\./ {
    print $11"\t"$3"\t"$4"\t"$6"\t"$9"\t"$10
  }
' "$FLOW_FILE" | sort | head -20

echo ""
echo "=== Step 2: AWS metadata service access (IMDS — credential theft indicator) ==="
awk '
  $3 == "10.0.1.10" && $4 == "169.254.169.254" {
    print $11"\t"$3"\t"$4"\t"$5"\t"$9"\t"$10
  }
' "$FLOW_FILE" | sort | head -10

echo ""
echo "=== Step 3: Internal lateral movement from compromised host ==="
awk '
  $3 == "10.0.1.10" &&
  $4 ~ /^10\.0\.(2|3)\./ &&
  $14 == "ACCEPT" {
    print $11"\t"$4"\t"$6"\t"$9"\t"$10
  }
' "$FLOW_FILE" | sort | head -20

echo ""
echo "=== Step 4: Reverse shell / outbound C2 from internal hosts ==="
awk '
  $3 ~ /^10\.0\.(2|3)\./ &&
  $4 !~ /^10\./ && $4 != "169.254.169.254" &&
  $6 == "443" && $14 == "ACCEPT" {
    print $11"\t"$3"\t"$4"\t"$6"\t"$9"\t"$10
  }
' "$FLOW_FILE" | sort | head -20

echo ""
echo "=== Step 5: Large outbound transfers (exfiltration candidates) ==="
awk '
  $3 ~ /^10\.0\./ &&
  $4 !~ /^10\./ &&
  $9+0 > 1000000 && $14 == "ACCEPT" {
    print $9"\t"$3"\t"$4"\t"$6
  }
' "$FLOW_FILE" | sort -rn | head -20

echo ""
echo "=== Step 6: Traffic to S3 service IPs (data access) ==="
# AWS S3 IPs are in 52.216.0.0/15 and 54.231.0.0/17 ranges
awk '
  ($4 ~ /^52\.(21[6-9]|22[0-9]|230|231)\./ ||
   $4 ~ /^54\.(231|232)\./) &&
  $3 ~ /^10\./ && $14 == "ACCEPT" {
    print $11"\t"$3"\t"$4"\t"$9"\t"$10
  }
' "$FLOW_FILE" | sort | head -20
Mental model: VPC Flow Logs are the starting point, not the complete picture

VPC Flow Logs answer the same questions as traditional NetFlow: which EC2 instance talked to what IP, on what port, how much data, accepted or rejected. They don't answer what those connections actually did. In a cloud incident, VPC Flow Logs are always step one — they give you the attack path through the network. But to answer the critical questions (which S3 buckets were accessed, which IAM roles were used, what commands were run), you need: (1) AWS CloudTrail: API calls (GetObject, PutObject, AssumeRole, etc.) — the equivalent of application-layer logs for AWS services. (2) S3 Access Logs: every GetObject/PutObject against every bucket, if enabled. (3) CloudWatch Logs: application and system logs from the EC2 instances. The incident response workflow: VPC Flow Logs → identify compromised instances and attacker's path → CloudTrail → identify API calls made with harvested credentials → S3 Access Logs → determine what data was accessed. Missing any layer leaves gaps in the investigation. Enable all three before you need them, because you cannot retroactively enable CloudTrail for past events.

Book Conclusion

You have reached the end of Network Forensics & Traffic Analysis. The journey covered:

The network does not forget. Traffic that has been captured is evidence that can reconstruct what happened even when endpoint logs are missing, tampered with, or unavailable. The skills in this book are what let you read that evidence.

What to do next

To build real fluency, you need reps on real traffic: download the PCAP exercises from malware-traffic-analysis.net, set up a home lab with Zeek + Suricata + Arkime on a Linux server, and work through actual incident captures. Every technique in this book was written because it appears in real incidents. The gap between understanding the technique conceptually and applying it under incident pressure narrows with practice.