Lesson 3 of 3 · 20 min read

File Carving, Timeline Reconstruction, and PCAP Evidence Standards

PCAP evidence has the highest forensic fidelity of any network artifact — it contains the actual conversation, not just metadata. But size and complexity require a systematic workflow to extract findings efficiently.


Running Zeek on a PCAP

# Install and run Zeek offline against a PCAP
zeek -r capture.pcap local --no-checksums
# --no-checksums: skip checksum verification (common issue with captured traffic)

# Output files generated:
ls -la *.log
# conn.log      dns.log     http.log    ssl.log
# files.log     weird.log   notice.log  dpd.log
# x509.log      ftp.log     smtp.log

# Analyze conn.log for beaconing
cat conn.log | zeek-cut ts id.orig_h id.resp_h proto service duration orig_bytes resp_bytes | \
  grep "185.220.101.9" | sort -k1

# Find all external ESTABLISHED connections
cat conn.log | zeek-cut ts id.orig_h id.resp_h proto duration orig_bytes resp_bytes conn_state | \
  awk '$8 == "SF"' | \
  grep -v "10\.\|192\.168\.\|172\.\(1[6-9]\|2[0-9]\|3[01]\)\." | \
  sort -k4 -rn | head -20

# Analyze dns.log for tunneling candidates
cat dns.log | zeek-cut ts id.orig_h query qtype_name answers | \
  awk '{print length($3), $3}' | sort -rn | head -20

# Check files.log for transferred malware
cat files.log | zeek-cut ts source sha256 filename mime_type | \
  grep -v "^-" | sort | uniq
# Submit all SHA256 hashes to VirusTotal

File Carving Workflow

# Method 1: Zeek file extraction (most automated)
zeek -r capture.pcap local --no-checksums FileExtract::PREFIX=./extracted/
# All files transferred over HTTP/FTP/SMTP are written to ./extracted/
# MD5/SHA256 hashes in files.log
ls -la ./extracted/

# Method 2: NetworkMiner (GUI — Windows)
# 1. Open NetworkMiner
# 2. File → Open → select capture.pcap
# 3. Click Files tab
# 4. Examine all extracted files with MD5 hashes
# 5. Right-click → Submit to VirusTotal (if online)

# Method 3: Wireshark HTTP export
# File → Export Objects → HTTP
# Exports all HTTP response bodies as files
# Limited to HTTP (not HTTPS, SMB, FTP)

# Method 4: tcpflow + manual carving
# Reconstruct all TCP streams as files:
tcpflow -r capture.pcap -o ./streams/
ls ./streams/
# Files named: [src_IP].[src_port]-[dst_IP].[dst_port]
# Look for streams containing MZ (PE executable) or PK (ZIP) headers:
for f in ./streams/*; do
  magic=$(xxd "$f" 2>/dev/null | head -1 | awk '{print $2$3}')
  case "$magic" in
    "4d5a"*) echo "PE executable: $f" ;;
    "504b03"*) echo "ZIP/Office file: $f" ;;
    "25504446"*) echo "PDF: $f" ;;
    *) ;;
  esac
done

# Hash all carved files and check against VirusTotal
for f in ./extracted/*; do
  hash=$(sha256sum "$f" | awk '{print $1}')
  echo "$hash  $f"
done | tee extracted_hashes.txt

SMB Lateral Movement in PCAP

# Identify SMB connections (TCP 445)
tshark -r capture.pcap \
  -Y "tcp.dstport == 445" \
  -T fields \
  -e ip.src \
  -e ip.dst \
  | sort | uniq -c | sort -rn

# SMB2 file operations — what files were accessed/created
tshark -r capture.pcap \
  -Y "smb2.cmd == 5" \
  -T fields \
  -e frame.time \
  -e ip.src \
  -e ip.dst \
  -e smb2.filename

# SMB authentication (NTLM challenge-response)
# Look for NTLM auth events (may indicate pass-the-hash)
tshark -r capture.pcap \
  -Y "ntlmssp" \
  -T fields \
  -e frame.time \
  -e ip.src \
  -e ip.dst \
  -e ntlmssp.identifier

# Extract files transferred via SMB
# Zeek SMB file extraction:
zeek -r capture.pcap local --no-checksums FileExtract::PREFIX=./smb_extracted/
cat files.log | grep "smb\|SMB"

Building a Network Attack Timeline

#!/bin/bash
# Generate a network timeline from PCAP for IR report
PCAP="WS-JSMITH-01_network_20240902.pcap"
ATTACKER_IP="185.220.101.9"
OUTPUT="network_timeline.txt"

echo "=== NETWORK ATTACK TIMELINE ===" > "$OUTPUT"
echo "Source PCAP: $PCAP" >> "$OUTPUT"
echo "Analysis date: $(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "$OUTPUT"
echo "" >> "$OUTPUT"

echo "=== FIRST CONTACT ===" >> "$OUTPUT"
tshark -r "$PCAP" \
  -Y "ip.addr == $ATTACKER_IP" \
  -T fields \
  -e frame.time \
  | head -1 >> "$OUTPUT"

echo "" >> "$OUTPUT"
echo "=== ALL CONNECTIONS TO/FROM ATTACKER ===" >> "$OUTPUT"
tshark -r "$PCAP" \
  -Y "ip.addr == $ATTACKER_IP" \
  -T fields \
  -e frame.time \
  -e ip.src \
  -e ip.dst \
  -e tcp.dstport \
  -e tcp.srcport \
  -e tcp.len \
  >> "$OUTPUT"

echo "" >> "$OUTPUT"
echo "=== HTTP REQUESTS TO ATTACKER ===" >> "$OUTPUT"
tshark -r "$PCAP" \
  -Y "ip.addr == $ATTACKER_IP and http.request" \
  -T fields \
  -e frame.time \
  -e http.request.method \
  -e http.request.uri \
  -e http.user_agent \
  -e http.content_length \
  >> "$OUTPUT"

echo "" >> "$OUTPUT"
echo "=== FILES TRANSFERRED ===" >> "$OUTPUT"
zeek -r "$PCAP" local --no-checksums 2>/dev/null
cat files.log | zeek-cut ts source sha256 filename | \
  grep "$ATTACKER_IP" >> "$OUTPUT"

echo "Timeline written to $OUTPUT"