Build a PCAP Triage Lab with Zeek and T-Shark: Spot C2 Beacons in Real Traffic

Build a PCAP Triage Lab with Zeek and T-Shark: Spot C2 Beacons in Real Traffic

📋 Key Takeaways
  • Pair Zeek's conn.log and ssl.log with tshark display filters to surface C2 beaconing, JA3 hashes, and DNS tunnels from any capture — in under an hour, you can build a repeatable triage lab that turns network evidence into a defensible verdict.
  • Start with a hardened Linux VM or container—Ubuntu 22.04 LTS works fine.
  • You need real, known-bad traffic to calibrate your instincts.
  • Zeek's offline processing mode is the workhorse of triage.
10 min read · 1,885 words
Educational & Ethical Use Only — This article is provided for educational and ethical cybersecurity research purposes only. The techniques described should only be used on systems you own or have explicit permission to test. Always follow responsible disclosure and the laws applicable to you. Mitigations are included so engineers can harden real systems.
Security· 10 min read

TL;DR: Zeek and tshark turn raw PCAPs into beacon evidence in minutes

Pair Zeek’s conn.log and ssl.log with tshark display filters to surface C2 beaconing, JA3 hashes, and DNS tunnels from any capture — in under an hour, you can build a repeatable triage lab that turns network evidence into a defensible verdict.

Command-line network forensics has a well-established playbook: load the capture, eyeball the conversations, chase whatever looks weird. That workflow collapses the moment you’re handed a multi-gigabyte PCAP from an incident at 2 a.m. Modern triage demands automation—log generation, statistical analysis, fingerprint extraction—all of it repeatable and scriptable. Zeek and tshark give you exactly that: two complementary engines that turn raw packets into structured evidence without ever opening a GUI. This guide walks you through building that lab from scratch, using real malware captures you can legally download.

Lab setup: installing Zeek and tshark on Linux

Start with a hardened Linux VM or container—Ubuntu 22.04 LTS works fine. Zeek (formerly Bro, renamed in 2018 under the zeek.org project) is available via the OpenSUSE Build Service packages or source; tshark ships with Wireshark’s package family.

# tshark via distro packages
sudo apt update
sudo apt install -y tshark

# Zeek via the official OpenSUSE build service (Ubuntu 22.04)
echo 'deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_22.04/ /' | sudo tee /etc/apt/sources.list.d/zeek.list
curl -fsSL https://download.opensuse.org/repositories/security:/zeek/xUbuntu_22.04/Release.key | gpg --dearmor | sudo tee /etc/zeek-repo.key >/dev/null
sudo apt-key add /etc/zeek-repo.key
sudo apt update
sudo apt install -y zeek

# Or run Zeek from Docker if you prefer disposable labs
docker pull zeek/zeek

Verify the installs:

zeek --version   # expect zeek version 6.x or newer
tshark -v        # expect TShark (Wireshark) 4.x

Keep a clean directory layout so every case is reproducible:

~/triage/
├── pcaps/       # raw captures, never modified
├── logs/        # zeek output per case
├── scripts/     # your custom zeek scripts and awk tools
└── notes/       # findings, timeline, verdicts

Getting sample PCAPs: Malware-Traffic-Analysis and CTF captures

You need real, known-bad traffic to calibrate your instincts. The gold standard is Malware-Traffic-Analysis.net’s training exercises, curated by Brad Duncan. CISA and its Malware Initial Access report series also publish Indicators of Attack alongside PCAPs—see CISA’s threat advisories for current material. Good starter captures:

  • Emotet / Qakbot infection exercises — demonstrate HTTP callbacks, redirect chains, and post-exploitation beacons over TLS.
  • Cobalt Strike exercise captures — the canonical teaching sample for jittered C2 beaconing and certificate anomalies.
  • CTF archives (DEF CON, online CTF platforms) — frequently include DNS-tunneling challenges purpose-built for this kind of analysis.

Run every sample inside an isolated VM with no host networking. Analysis is safe; exposure is not.

First pass with Zeek: generating conn.log, dns.log, and ssl.log

Zeek’s offline processing mode is the workhorse of triage. From your case directory:

cd ~/triage/logs
zeek -r ../pcaps/sample.pcap local

Within seconds you’ll have a directory of structured, tab-separated logs. The three you’ll live in:

  • conn.log — one row per connection. Key fields: uid (unique connection ID), id.orig_h (source IP), id.resp_h (destination), duration, orig_bytes and resp_bytes (volume each direction), and ts (epoch timestamp).
  • dns.log — every DNS query and response, including query name, query type, and rcode.
  • ssl.log — TLS handshake metadata: version, cipher suites, server name (SNI), certificate details, and—critically—ja3 and ja3s fingerprints.

