Chapter 16

tshark Statistics

tshark's -z flag generates statistical summaries directly from PCAP files — top TCP conversations by bytes, HTTP request breakdown, DNS query distribution, I/O time-bucketed packet counts. These statistics give you the same orientation view as Wireshark's GUI statistics, but scriptably and against multi-gigabyte files that would be impractical to open in the GUI.

Scenario

You have a 4 GB PCAP from a 24-hour window. You need to answer three questions in the first 5 minutes: (1) Which internal host generated the most outbound traffic? (2) What are the top DNS query domains? (3) At what time did the traffic spike occur? Three tshark -z commands, three answers, no GUI required and no 10-minute file load time.

-z Statistics Reference

Bashtshark-statistics.sh
PCAP="merged.pcap"

# ── CONVERSATION STATISTICS ───────────────────────────────────────────
# Top TCP conversations by bytes (exfiltration candidates at top)
tshark -r $PCAP -n -q -z "conv,tcp" 2>/dev/null | head -30
# Output:
# TCP Conversations
# | Address A        <-> Address B          Frames  Bytes    Frames  Bytes  Total Frames Total Bytes  Duration  bps
# | 10.0.1.50:49812  <-> 185.220.101.47:443    234   3.5 kB     189   47 MB    423       47 MB       3612 s  103 kbps

# Top UDP conversations (DNS, QUIC)
tshark -r $PCAP -n -q -z "conv,udp" 2>/dev/null | head -20

# Top IP conversations (source+dest without port — higher level view)
tshark -r $PCAP -n -q -z "conv,ip" 2>/dev/null | head -20

# ── ENDPOINT STATISTICS ───────────────────────────────────────────────
# All unique IPs ranked by bytes
tshark -r $PCAP -n -q -z "endpoints,ip" 2>/dev/null | sort -k5 -rn | head -20

# ── HTTP STATISTICS ───────────────────────────────────────────────────
# All HTTP request URIs (frequency count)
tshark -r $PCAP -n -q -z "http,requests" 2>/dev/null | head -30
# Shows: URI | count | avg size

# HTTP request method breakdown
tshark -r $PCAP -n -q -z "http_req,tree" 2>/dev/null

# ── DNS STATISTICS ────────────────────────────────────────────────────
# DNS query type breakdown + top queried domains
tshark -r $PCAP -n -q -z "dns,tree" 2>/dev/null | head -50
# Shows query type counts + most frequent domains

# ── I/O STATISTICS (TIME BUCKETS) ────────────────────────────────────
# Packet count per 10-second bucket — shows traffic timeline
tshark -r $PCAP -n -q -z "io,stat,10" 2>/dev/null
# Output: | Interval | Frames | Bytes |
# | 0.000  <> 10.000 | 8293 | 3.2 MB |
# | 10.000 <> 20.000 | 7841 | 3.0 MB |
# Look for interval with dramatically higher count = attack window

# I/O with filter: count only external destination packets per second
tshark -r $PCAP -n -q \
    -z "io,stat,60,ip.dst!=10.0.0.0/8 and ip.dst!=192.168.0.0/16" 2>/dev/null

# ── FLOW VOLUME BY DESTINATION PORT ───────────────────────────────────
tshark -r $PCAP -n -q -z "conv,tcp" 2>/dev/null | \
    awk '{print $3}' | cut -d: -f2 | sort | uniq -c | sort -rn | head -20
# Shows which destination ports have the most conversations

# ── COMBINED ANALYSIS SCRIPT ──────────────────────────────────────────
echo "=== Top 10 TCP Conversations by Bytes ==="
tshark -r $PCAP -n -q -z "conv,tcp" 2>/dev/null | \
    sort -k8 -rn | head -10

echo ""
echo "=== Traffic Timeline (1-minute buckets) ==="
tshark -r $PCAP -n -q -z "io,stat,60" 2>/dev/null | head -30

