Chapter 2.1 Quiz - Packet Analysis & Protocol Dissection
Quiz Mode - All answers are hidden under collapsible sections. Attempt each question before revealing the answer.
Question 1
You are analyzing a packet capture and see the following TCP flag combinations from source IP 10.0.0.88 to multiple destinations. Identify the scan type for each, explain what the attacker is trying to determine, and write a tcpdump filter to capture each:
Packet A: Flags [F] (FIN only)
Packet B: Flags [FPU] (FIN + PSH + URG)
Packet C: Flags [] (No flags)
Packet D: Flags [S] to 65535 unique destination ports in 30 seconds
Reveal Answer & Explanation
Answer: FIN scan, Xmas scan, NULL scan, vertical SYN scan.
Packet A - FIN Scan (Nmap -sF)
A FIN packet is sent without a prior connection. According to RFC 793:
- Open port: silently drops the unexpected FIN (no response)
- Closed port: responds with RST
This allows port state enumeration. FIN scans bypass stateless packet filters that only block SYN packets, because there's no SYN to block.
# tcpdump filter:
tcpdump -i eth0 'tcp[tcpflags] == tcp-fin'
# Wireshark: tcp.flags == 0x001
Packet B - Xmas Scan (Nmap -sX)
Sets FIN, PSH, and URG simultaneously - these flags don't coexist in normal traffic. Named "Xmas" because the flags light up like a Christmas tree. Same open/closed port behavior as FIN scan (RFC 793).
# tcpdump filter:
tcpdump -i eth0 'tcp[tcpflags] & (tcp-fin|tcp-push|tcp-urg) == (tcp-fin|tcp-push|tcp-urg)'
# Wireshark: tcp.flags == 0x029
Packet C - NULL Scan (Nmap -sN)
Zero TCP flags set - completely invalid per RFC 793. Same RFC 793 open/closed response behavior.
# tcpdump filter:
tcpdump -i eth0 'tcp[tcpflags] == 0'
# Wireshark: tcp.flags == 0x000
Important caveat for A, B, C: These scans only work reliably against RFC 793-compliant TCP stacks (Linux). Windows ignores the RFC rule and always responds with RST regardless of port state - making these scans unreliable for Windows targets.
Packet D - Vertical SYN Scan / Port Scan
SYN packets to 65535 different ports in 30 seconds is a classic port scan. The attacker is mapping all open services on a target host.
# tcpdump filter:
tcpdump -i eth0 'src host 10.0.0.88 and tcp[tcpflags] & tcp-syn != 0 and tcp[tcpflags] & tcp-ack == 0'
# Wireshark: ip.src==10.0.0.88 && tcp.flags.syn==1 && tcp.flags.ack==0
Detection and response:
# Alert on any host sending >100 SYNs in 60 seconds (Suricata rule):
# alert tcp any any -> $HOME_NET any (msg:"Port Scan Detected";
# flags:S,12; threshold:type both, track by_src, count 100, seconds 60;
# sid:1000001;)
# Block scanning source with iptables (temporary, may block legitimate scanners too)
iptables -A INPUT -s 10.0.0.88 -p tcp --tcp-flags ALL FIN -j DROP
iptables -A INPUT -s 10.0.0.88 -p tcp --tcp-flags ALL FIN,PSH,URG -j DROP
iptables -A INPUT -s 10.0.0.88 -p tcp --tcp-flags ALL NONE -j DROP
Question 2
Analyze this tshark output extract and identify all security anomalies:
Time Src IP Dst IP Proto Info
0.000 10.0.1.55 8.8.8.8 DNS Standard query A aGVsbG8ud29ybGQ.updates.corp-patch.net
30.012 10.0.1.55 8.8.8.8 DNS Standard query A dGhpcyBpcyBhIHRlc3Q.updates.corp-patch.net
60.001 10.0.1.55 8.8.8.8 DNS Standard query A c2VjcmV0IGRhdGE.updates.corp-patch.net
90.003 10.0.1.55 8.8.8.8 DNS Standard query A ZXhmaWx0cmF0aW5n.updates.corp-patch.net
0.000 10.0.1.77 10.0.0.1 TCP SYN to port 22
0.001 10.0.1.77 10.0.0.1 TCP SYN to port 23
0.002 10.0.1.77 10.0.0.1 TCP SYN to port 25
0.003 10.0.1.77 10.0.0.1 TCP SYN to port 80
0.300 10.0.1.77 10.0.0.2 TCP SYN to port 22
0.301 10.0.1.77 10.0.0.2 TCP SYN to port 23
200.000 10.0.0.5 203.45.67.89 TCP [SYN] Seq=0 Win=1024
200.030 203.45.67.89 10.0.0.5 TCP [SYN, ACK]
200.031 10.0.0.5 203.45.67.89 TCP [ACK]
200.032 10.0.0.5 203.45.67.89 TCP [PSH,ACK] len=1 (payload: "\x00")
230.035 10.0.0.5 203.45.67.89 TCP [PSH,ACK] len=1 (payload: "\x00")
260.038 10.0.0.5 203.45.67.89 TCP [PSH,ACK] len=1 (payload: "\x00")
Explain what is happening, identify the attacker, and describe two distinct post-capture actions the attacker can take.
Reveal Answer & Explanation
Answer: Three distinct attacks - DNS tunneling/exfiltration from .55, internal network scan from .77, and C2 beacon from .5.
Anomaly 1 - DNS Tunneling / Data Exfiltration (10.0.1.55)
# Decode the subdomain labels:
echo "aGVsbG8ud29ybGQ" | base64 -d # -> "hello.world"
echo "dGhpcyBpcyBhIHRlc3Q" | base64 -d # -> "this is a test"
echo "c2VjcmV0IGRhdGE" | base64 -d # -> "secret data"
echo "ZXhmaWx0cmF0aW5n" | base64 -d # -> "exfiltrating"
- Subdomains are Base64-encoded data - DNS tunneling confirmed
- 30-second intervals - classic C2 beacon / exfil timing
- Destination domain
corp-patch.net- typosquatting the company's legitimate domain - Data decoded: "hello.world", "this is a test", "secret data", "exfiltrating" - attacker is testing the channel then exfiltrating data
Anomaly 2 - Internal Network Scan / Lateral Movement (10.0.1.77)
- SYN packets to ports 22, 23, 25, 80 on 10.0.0.1 within 3ms - automated port scan
- Immediately repeats against 10.0.0.2 - horizontal scan across multiple hosts
- Scanning ports 22 (SSH), 23 (Telnet), 25 (SMTP), 80 (HTTP) - common service enumeration
- The millisecond timing is machine-speed - not manual, automated tool (Nmap, Masscan)
Anomaly 3 - C2 Beacon Heartbeat (10.0.0.5 -> 203.45.67.89)
- TCP window size 1024 - characteristic of Nmap and some malware (non-standard, most OS use 64240+)
- After handshake: sends exactly 1 byte (
\x00) payload - Interval between 1-byte sends: exactly 30 seconds (200.032 -> 230.035 -> 260.038)
- 1-byte heartbeat at 30s interval = classic C2 beacon keepalive
203.45.67.89is external - malware maintaining connection to attacker's C2 server
Response actions:
# 1. Isolate 10.0.1.55 -- confirmed exfiltrating data
iptables -A FORWARD -s 10.0.1.55 -j DROP
# 2. Block DNS tunnel domain at resolver
# Add to /etc/bind/named.conf: zone "corp-patch.net" { type redirect; ... }
# 3. Block C2 IP at firewall
iptables -A OUTPUT -d 203.45.67.89 -j DROP
# 4. Identify host at 10.0.0.5 and initiate IR process
# Check DHCP logs for MAC address
grep "10.0.0.5" /var/log/dhcp.log | tail -5
# 5. Look for lateral movement from 10.0.1.77
# Check auth logs on 10.0.0.1 and 10.0.0.2 for subsequent logins
grep "10.0.1.77" /var/log/auth.log
Question 3
Write a Python script using Scapy that detects ARP spoofing attacks in real time by monitoring ARP traffic. The script should alert when a MAC address change is detected for an existing IP and log it with a timestamp.
Reveal Answer & Explanation
Answer: Below is a production-quality ARP spoof detector.
#!/usr/bin/env python3
"""
ARP Spoofing Detector
Monitors ARP traffic in real-time and alerts on MAC address changes
for existing IP-to-MAC mappings.
Usage: sudo python3 arp_detector.py -i eth0
"""
from scapy.all import ARP, Ether, sniff, get_if_hwaddr
from datetime import datetime
import argparse
import json
import sys
# ARP table: maps IP -> MAC (trusted, learned from first ARP seen)
arp_table = {}
alerts = []
# Known gateway/critical hosts -- alert immediately (not just on change)
CRITICAL_IPS = {
"10.0.0.1": "Gateway",
"10.0.0.254": "Secondary GW",
}
def get_mac_vendor(mac):
"""Return vendor from first 3 octets of MAC (OUI lookup)"""
# In production: query macvendors API or use local OUI database
known_oui = {
"00:50:56": "VMware",
"08:00:27": "VirtualBox",
"52:54:00": "QEMU/KVM",
"00:0c:29": "VMware",
"aa:bb:cc": "Unknown (suspicious)",
}
oui = mac[:8].lower()
return known_oui.get(oui, "Unknown")
def process_arp(pkt):
"""Process each ARP packet and detect spoofing"""
if ARP not in pkt:
return
arp = pkt[ARP]
# Only process ARP replies (op=2) and gratuitous ARPs
# Gratuitous: sender IP == target IP, used to announce MAC changes
if arp.op not in [1, 2]: # 1=who-has (request), 2=is-at (reply)
return
src_ip = arp.psrc # Sender's IP
src_mac = arp.hwsrc # Sender's MAC
# Ignore broadcast MACs and empty IPs
if src_mac in ("ff:ff:ff:ff:ff:ff", "00:00:00:00:00:00"):
return
if src_ip in ("0.0.0.0", "255.255.255.255"):
return
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
# First time seeing this IP -- learn it
if src_ip not in arp_table:
arp_table[src_ip] = src_mac
vendor = get_mac_vendor(src_mac)
print(f"[{timestamp}] INFO Learned {src_ip:18s} -> {src_mac} ({vendor})")
# If it's a critical IP, log it prominently
if src_ip in CRITICAL_IPS:
print(f"[{timestamp}] NOTE Critical host {CRITICAL_IPS[src_ip]} at {src_mac}")
return
# IP already known -- check if MAC changed
known_mac = arp_table[src_ip]
if src_mac != known_mac:
severity = "CRITICAL" if src_ip in CRITICAL_IPS else "WARNING"
role = CRITICAL_IPS.get(src_ip, "")
alert = {
"timestamp": timestamp,
"severity": severity,
"src_ip": src_ip,
"role": role,
"old_mac": known_mac,
"new_mac": src_mac,
"old_vendor": get_mac_vendor(known_mac),
"new_vendor": get_mac_vendor(src_mac),
}
alerts.append(alert)
# Print alert
print(f"\n{'!'*60}")
print(f"[{timestamp}] {severity}: ARP SPOOF DETECTED")
print(f" IP: {src_ip} {('(' + role + ')') if role else ''}")
print(f" OLD MAC: {known_mac} ({get_mac_vendor(known_mac)})")
print(f" NEW MAC: {src_mac} ({get_mac_vendor(src_mac)})")
print(f" ARP Op: {'Request' if arp.op==1 else 'Reply (Gratuitous ARP)'}")
print(f"{'!'*60}\n")
# Update table to track current state
arp_table[src_ip] = src_mac
# Write alert to log file
with open("arp_alerts.jsonl", "a") as f:
f.write(json.dumps(alert) + "\n")
def main():
parser = argparse.ArgumentParser(description="ARP Spoofing Detector")
parser.add_argument("-i", "--interface", default="eth0",
help="Network interface to monitor")
parser.add_argument("-c", "--count", type=int, default=0,
help="Packet count (0 = infinite)")
args = parser.parse_args()
print(f"ARP Spoofing Detector - monitoring {args.interface}")
print(f"Critical IPs: {CRITICAL_IPS}")
print(f"Alerts logged to: arp_alerts.jsonl\n")
try:
sniff(
iface=args.interface,
filter="arp", # BPF filter -- only ARP
prn=process_arp, # Callback for each packet
store=0, # Don't store packets in memory
count=args.count
)
except KeyboardInterrupt:
print(f"\n\nStopped. {len(alerts)} alerts generated.")
print(f"Final ARP table ({len(arp_table)} entries):")
for ip, mac in sorted(arp_table.items()):
print(f" {ip:18s} -> {mac}")
if __name__ == "__main__":
if sys.platform != "win32":
import os
if os.geteuid() != 0:
print("Error: must run as root (sudo)")
sys.exit(1)
main()
How it works:
- Sniffs only ARP packets using BPF (efficient, no processing of other traffic)
- Builds a trusted IP->MAC table from the first ARP observed for each IP
- Any subsequent ARP claiming a different MAC for a known IP triggers an alert
- Gratuitous ARPs (op=1 where sender IP == target IP) are also processed - attackers use these to poison the entire subnet at once
- Critical IPs (gateways, servers) generate higher severity alerts
Running it:
sudo python3 arp_detector.py -i eth0
# Example output on detection:
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
# [2024-01-15 14:32:07.442] CRITICAL: ARP SPOOF DETECTED
# IP: 10.0.0.1 (Gateway)
# OLD MAC: aa:bb:cc:dd:ee:ff (Cisco)
# NEW MAC: 11:22:33:44:55:66 (Unknown (suspicious))
# ARP Op: Reply (Gratuitous ARP)
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Question 4
A pcap capture taken during an incident shows regular outbound HTTPS connections from an internal host (10.0.0.99) to 185.220.101.45:443. You cannot decrypt the traffic. Using only metadata (timing, packet sizes, TLS fingerprints), describe a methodology to determine with high confidence whether this is C2 beaconing, and what tools/commands you would use at each step.
Reveal Answer & Explanation
Answer: Five-step metadata analysis - timing regularity, packet size distribution, JA3 fingerprint, IP reputation, and TLS SNI analysis.
Step 1 - Beacon Interval Analysis
# Extract connection timestamps for the specific flow
tshark -r capture.pcap \
-Y "ip.src==10.0.0.99 && ip.dst==185.220.101.45 && tcp.flags.syn==1 && tcp.flags.ack==0" \
-T fields \
-e frame.time_epoch | \
python3 -c "
import sys, statistics
times = [float(t) for t in sys.stdin if t.strip()]
times.sort()
intervals = [times[i+1]-times[i] for i in range(len(times)-1)]
if intervals:
avg = statistics.mean(intervals)
std = statistics.stdev(intervals) if len(intervals)>1 else 0
print(f'Connections: {len(times)}')
print(f'Avg interval: {avg:.1f}s ({avg/60:.1f}m)')
print(f'Std deviation: {std:.1f}s')
print(f'Jitter ratio: {std/avg:.3f}')
print(f'Beacon verdict: {\"LIKELY BEACON\" if std/avg < 0.15 else \"IRREGULAR\"}')
"
# Human browsing: irregular, high jitter (jitter ratio > 0.5)
# C2 beacon: low jitter (< 0.1), regular interval every N seconds/minutes
Step 2 - Packet Size Distribution
# C2 beacons often send tiny packets (keepalive/checkin) vs large responses
tshark -r capture.pcap \
-Y "ip.addr==10.0.0.99 && ip.addr==185.220.101.45" \
-T fields \
-e frame.len \
-e ip.src | \
python3 -c "
import sys
from collections import Counter
outbound = []
inbound = []
for line in sys.stdin:
parts = line.strip().split()
if len(parts) == 2:
size, src = int(parts[0]), parts[1]
if src == '10.0.0.99':
outbound.append(size)
else:
inbound.append(size)
print(f'Outbound: avg={sum(outbound)/len(outbound):.0f}b, n={len(outbound)}')
print(f'Inbound: avg={sum(inbound)/len(inbound):.0f}b, n={len(inbound)}')
# Typical C2: small outbound (100-400b checkin) + occasional large inbound (task delivery)
# Typical browsing: variable outbound + large inbound (page content)
"
Step 3 - JA3 Fingerprint Analysis
# Extract JA3 hash from TLS ClientHello
tshark -r capture.pcap \
-Y "tls.handshake.type==1 && ip.src==10.0.0.99" \
-T fields \
-e tls.handshake.ja3 # Requires tshark >= 3.0
# Or use zeek (formerly Bro) which computes JA3 natively
zeek -r capture.pcap
cat ssl.log | grep "185.220.101.45" | cut -f15 # ja3 field
# Compare JA3 against known malware hashes:
KNOWN_C2_JA3=(
"72a589da586844d7f0818ce684948eea" # Cobalt Strike default
"d4e457bda4060ac3a1e0ce55df394aa6" # Metasploit
"a0e9f5d64349fb13191bc781f81f42e1" # Sliver
"51c64c77e60f3980eea90869b68c58a8" # Common malware
)
OBSERVED_JA3="<hash from tshark>"
for h in "${KNOWN_C2_JA3[@]}"; do
if [ "$OBSERVED_JA3" == "$h" ]; then
echo "MATCH: Known C2 JA3 hash $h"
fi
done
# Also compute JARM (server fingerprint) for the C2 server
python3 jarm.py 185.220.101.45 443
# Compare against threat intel: known Cobalt Strike JARM hashes
Step 4 - IP/Domain Reputation
# Check IP reputation (multiple sources)
# VirusTotal
curl "https://www.virustotal.com/api/v3/ip_addresses/185.220.101.45" \
-H "x-apikey: YOUR_KEY" | python3 -m json.tool | grep -E "malicious|suspicious|asn"
# AbuseIPDB
curl "https://api.abuseipdb.com/api/v2/check" \
-d "ipAddress=185.220.101.45&maxAgeInDays=90" \
-H "Key: YOUR_KEY" | python3 -m json.tool
# Shodan: what is running on that IP?
shodan host 185.220.101.45
# Tor exit node? Known hosting provider for bulletproof hosting?
# Unusual certificate? Self-signed? Short validity?
# Check ASN
whois 185.220.101.45 | grep -E "OrgName|ASName|country"
# Known bulletproof hosting ASNs: AS62282, AS206728, AS209588
Step 5 - TLS SNI and Certificate Analysis
# Extract SNI from TLS ClientHello (what hostname was requested)
tshark -r capture.pcap \
-Y "tls.handshake.type==1 && ip.src==10.0.0.99" \
-T fields \
-e tls.handshake.extensions_server_name
# If SNI is absent -> suspicious (legitimate HTTPS always sends SNI)
# If SNI doesn't match the IP's PTR record -> suspicious
# If SNI is an IP address instead of hostname -> red flag
# Extract server certificate details (even without decrypting content)
tshark -r capture.pcap \
-Y "tls.handshake.type==11 && ip.src==185.220.101.45" \
-T fields \
-e tls.handshake.certificate
# Check certificate:
# - Self-signed? (Issuer == Subject)
# - Subject CN matches the domain/IP?
# - Issued recently? (C2 certs are often rotated quickly)
# - Certificate validity period (unusually long/short?)
# - Issued by a free CA like Let's Encrypt? (common for malware)
Verdict methodology:
| Signal | Benign | Malicious Indicator |
|---|---|---|
| Interval jitter ratio | > 0.5 (irregular) | < 0.1 (very regular) |
| Outbound packet size | Variable, large | Small, uniform (checkin) |
| JA3 hash | Common browser hash | Matches known malware |
| JARM hash | Common server hash | Matches known C2 (CS, Meterpreter) |
| IP reputation | Clean | High abuse score |
| ASN | Major cloud/CDN | Bulletproof hosting |
| SNI present | Yes | Missing or IP-based |
| Certificate | Trusted CA, valid org | Self-signed, recently issued |
If 4+ signals match malicious indicators -> high confidence C2 beacon.