The immediate triage win: cut out the destination hosts your host talked to, sorted by bytes:

cat conn.log | zeek-cut id.orig_h id.resp_h orig_bytes resp_bytes | sort -k2 -n | tail -20

Detecting C2 beacons with Zeek’s interval analysis

How do you identify a C2 beacon in a PCAP using Zeek logs? Beacons follow a schedule—malware polls its operator on a fixed or jittered timer (commonly 30–60 seconds). That schedule leaves a fingerprint in conn.log: the same source/destination pair repeating with near-constant inter-arrival times and small, similar byte counts.

Zeek ships with script packages that automate this. The detect-traffic-cycles script (part of the core distribution under misc/) flags host pairs communicating in regular intervals:

# add to local.zeek or run standalone
@load misc/detect-traffic-cycles
zeek -r ../pcaps/sample.pcap local

For finer control, a minimal custom interval check over the logs works too:

# group conn.log by (src, dst, dst_port) and compute inter-arrival deltas
awk -F't' '$7=="tcp" {key=$3"-"$5"-"$6; if (ts[key]) {delta=$2-ts[key]; printf "%s %.2fn", key, delta} ts[key]=$2}' conn.log < /dev/null | sort | uniq -c | sort -rn | head

Inter-arrival deltas clustering tightly around one value—say 59.9, 60.1, 60.0, 59.8—is beacon behavior, full stop. Legitimate user traffic doesn’t keep a schedule.

Hunting beacons with tshark: display filters and statistical bucketing

tshark shines where you need surgical, repeatable extraction. What display filters isolate periodic TLS or HTTP callbacks?

# TLS traffic to a specific destination, plus handshake types
tshark -r ../pcaps/sample.pcap -Y 'tls.record.content_type == 23 && ip.dst == 203.0.113.50' -T fields -e frame.time_epoch -e ip.src -e ip.dst -e tcp.dstport

# Small periodic HTTP GET callbacks — a classic beacon signature
tshark -r ../pcaps/sample.pcap -Y 'http.request.method == "GET" && http.request.uri == "/"' -T fields -e frame.time_epoch -e ip.dst

Then bucket the inter-arrival times into a histogram with awk to defeat small amounts of jitter:

tshark -r ../pcaps/sample.pcap -Y 'ip.dst==203.0.113.50 && tcp.dstport==443' -T fields -e frame.time_epoch 
| awk 'NR>1 {d=$1-prev; printf "%dn", int(d/5)*5} {prev=$1}' | sort -n | uniq -c

Ten callbacks landing in one five-second bucket is a beacon. This is the technique behind dedicated beacon hunters like RITA (below)—you’re just doing it by hand, and understanding it makes the automated scoring legible.

Extracting JA3 fingerprints from ssl.log and handshakes

How is a JA3 fingerprint extracted and matched against known malware? JA3 (by Salesforce engineers, open-sourced on GitHub) is an MD5 hash over the TLS Client Hello’s version, cipher suites, extensions, elliptic curves, and curve formats. Malicious frameworks—Cobalt Strike most famously—use distinctive, static TLS stacks, so their JA3 values match across campaigns.

Zeek computes this for you. Check the ja3 (client) and ja3s (server) columns in ssl.log:

cat ssl.log | zeek-cut id.orig_h id.resp_h server_name ja3 ja3s | sort -u

With tshark, parse the Client Hello directly:

tshark -r ../pcaps/sample.pcap -Y 'tls.handshake.type == 1' -T fields -e ip.src -e ip.dst -e tls.handshake.extensions_server_name

Copy the well-known JA3 hash associated with default Cobalt Strike (widely documented as 72a589da586844d7f0818ce684948eea) and cross-reference via your ThreatConnect/MISP instance or public JA3 search services. One caveat that matters: JA3 is a correlation signal, not proof. Common libraries—Go’s crypto/tls, curl, Python requests—share fingerprints across benign and malicious software. Treat matching JA3 as a lead, then confirm with the interval analysis and DNS evidence above.

Spotting DNS tunnels: high-entropy subdomains and query ratios

What patterns indicate DNS tunneling in dns.log? Tunneling tools (dnscat2, iodine, DNSExfiltrator) encode exfiltrated data into subdomain labels—frequently base32 or base64—which produces two signatures:

  • Abnormally long, high-entropy query names — 40+ character labels with no dictionary structure.
  • Extreme query ratios — hundreds of unique subdomains under a single authoritative domain, mostly TXT or NULL record types.
# longest queries in dns.log
cat dns.log | zeek-cut query qtype_name | awk '{print length($1), $1, $2}' | sort -rn | head -20