echo ""
echo "=== Top DNS Query Domains ==="
tshark -r $PCAP -n -q \
    -Y "dns.flags.response==0" \
    -T fields -e dns.qry.name 2>/dev/null | \
    sort | uniq -c | sort -rn | head -20

Time-Bucketed Statistics for Beaconing Detection

Bashbeacon-stats-tshark.sh
PCAP="merged.pcap"
SUSPECT_DST="185.220.101.47"

# Step 1: Extract per-second packet count from suspect session
# This shows beaconing visually as a time series
echo "Packet counts per 60 seconds to $SUSPECT_DST:"
tshark -r $PCAP -n -q \
    -z "io,stat,60,ip.dst==$SUSPECT_DST" 2>/dev/null | \
    grep -v "^=" | grep "[0-9]"

# Step 2: Extract SYN timestamps for precise interval analysis
echo ""
echo "SYN timestamps to $SUSPECT_DST port 443:"
tshark -r $PCAP -n \
    -Y "tcp.flags.syn==1 and not tcp.flags.ack and ip.dst==$SUSPECT_DST and tcp.dstport==443" \
    -T fields -e frame.time_epoch 2>/dev/null | \
    awk 'prev != "" {printf "%.3f\n", $1 - prev} {prev=$1}' | \
    sort -n

# The output will show intervals between connection attempts.
# Consistent intervals (e.g., all around 60.0 seconds) = beaconing
# High variance = normal application behavior

# Step 3: Compute statistics on intervals
tshark -r $PCAP -n \
    -Y "tcp.flags.syn==1 and not tcp.flags.ack and ip.dst==$SUSPECT_DST" \
    -T fields -e frame.time_epoch 2>/dev/null | \
    awk 'prev != "" {d=$1-prev; sum+=d; sumsq+=d*d; n++} {prev=$1} END {
        if (n>1) {
            mean=sum/n
            variance=sumsq/n - mean*mean
            stddev=sqrt(variance)
            cv=stddev/mean
            printf "Count: %d\nMean interval: %.2f sec\nStddev: %.2f sec\nCV: %.3f\n", n, mean, stddev, cv
            if (cv < 0.2) print ">>> LOW CV: LIKELY BEACONING <<<"
        }
    }'
Mental model: -z statistics as free initial triage before any display filtering

The -z statistics flag processes the entire PCAP once and produces an aggregate summary — no per-packet output, just totals. This is orders of magnitude faster than applying display filters and reading individual packets. For a 4 GB PCAP, tshark -z conv,tcp -q runs in 60–90 seconds and gives you all TCP conversations with byte counts sorted — the exfiltration candidates are right at the top when you sort by bytes. Use -z statistics as your first pass on any large capture to orient before spending time on packet-level analysis. The seconds you invest in -z io,stat,60 (traffic timeline) and -z conv,tcp (top talkers) are the highest-ROI 2 minutes in any PCAP investigation.

Q & A

Q: The -z conv,tcp output shows a conversation with 50 MB sent but the duration is only 2 seconds. Is that exfiltration?

A 50 MB transfer in 2 seconds = 25 MB/s = 200 Mbps — achievable on a corporate gigabit LAN or fast internet connection. Whether this is exfiltration depends on context: (1) Direction: 50 MB sent FROM an internal host TO an external IP is a strong exfiltration indicator. 50 MB received from an external host to an internal one could be a large file download (software update, video file). Check which column shows 50 MB (A→B vs B→A). (2) Destination: look up the destination IP — is it a known CDN (Akamai, Fastly, AWS), which suggests a download? Or an IP that doesn't resolve to any known service? Unknown/new IP + high volume + outbound = high priority. (3) Protocol: HTTPS to port 443 is normal; HTTPS to an unusual port like 8443 or 4443 is suspicious. (4) Baseline: does the source host normally transfer large files? An engineering workstation might legitimately send large code pushes. A finance workstation sending 50 MB to an external IP is highly anomalous. Follow up by extracting the actual TCP stream from Wireshark to see what protocol and content was in the 50 MB.