Chapter 2.1 - Packet Analysis & Protocol Dissection
Module 2: Traffic Analysis & Intrusion Detection Level: Intermediate to Advanced | Estimated reading time: 60-75 min
Table of Contents
- Why Packet Analysis Matters
- Packet Anatomy - From Ethernet Frame to Payload
- Capture Tools & Techniques
- Wireshark - Deep Inspection
- Protocol Dissection - TCP, UDP, HTTP, DNS, TLS
- Detecting Malicious Traffic Patterns
- Network Forensics - Reconstructing Sessions
- Architecture Diagram
1. Why Packet Analysis Matters
Packet analysis is the discipline of capturing, decoding, and interpreting raw network traffic. It sits at the intersection of offense and defense:
- For defenders: detect C2 beacons, data exfiltration, lateral movement, protocol abuse
- For incident responders: reconstruct what happened, establish timelines, recover transferred files
- For red teamers: understand what your tools look like on the wire and how to blend in
- For engineers: debug protocol issues, validate encryption, confirm firewall rules
The fundamental principle: every network interaction leaves a trace. Packets don't lie. Logs can be deleted, but if you capture traffic at the wire level, the evidence is preserved.
The Analyst's Mindset
When looking at a capture, ask:
- What is the baseline for this network? What does normal look like?
- Is this traffic expected from this source/destination pair?
- Does the volume, timing, and direction match the stated purpose?
- Is the protocol being used as designed, or abused?
2. Packet Anatomy - From Ethernet Frame to Payload
Every packet traversing an Ethernet network is an onion of encapsulated headers. Understanding each layer's fields is prerequisite to reading them.
2.1 Layer Structure
+------------------------------------------------------------------+
| Ethernet Frame |
| +------------+------------+-------------------------------+ |
| | Dst MAC | Src MAC | EtherType (0x0800=IPv4) | |
| | 6 bytes | 6 bytes | 2 bytes | |
| +------------+------------+-------------------------------+ |
| +------------------------------------------------------------+ |
| | IP Header (20+ bytes) | |
| | Ver|IHL|DSCP|ECN| Total Length | Identification | |
| | Flags|Fragment Offset| TTL | Protocol | Header Checksum | |
| | Source IP (4 bytes) | Destination IP (4 bytes) | |
| +------------------------------------------------------------+ |
| +------------------------------------------------------------+ |
| | TCP/UDP Header | |
| | Src Port (2) | Dst Port (2) | Sequence Number (4) | |
| | Acknowledgment Number (4) | Data Offset|Flags|Window | |
| +------------------------------------------------------------+ |
| +------------------------------------------------------------+ |
| | Application Data | |
| | (HTTP, DNS, TLS record, raw payload...) | |
| +------------------------------------------------------------+ |
+------------------------------------------------------------------+
2.2 IP Header Fields - Security Relevance
| Field | Size | Security Significance |
|---|---|---|
| TTL | 8 bits | OS fingerprinting (Linux=64, Windows=128, Cisco=255); decremented each hop |
| Protocol | 8 bits | 6=TCP, 17=UDP, 1=ICMP - covert channels use unexpected protocols |
| Source IP | 32 bits | Spoofable (attacker tool for DoS, scanning obfuscation) |
| Fragment Offset | 13 bits | Fragment overlap attacks to bypass IDS |
| Flags (DF, MF) | 3 bits | DF=Don't Fragment; overlapping fragments used in evasion |
| DSCP/ECN | 6+2 bits | Sometimes used for covert channels |
| Identification | 16 bits | Sequential IDs leak OS info; used for idle scan (Nmap -sI) |
2.3 TCP Header Fields - Security Relevance
| Field | Size | Security Significance |
|---|---|---|
| Source Port | 16 bits | Ephemeral port ranges differ by OS (Linux: 32768-60999, Windows: 49152-65535) |
| Sequence Number | 32 bits | Predictable ISN = session hijack risk; TCP reset attacks |
| ACK Number | 32 bits | Must match expected seq - mismatches indicate injection |
| Flags | 9 bits | SYN/FIN/RST/PSH/ACK/URG - unusual combos (Xmas, NULL, FIN scan) |
| Window Size | 16 bits | OS fingerprinting; zero window = flow control |
| Urgent Pointer | 16 bits | Rarely used legitimately; sometimes used for covert data |
2.4 TCP Flags and Scan Types
Normal communication flags:
SYN -> Connection initiation
SYN + ACK -> Server acknowledgment
ACK -> Data acknowledgment
FIN + ACK -> Graceful connection close
RST -> Abrupt connection reset
Scanning flag combinations:
SYN only -> SYN scan (stealthy -- no full handshake)
FIN only -> FIN scan (bypasses stateless packet filters)
FIN + URG + PSH -> Xmas scan (all "lit up" -- unusual, evades some IDS)
No flags (NULL) -> NULL scan (zero flags -- RFC non-compliant, evasion)
ACK only -> ACK scan (maps firewall rules -- reveals filtered vs unfiltered)
SYN + FIN -> Invalid -- immediate red flag in IDS
SYN + RST -> Invalid -- red flag
tcpdump filter for suspicious flag combinations:
tcpdump -i eth0 'tcp[tcpflags] & (tcp-fin|tcp-syn|tcp-rst|tcp-push|tcp-urg) == 0' # NULL scan
tcpdump -i eth0 'tcp[tcpflags] & (tcp-fin|tcp-urg|tcp-push) == (tcp-fin|tcp-urg|tcp-push)' # Xmas scan
tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-fin) == (tcp-syn|tcp-fin)' # SYN+FIN (invalid)
3. Capture Tools & Techniques
3.1 tcpdump - Command Line Capture
tcpdump is the universal capture tool on Linux/Unix. Its Berkeley Packet Filter (BPF) syntax is shared across tcpdump, Wireshark, Snort, Suricata, and many other tools.
# Basic capture on interface eth0
tcpdump -i eth0
# Capture to file (always do this -- analyze offline)
tcpdump -i eth0 -w capture.pcap
# Capture with larger buffer (reduces dropped packets on busy links)
tcpdump -i eth0 -B 4096 -w capture.pcap
# Capture with timestamps and no DNS resolution (faster, shows raw IPs)
tcpdump -i eth0 -nn -tttt -w capture.pcap
# -nn: don't resolve hostnames or port names
# -tttt: show absolute timestamp with date
# Rotate capture files: new file every 100MB or every 3600 seconds
tcpdump -i eth0 -C 100 -G 3600 -w capture_%Y%m%d_%H%M%S.pcap
# -- BPF Filters --
# Capture only traffic to/from a specific host
tcpdump -i eth0 host 10.0.0.50
# Capture a specific port
tcpdump -i eth0 port 443
# Capture a subnet
tcpdump -i eth0 net 10.0.0.0/24
# Capture TCP only (exclude UDP/ICMP noise)
tcpdump -i eth0 tcp
# Capture DNS queries (UDP port 53) AND responses
tcpdump -i eth0 'udp port 53'
# Capture HTTP traffic and show ASCII payload
tcpdump -i eth0 -A 'tcp port 80 and (((ip[2:2] - ((ip[0]&0xf)<<2)) - ((tcp[12]&0xf0)>>2)) != 0)'
# Capture all traffic EXCEPT SSH (avoid capturing your own session)
tcpdump -i eth0 'not port 22'
# Detect potential port scans: SYN packets to multiple ports from one source
tcpdump -i eth0 'tcp[tcpflags] == tcp-syn' -nn | \
awk '{print $3}' | \
cut -d. -f1-4 | \
sort | uniq -c | sort -rn | head
# Capture ICMP and show type/code
tcpdump -i eth0 icmp -v
3.2 tshark - Wireshark's CLI
tshark provides Wireshark's dissection engine at the command line - ideal for automated analysis and scripting.
# Read pcap and display with specific fields
tshark -r capture.pcap \
-T fields \
-e frame.time \
-e ip.src \
-e ip.dst \
-e tcp.dstport \
-e http.request.method \
-e http.request.uri \
-E header=y \
-E separator=, \
> http_requests.csv
# Extract all DNS queries from a capture
tshark -r capture.pcap \
-Y "dns.flags.response == 0" \
-T fields \
-e frame.time \
-e ip.src \
-e dns.qry.name \
-e dns.qry.type
# Follow a specific TCP stream (reconstruct conversation)
tshark -r capture.pcap \
-z follow,tcp,ascii,0 # Stream index 0
# Statistics: top talkers by bytes
tshark -r capture.pcap -q -z conv,ip | head -20
# Protocol hierarchy (what protocols are in the capture)
tshark -r capture.pcap -q -z io,phs
# Extract files from HTTP traffic (images, executables, documents)
tshark -r capture.pcap \
--export-objects http,/tmp/extracted_files/
# Find long-running TCP sessions (potential C2 beacons)
tshark -r capture.pcap -q -z conv,tcp | \
awk '{print $1, $3, $5, $9}' | \
sort -k4 -rn | head -20 # Sort by duration
# Decrypt TLS traffic (requires key log file)
tshark -r capture.pcap \
-o "tls.keylog_file:/tmp/sslkeys.log" \
-Y "http" \
-T fields -e http.request.full_uri
3.3 Capture Positions in the Network
The position of your capture sensor determines what you can see:
SPAN / Port Mirroring (Switched Port Analyzer):
In a switched network, you can't just plug in a laptop and see everyone's traffic (unlike hubs). A SPAN port copies all traffic from source ports to a monitor port.
# Cisco switch: configure SPAN port
Switch(config)# monitor session 1 source interface Gi0/1 - Gi0/24
Switch(config)# monitor session 1 destination interface Gi0/25
# Now connect capture device to Gi0/25 -- sees all traffic from Gi0/1-24
# Linux: tap a bridge interface to see traffic between two segments
ip link add br0 type bridge
ip link set eth0 master br0
ip link set eth1 master br0
ip link set br0 up
tcpdump -i br0 -w capture.pcap
4. Wireshark - Deep Inspection
4.1 Essential Filters
Wireshark display filters use a different syntax from tcpdump BPF filters - they operate on decoded protocol fields.
# -- Basic Filters --
ip.addr == 10.0.0.50 # Traffic to or from this IP
ip.src == 10.0.0.50 # Traffic from this source
ip.dst == 10.0.0.50 # Traffic to this destination
tcp.port == 443 # Either src or dst port 443
tcp.dstport == 8080 # Destination port 8080
tcp.flags.syn == 1 # SYN flag set
tcp.flags == 0x002 # Exactly SYN only (scan detection)
tcp.flags.syn == 1 && tcp.flags.ack == 0 # SYN without ACK (half-open scan)
# -- Protocol-Specific Filters --
http.request.method == "POST" # HTTP POST requests
http.response.code >= 500 # Server errors
dns.qry.name contains "evil" # DNS query containing "evil"
dns.resp.ttl < 60 # Low TTL DNS responses (C2 fast-flux)
tls.handshake.type == 1 # TLS ClientHello
tls.record.version == 0x0301 # TLS 1.0 (deprecated)
smtp.req.command == "AUTH" # SMTP authentication attempts
ftp.request.command == "PASS" # FTP password (cleartext!)
# -- Advanced Analysis Filters --
frame.len > 1400 # Large packets (data exfil? fragmentation?)
tcp.analysis.retransmission # TCP retransmissions (network issues or evasion)
tcp.analysis.zero_window # Zero window -- flow control issues
tcp.analysis.duplicate_ack # Duplicate ACKs -- packet loss indicator
ip.ttl < 10 # Low TTL -- packet traversed many hops OR TTL manipulation
icmp.type == 8 && frame.len > 100 # Oversized ICMP ping (ICMP tunneling)
# -- Credential Hunting --
http contains "password" # HTTP traffic containing "password" string
ftp # All FTP (credentials in cleartext)
telnet # All Telnet (credentials in cleartext)
pop || imap # Email protocols (often cleartext auth)
4.2 Statistics & Analysis Features
Wireshark Menu Analysis Tools:
Statistics -> Protocol Hierarchy
-> Shows all protocols in capture, % of bytes each uses
-> Anomaly: unexpected protocol (e.g., IRC, IMAP in corporate traffic)
Statistics -> Conversations
-> Lists all TCP/UDP/IP conversations with bytes transferred
-> Sort by bytes: large transfers to external IPs = exfil candidates
Statistics -> Endpoints
-> Shows all IP/MAC endpoints, packets sent/received
-> Internal IPs talking to many external IPs = scanner or botnet
Statistics -> I/O Graph
-> Traffic volume over time
-> Regular spikes at fixed intervals = C2 beacon heartbeat
Analyze -> Expert Information
-> Wireshark's automatic anomaly detection
-> Errors (red): TCP retransmissions, checksum failures
-> Warnings (yellow): connection resets, window problems
-> Notes (cyan): protocol anomalies
Analyze -> Follow -> TCP Stream
-> Reconstructs the application-layer conversation
-> Shows request + response in readable form
-> Critical for: HTTP session analysis, cleartext credential capture
Statistics -> HTTP -> Requests
-> All HTTP requests by host, path, user-agent
-> Unusual user-agents = malware or scanning tools
4.3 Colorization Rules for Security Analysis
Wireshark's default colorization helps spot issues:
- Black background (red text): TCP errors, bad checksums
- Dark red: HTTP errors (4xx, 5xx)
- Light purple: TCP SYN
- Light blue: DNS
- Light green: HTTP traffic
Custom rule for C2 detection:
# Add custom color rule for suspicious long connections:
# Edit -> Coloring Rules -> New
# Name: "Suspicious Long TCP"
# Filter: tcp.time_relative > 3600 && tcp.flags.syn == 0
# Color: bright red background
5. Protocol Dissection
5.1 TCP - Three-Way Handshake Analysis
# Capture and annotate a TCP handshake
tcpdump -i eth0 -nn 'host 10.0.0.50 and port 80' -v
# Expected output for normal handshake:
# 10:00:01.000 IP 10.0.0.10.54321 > 10.0.0.50.80: Flags [S], seq 0, win 64240, length 0
# ^ ^SYN ^ISN
# 10:00:01.001 IP 10.0.0.50.80 > 10.0.0.10.54321: Flags [S.], seq 0, ack 1, win 65535, length 0
# ^ ^SYN+ACK
# 10:00:01.002 IP 10.0.0.10.54321 > 10.0.0.50.80: Flags [.], ack 1, win 502, length 0
# ^ ^ACK
# Detect RST storms (intrusion attempt / DoS)
tcpdump -i eth0 'tcp[tcpflags] & tcp-rst != 0' -nn | \
awk '{print $3}' | cut -d. -f1-4 | sort | uniq -c | sort -rn
# Detect half-open connections (SYN flood)
ss -ant | grep SYN_RECV | wc -l
# >200 SYN_RECV is suspicious
netstat -ant | grep SYN_RECV | awk '{print $5}' | cut -d: -f1 | sort | uniq -c | sort -rn
5.2 HTTP - Request/Response Analysis
# Extract all HTTP requests with timestamps, method, URI, response code
tshark -r capture.pcap \
-Y "http.request or http.response" \
-T fields \
-e frame.time_relative \
-e ip.src \
-e ip.dst \
-e http.request.method \
-e http.request.uri \
-e http.response.code \
-e http.user_agent \
-E separator="|"
# Suspicious user-agents to hunt for in HTTP:
# curl/7.x.x -> scripted requests (automation / attacker tools)
# python-requests -> scripted (pentesting, malware)
# Go-http-client -> Go-based tools (many C2 frameworks)
# Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1) -> very old UA, likely malware
# (blank user agent) -> raw socket connection, automated tool
# Nikto, sqlmap, Masscan, dirbuster -> scanning tools
# Hunt for large POST bodies (potential exfiltration or exploit payload)
tshark -r capture.pcap \
-Y "http.request.method == POST && http.content_length > 10000" \
-T fields \
-e ip.src -e http.request.uri -e http.content_length
# Detect directory traversal attempts in HTTP URIs
tshark -r capture.pcap \
-Y 'http.request.uri contains "../"' \
-T fields -e ip.src -e http.request.uri
# Detect SQL injection patterns in URIs
tshark -r capture.pcap \
-Y 'http.request.uri matches "(?i)(union|select|insert|drop|exec|xp_)"' \
-T fields -e ip.src -e http.request.uri
5.3 DNS - Query Analysis
DNS is the most information-rich protocol for detecting malicious activity - almost every network action requires DNS.
# Extract all DNS queries with response IPs
tshark -r capture.pcap \
-Y "dns" \
-T fields \
-e frame.time \
-e ip.src \
-e dns.qry.name \
-e dns.a \
-e dns.flags.response \
| grep "0$" # Only queries (response flag = 0)
# Find domains with suspiciously low TTL (fast-flux C2)
tshark -r capture.pcap \
-Y "dns.flags.response == 1" \
-T fields \
-e dns.qry.name \
-e dns.resp.ttl \
| awk '$2 < 60 {print}' | sort -u
# Detect DNS tunneling: look for long subdomain labels
tshark -r capture.pcap \
-Y "dns" \
-T fields \
-e dns.qry.name \
| awk '{if(length($1) > 50) print $1}' | sort -u
# Calculate entropy of DNS query names (DGA detection)
python3 << 'EOF'
import math
from collections import Counter
# Paste DNS query names here (from tshark output or DNS logs)
domains = [
"www.google.com",
"api.github.com",
"aGVsbG8.evil-c2.net", # Base64-encoded subdomain
"xkjhdfqwer.biz", # DGA-like
"mail.company.com"
]
def entropy(s):
counts = Counter(s)
total = len(s)
return -sum((c/total) * math.log2(c/total) for c in counts.values())
for d in domains:
label = d.split('.')[0] # First label
e = entropy(label)
flag = "SUSPICIOUS" if e > 3.5 or len(label) > 30 else "OK"
print(f"{d:45s} entropy={e:.2f} {flag}")
EOF
# Detect DNS rebinding (IP changes in short succession for same domain)
# Attacker changes DNS to internal IP after browser caches original resolution
tshark -r capture.pcap -Y "dns.flags.response == 1" \
-T fields -e dns.qry.name -e dns.a | \
sort | awk 'seen[$1]++ && seen[$1]==2 {print "MULTIPLE IPs: "$0}'
5.4 TLS - Handshake Inspection Without Decryption
Even without decrypting TLS, the handshake reveals significant intelligence.
# Extract TLS ClientHello details (what client supports)
tshark -r capture.pcap \
-Y "tls.handshake.type == 1" \
-T fields \
-e ip.src \
-e tls.handshake.extensions_server_name \
-e tls.handshake.ciphersuite \
-e tls.handshake.extensions.supported_versions
# JA3 fingerprinting: fingerprint TLS clients by handshake parameters
# JA3 = MD5 of (SSLVersion,Ciphers,Extensions,EllipticCurves,EllipticCurvePoints)
# Malware families have characteristic JA3 hashes regardless of payload content
# Install ja3 for tshark
pip3 install pyshark
python3 << 'EOF'
import pyshark
import hashlib
def compute_ja3(pkt):
"""Compute JA3 hash from TLS ClientHello"""
try:
tls = pkt.tls
if int(tls.handshake_type) != 1:
return None
version = tls.handshake_version
ciphers = tls.handshake_ciphersuite.replace(':', '-')
extensions = getattr(tls, 'handshake_extensions_type', '').replace(':', '-')
curves = getattr(tls, 'handshake_extensions_supported_group', '').replace(':', '-')
points = getattr(tls, 'handshake_extensions_ec_point_format', '').replace(':', '-')
ja3_str = f"{version},{ciphers},{extensions},{curves},{points}"
ja3_hash = hashlib.md5(ja3_str.encode()).hexdigest()
return ja3_hash, ja3_str
except AttributeError:
return None
cap = pyshark.FileCapture('capture.pcap', display_filter='tls.handshake.type==1')
for pkt in cap:
result = compute_ja3(pkt)
if result:
ja3_hash, ja3_str = result
print(f"src={pkt.ip.src} JA3={ja3_hash}")
# Compare against known malware JA3 hashes:
# Cobalt Strike default: 72a589da586844d7f0818ce684948eea
# Metasploit default: d4e457bda4060ac3a1e0ce55df394aa6
EOF
# JARM fingerprinting -- fingerprint TLS SERVERS
# JARM sends 10 specially crafted TLS ClientHellos, fingerprints server responses
# Useful for identifying C2 infrastructure regardless of certificate
pip3 install jarm-fingerprinter
python3 -m jarm 10.0.0.1 443
# Output: JARM hash (e.g., "07d14d16d21d21d00042d43d000000...")
# Compare against known C2 server hashes in threat intel feeds
6. Detecting Malicious Traffic Patterns
6.1 C2 Beacon Detection
C2 beaconing is characterized by periodic, regular connections to an external IP/domain. The callback interval is a key detection signal.
# Detect beaconing: find connections with regular time intervals
# Extract connection timestamps per destination
python3 << 'EOF'
import subprocess, json
from collections import defaultdict
import statistics
# Run tshark to extract TCP SYN timestamps per destination IP
output = subprocess.check_output([
'tshark', '-r', 'capture.pcap',
'-Y', 'tcp.flags.syn==1 && tcp.flags.ack==0',
'-T', 'fields',
'-e', 'frame.time_epoch',
'-e', 'ip.dst'
], text=True)
# Group by destination IP
connections = defaultdict(list)
for line in output.strip().split('\n'):
if '\t' in line:
ts, dst = line.split('\t')
connections[dst].append(float(ts))
# Analyze intervals for each destination
print(f"{'Destination IP':20s} {'Connections':12s} {'Avg Interval':15s} {'Std Dev':12s} {'Beacon?'}")
print("-" * 75)
for dst, timestamps in sorted(connections.items()):
if len(timestamps) < 4:
continue
timestamps.sort()
intervals = [timestamps[i+1]-timestamps[i] for i in range(len(timestamps)-1)]
avg = statistics.mean(intervals)
std = statistics.stdev(intervals) if len(intervals) > 1 else 0
jitter_ratio = std / avg if avg > 0 else 1
beacon = "BEACON" if jitter_ratio < 0.1 and avg < 3600 else ""
print(f"{dst:20s} {len(timestamps):12d} {avg:15.1f}s {std:12.1f} {beacon}")
EOF
6.2 Data Exfiltration Patterns
# Detect large outbound data transfers
tshark -r capture.pcap -q -z conv,tcp | \
awk 'NR>5 {
split($1, src, ":")
split($3, dst, ":")
if ($6+0 > $8+0) { # More bytes sent than received = outbound transfer
print $1, "->", $3, "Outbound:", $6, "bytes"
}
}' | sort -k5 -rn | head -20
# Detect DNS exfiltration (high query volume per domain)
tshark -r capture.pcap -Y "dns.flags.response==0" \
-T fields -e dns.qry.name | \
awk -F. '{
n = NF; domain = $(n-1)"."$n;
count[domain]++
}
END {
for (d in count) if (count[d] > 100) print count[d], d
}' | sort -rn
# Detect HTTPS exfiltration via unusual upload volumes
# Large POST to external IP at unusual hours
tshark -r capture.pcap \
-Y "http.request.method==POST" \
-T fields \
-e frame.time \
-e ip.src \
-e ip.dst \
-e http.content_length | \
awk -F'\t' '$4+0 > 100000 {print}' # POST bodies > 100KB
6.3 Port Scanning Detection
# Detect horizontal scan: one source hitting many destinations on same port
# (e.g., worm spreading, lateral movement)
tshark -r capture.pcap \
-Y "tcp.flags.syn==1 && tcp.flags.ack==0" \
-T fields -e ip.src -e ip.dst | \
awk '{srcdst[$1][$2]=1} END {
for (src in srcdst) {
n = 0
for (dst in srcdst[src]) n++
if (n > 20) print src, "->", n, "destinations"
}
}'
# Detect vertical scan: one source hitting many ports on same destination
# (service enumeration)
tshark -r capture.pcap \
-Y "tcp.flags.syn==1 && tcp.flags.ack==0" \
-T fields -e ip.src -e ip.dst -e tcp.dstport | \
awk '{key=$1" "$2; ports[key][$3]=1}
END {
for (k in ports) {
n=0; for (p in ports[k]) n++
if (n > 15) print k, ":", n, "ports"
}
}'
# Detect Nmap OS detection probes (sends packets with unusual TCP options)
# Look for TCP packets with unusual window sizes and option combinations
tcpdump -r capture.pcap -v 'tcp[tcpflags] == tcp-syn' 2>/dev/null | \
grep -E "win [0-9]+ " | \
awk '{print $NF}' | \
sort | uniq -c | sort -rn
# Nmap SYN scan uses window size 1024, 2048, 4096 in sequence
6.4 Protocol Anomalies
# Detect non-standard ports for common protocols
# HTTP on non-80/443 ports (possible evasion or pivot)
tshark -r capture.pcap \
-Y "http && not tcp.port == 80 && not tcp.port == 443 && not tcp.port == 8080" \
-T fields -e ip.src -e ip.dst -e tcp.dstport -e http.request.uri
# DNS over non-standard port (DNS tunneling evasion)
tshark -r capture.pcap \
-Y "dns && not udp.port == 53 && not tcp.port == 53" \
-T fields -e ip.src -e ip.dst -e udp.dstport
# ICMP packets with large payload (tunneling)
tshark -r capture.pcap \
-Y "icmp && frame.len > 100" \
-T fields -e ip.src -e ip.dst -e frame.len -e data.len
# Cleartext credentials on the wire (legacy protocols)
tcpdump -r capture.pcap -A -l 'port ftp or port 23 or port 110 or port 143' | \
grep -iE "(user|pass|login|password|auth)" | \
grep -v "^[[:space:]]*$"
7. Network Forensics - Reconstructing Sessions
7.1 Extracting Files from Captures
# Extract HTTP-transferred files (executables, documents, images)
tshark -r capture.pcap \
--export-objects http,/tmp/http_extracted/
ls -la /tmp/http_extracted/
# Files are named by HTTP URI path
# Check extracted files for malware:
file /tmp/http_extracted/*
for f in /tmp/http_extracted/*; do
hash=$(sha256sum "$f" | awk '{print $1}')
echo "$f: $hash"
# Query VirusTotal, MalwareBazaar, etc.
done
# Extract SMB-transferred files
tshark -r capture.pcap \
--export-objects smb,/tmp/smb_extracted/
# Reconstruct a TCP stream to a file
tshark -r capture.pcap \
-z follow,tcp,raw,0 \
| xxd | head -50 # Show as hex
# Alternative: Python's dpkt for raw stream reassembly
python3 << 'EOF'
import dpkt, socket
with open('capture.pcap', 'rb') as f:
pcap = dpkt.pcap.Reader(f)
flows = {}
for ts, buf in pcap:
try:
eth = dpkt.ethernet.Ethernet(buf)
if not isinstance(eth.data, dpkt.ip.IP): continue
ip = eth.data
if not isinstance(ip.data, dpkt.tcp.TCP): continue
tcp = ip.data
src = socket.inet_ntoa(ip.src)
dst = socket.inet_ntoa(ip.dst)
key = f"{src}:{tcp.sport}->{dst}:{tcp.dport}"
if tcp.data:
flows.setdefault(key, b'')
flows[key] += tcp.data
except:
continue
for flow, data in flows.items():
if len(data) > 1000:
print(f"Flow {flow}: {len(data)} bytes")
# Save large flows for analysis
fname = flow.replace(':', '_').replace('>', '_').replace('/', '_')
with open(f"/tmp/flow_{fname}.bin", 'wb') as out:
out.write(data)
EOF
7.2 Timeline Reconstruction
# Build a timeline of network events from a capture
tshark -r capture.pcap \
-T fields \
-e frame.time \
-e ip.src \
-e ip.dst \
-e _ws.col.Protocol \
-e _ws.col.Info \
-E separator="|" \
| sort > network_timeline.txt
# Find first and last occurrence of each connection
tshark -r capture.pcap -q -z conv,tcp | \
awk 'NR>5 {print $1, "first:", $5, "last:", $6, "duration:", $9}'
# Correlate with system logs (combine network and host artifacts)
# Join network timeline with auth.log timestamps
join <(awk -F'|' '{print $1, $2}' network_timeline.txt | sort) \
<(grep "Failed password" /var/log/auth.log | awk '{print $1, $2, $3, $9}' | sort)
7.3 Pcap Analysis Automation with Python Scapy
from scapy.all import *
from collections import defaultdict
import statistics
def analyze_pcap(pcap_file):
"""Comprehensive pcap analysis for security incidents"""
packets = rdpcap(pcap_file)
print(f"Loaded {len(packets)} packets\n")
# Track statistics
syn_counts = defaultdict(int)
dns_queries = defaultdict(list)
large_transfers = []
cleartext_creds = []
for pkt in packets:
# SYN scan detection
if TCP in pkt and pkt[TCP].flags == 'S':
src = pkt[IP].src if IP in pkt else "unknown"
syn_counts[src] += 1
# DNS query logging
if DNS in pkt and pkt[DNS].qr == 0:
query = pkt[DNS].qd.qname.decode('utf-8', errors='ignore').rstrip('.')
src = pkt[IP].src if IP in pkt else "unknown"
dns_queries[src].append(query)
# Large TCP payload detection (>10KB per packet)
if TCP in pkt and Raw in pkt:
if len(pkt[Raw].load) > 10000:
large_transfers.append({
'src': pkt[IP].src,
'dst': pkt[IP].dst,
'size': len(pkt[Raw].load),
'time': float(pkt.time)
})
# Cleartext FTP credential detection
if TCP in pkt and Raw in pkt:
payload = pkt[Raw].load.decode('utf-8', errors='ignore')
if payload.startswith(('USER ', 'PASS ')):
cleartext_creds.append({
'src': pkt[IP].src,
'dst': pkt[IP].dst,
'data': payload.strip()
})
# Report findings
print("=== PORT SCAN CANDIDATES ===")
for src, count in sorted(syn_counts.items(), key=lambda x: -x[1]):
if count > 50:
print(f" {src}: {count} SYNs")
print("\n=== HIGH DNS QUERY VOLUME ===")
for src, queries in dns_queries.items():
if len(queries) > 100:
print(f" {src}: {len(queries)} queries")
# Show top queried domains
from collections import Counter
top = Counter(queries).most_common(5)
for domain, cnt in top:
print(f" {domain}: {cnt}x")
print("\n=== LARGE TRANSFERS ===")
for t in sorted(large_transfers, key=lambda x: -x['size'])[:10]:
print(f" {t['src']} -> {t['dst']}: {t['size']:,} bytes")
print("\n=== CLEARTEXT CREDENTIALS ===")
for c in cleartext_creds:
print(f" {c['src']} -> {c['dst']}: {c['data']}")
# analyze_pcap('capture.pcap')