# tshark: count TXT queries per domain
tshark -r ../pcaps/sample.pcap -Y 'dns.qry.type == 16' -T fields -e dns.qry.name | cut -d'.' -f3- | sort | uniq -c | sort -rn

High entropy is a quick screen—shannon entropy above ~3.5 bits per character on a long label is suspicious. The OWASP foundation’s documentation on data exfiltration channels, and CISA’s advisories on DNS abuse, both emphasize unique-subdomain counts per domain as the more discriminating metric: hundreds of unique queries under one zone in minutes is tunneling until proven otherwise.

Correlating findings: building a triage workflow from PCAP to verdict

Individually, each technique produces leads. Together, they produce verdicts. Your repeatable checklist:

  1. Generate logs: zeek -r case.pcap local.
  2. Rank external destinations by byte volume and connection count from conn.log.
  3. Run interval analysis on the top talkers; flag host pairs with tight inter-arrival clustering.
  4. Pull JA3/ja3s for every flagged pair; check against known-malware fingerprint lists.
  5. Screen DNS: long high-entropy queries and unique-subdomain ratios per domain.
  6. Cross-check IPs and domains against threat intel—CISA advisories, abuse.ch feeds, your SIEM’s enrichment.
  7. Write the verdict: beaconing confirmed/suspected/cleared, with the timestamped evidence rows attached.

Zeek and tshark complement each other here by design: Zeek gives you broad, structured coverage of the entire capture in one pass; tshark gives you surgical, scriptable extraction when you need to interrogate one conversation or validate a hypothesis. Wireshark’s GUI remains useful for the final eyeball, but it doesn’t scale and it isn’t repeatable—your checklist is.

Extending the lab: RITA, Zeek Intel framework, and automated alerting

Once manual triage works, automate it:

  • RITA (Real Intelligence Threat Analytics) — consumes Zeek logs and applies statistical beacon scoring (including variance and jitter tolerance) plus DNS tunnel detection out of the box. It’s the industrialized version of the awk histograms you just built.
  • Zeek Intel framework — load indicator feeds (IPs, domains, JA3 hashes) and have Zeek annotate or raise notices when PCAP traffic matches. Point it at abuse.ch’s Feodo Tracker or CISA’s AIS feeds.
  • SIEM piping — ship Zeek logs via Filebeat or Vector to Elastic/Splunk, then build saved detections on the same interval-math you validated manually.

Build the lab once. Every capture after that becomes a twenty-minute exercise instead of a night of scroll-scroll-scrolling in Wireshark—and that difference is what separates responders who keep up from responders who drown.

Frequently Asked Questions

What is a C2 beacon and why does it show up as regular intervals?

Malware polls its operator on a timer or jittered schedule—checking in every 30 or 60 seconds, for instance. That polling creates repeatable inter-arrival times between connections to the same destination, which show up plainly in conn.log timestamps as deltas clustering around a constant value.

Do I need Zeek and tshark, or is Wireshark enough?

Wireshark works for manual inspection of small captures. But Zeek automates structured log generation across the whole file, and tshark enables scripted, repeatable extraction—essential when your PCAP is gigabytes or you need to process dozens of captures per week.

Can JA3 fingerprints give false positives?

Yes—common TLS libraries share fingerprints across benign and malicious software. JA3 is a correlation signal, not proof of malware; always corroborate with behavioral evidence like beacon intervals and DNS patterns.

Where can I legally download malicious PCAPs for practice?

Malware-Traffic-Analysis.net and CTF archives provide curated captures for defensive training. Analyze them offline in an isolated lab VM with no host network access.

How does jitter affect beacon detection?

Attackers randomize intervals slightly to defeat exact-match timing. Bucketing arrival times into histograms (e.g., five-second buckets) absorbs jitter while still revealing the schedule—this is exactly how RITA’s beacon scoring works.

Hmmnm
Published by Hmmnm

Hands-on cybersecurity tutorials, CVE breakdowns, and guided learning paths — written and lab-tested by the Hmmnm team.

🛡️ Hmmnm also delivers this expertise as a service — security testing, assessment & training.
Keep going — the structured way
This post is one step. The learning paths chain the next ones for you, with progress tracking and no account needed.
Follow a learning path →

Prabhu Kalyan Samal

Application Security Consultant at TCS. Certifications: CompTIA SecurityX, Burp Suite Certified Practitioner, Azure Security Engineer, Azure AI Engineer, Certified Red Team Operator, eWPTX v3, LPT, CompTIA PenTest+, Professional Cloud Security Engineer, SC-900, SC-200, PSPO I, CEH, Oracle Java SE 8, ISP, Six Sigma Green Belt, DELF, AutoCAD. Writing about ethical hacking, security tutorials, and tech education at Hmmnm.