Skip to content
Go back

Zeek for Home Lab Forensics

By SumGuy 11 min read
Zeek for Home Lab Forensics

Suricata Yelled, But It Didn’t Explain

You know the feeling. Your IDS fires an alert. “SSH_OUTBOUND_ANOMALY_DETECTED” or whatever. Great. Now what? Did someone actually get in? Was it a false positive? What exactly happened? Suricata and Snort are alarmists — they see something that matches a rule and scream. Useful, sure. But they leave you holding the bag.

Zeek is different. Zeek doesn’t care about matching signatures. It’s a programmable network analysis framework that watches traffic and spits out events — structured, timestamped, queryable records of what actually occurred on your network. DNS lookups, HTTP requests, file transfers, TLS handshakes, SMB sessions. Zeek documents everything, then gets out of your way.

Think of it like the difference between a security guard shouting “SOMEONE BROKE IN!” versus a forensics team documenting exactly who, when, how, and what they touched. You’re building a home lab where you can actually investigate what happened, not just react to alarms.

What Zeek Actually Is (And Isn’t)

Here’s the bit that confuses people: Zeek is not an IDS, even though it looks like one. It doesn’t have a signature database. It doesn’t say “this traffic is malicious.” What it does is turn network traffic into structured logs.

You point Zeek at a packet stream (a SPAN mirror port, a PF_RING tap, or even a pcap file), and it assembles that chaos into log entries. Connections. DNS queries. HTTP requests. File transfers. TLS certificates. SMB commands. Each log is tab-separated, machine-readable, and timestamped to the microsecond.

Want to know every DNS query your Plex server made last Tuesday? Query dns.log.

Want a timeline of all outbound HTTPS connections from your lab subnet in the last 24 hours? conn.log + grep + sort.

Want to extract the actual binary files that crossed your network? files.log tells you which ones and where Zeek cached them.

That’s the magic: Zeek is a translator. It speaks packet. Your logs speak English (or JSON, or whatever format you want).

Zeek vs. Suricata vs. Snort

AspectZeekSuricataSnort
PurposeNetwork analysis + scriptingSignature-based IDS/IPSSignature-based IDS/IPS
OutputStructured logs (conn, dns, http, ssl, files, x509, smb, …)Alerts + eve.jsonAlerts + unified2
Queryinggrep, zeek-cut, Elastic, ClickHousegrep, ELK, Suricata Eve outputgrep, Splunk
CustomizationZeek script language (Bro DSL)Lua pluginsC plugins
CPU overheadHigher (protocol analysis is deep)ModerateModerate
Learning curveSteep (custom language)ModerateModerate
Best forForensics, hunting, baseliningIntrusion detectionIntrusion detection

Real talk: Suricata will yell faster. Zeek will tell you the full story. In a home lab, you want the story. Speed doesn’t matter at 1 Gbps on a home connection.

The Zeek Logs You’ll Actually Use

When Zeek runs, it writes a bunch of logs to a directory (usually logs/). Here are the ones worth knowing:

Setting Up Zeek in a Home Lab

Docker Compose (the Easy Path)

Docker is your friend here. The zeek/zeek image has everything baked in.

docker-compose.yml
version: '3.8'
services:
zeek:
image: zeek/zeek:latest
container_name: zeek
cap_add:
- NET_ADMIN
volumes:
- ./logs:/opt/zeek/logs
- ./share/zeek/site:/opt/zeek/share/zeek/site
environment:
HOME: /opt/zeek
networks:
- zeek_net
# If you're using a SPAN mirror or PF_RING tap:
command: zeek -i eth0 -C
networks:
zeek_net:
driver: bridge

Key flags:

The container writes logs to ./logs/, which persists on your host.

For Live Capture: SPAN or PF_RING

Your home lab switch probably has port mirroring (SPAN on Cisco, monitoring on Ubiquiti, traffic mirroring on Mikrotik). Point the mirror to a dedicated NIC on your Zeek box, then run Zeek on that interface. Zero impact on production traffic.

If your switch is dumb and has no mirroring, PF_RING is an option — it’s a kernel module that lets you tap traffic at line rate. But honestly, for a home lab, a mirror port is simpler.

Actually Using Zeek Logs

zeek-cut: The Best Friend Tool

zeek-cut is a tiny utility that extracts columns from Zeek logs. Way better than awk.

Terminal window
# All HTTPS connections from your lab subnet in the last day
zeek-cut id.orig_h id.resp_h id.resp_p proto < logs/conn.log | grep 443
# Every DNS lookup for suspicious domains (exfiltration attempt detection)
zeek-cut ts query answer < logs/dns.log | grep -i "\.ru\|\.xyz" | tail -20
# All HTTP requests to IPs outside your local range
zeek-cut ts id.orig_h host uri < logs/http.log | grep -v "192.168\|10\."

Sample conn.log Entry

Here’s what a real line looks like (fields tab-separated):

1576944000.000000 CXk3dJ4pP13Qs84dMj 192.168.1.100 54321 8.8.8.8 53 udp - - - - 0 53 1 53 0 S0 - - 0 Df - - - -

That’s: timestamp, flow ID, src IP, src port, dst IP, dst port, protocol, state, and a bunch of flags. Parse it with zeek-cut or load it into Elastic.

Writing Custom Zeek Scripts

Here’s where Zeek gets powerful. You can write scripts to detect anomalies, extract events, or trigger actions.

A simple example: detect unusually long DNS names (often used for exfiltration over DNS):

detect-long-dns.zeek
# Detect DNS queries with suspiciously long domain names
# Exfil-over-DNS often uses long subdomains to hide data
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) {
# Check if the domain name (without the root .) exceeds 100 characters
if (|query| > 100) {
print fmt("LONG_DNS: %s -> %s for %s (length: %d)",
c$id$orig_h, c$id$resp_h, query, |query|);
# You could also trigger an alert here
# NOTICE([t=c$start_time, uid=c$uid, $msg="Suspicious long DNS name"]);
}
}

Drop this in your Zeek site directory (share/zeek/site/), restart Zeek, and it’ll start logging long DNS names.

The Zeek language looks like C, but it’s actually event-driven: you write handlers for things like dns_request, http_request, file_over_http, etc. Each event fires as Zeek parses the traffic.

Integrating with Elasticsearch / OpenSearch

Raw logs are searchable, but indexing them in Elastic makes them useful. Set up an ELK stack (or OpenSearch if you want the open-source route), point Zeek’s logs at a Filebeat instance, and suddenly you have a searchable interface.

Alternatively, use Corelight (the commercial Zeek fork) which bakes in better Elastic integration. Or SecurityOnion, which is a full security monitoring distro built on Zeek + Suricata + Elastic.

For a home lab, though, plain Zeek + Elastic is overkill. zeek-cut + grep + tail will get you 90% of the way there.

Real Home Lab Use Cases

Catching a Smart TV Phone Home

Your new Roku TV starts making connections to random IPs. Query conn.log:

Terminal window
zeek-cut ts id.orig_h id.resp_h id.resp_p < logs/conn.log | grep "192.168.1.50"

See outbound connections to Akamai? To ad networks? That’s your data. Block the IP at the firewall if it’s sketchy. Zeek let you investigate instead of guessing.

Detecting Torrent Leaks

A roommate runs a torrent client. Zeek catches the DHT and peer traffic:

Terminal window
zeek-cut id.orig_h id.resp_p proto < logs/conn.log | grep "6881\|6889"

Boom. Bittorrent ports. Or you run a script that detects unusual connection patterns (lots of short connections, many peers, high port numbers) — hallmarks of P2P.

Lateral Movement Detection

If a device on your lab subnet suddenly starts scanning other devices (port scans, SMB probes, SSH attempts), Zeek’s conn.log shows it:

Terminal window
zeek-cut id.orig_h id.resp_h id.resp_p state < logs/conn.log | sort | uniq -c | sort -rn | head -20

Count connections per source host. If a device goes from zero connections to hundreds, it’s probably doing something.

File Extraction and Hashing

Zeek can extract files from network traffic and hash them. Check files.log:

Terminal window
# All EXEs seen on your network
zeek-cut md5 sha256 filename mime_type < logs/files.log | grep -i "exe\|dll"

Query VirusTotal with the hash. If it’s malware, you now know which device sent it and when.

JA3/JA4 Fingerprinting

Newer Zeek logs TLS client fingerprints (JA3/JA4). These are cryptographic hashes of the TLS handshake itself — unique per client software. Catch tools like:

Query ssl.log:

Terminal window
zeek-cut ts id.orig_h ja3 < logs/ssl.log | grep "YOUR_KNOWN_BAD_JA3_HERE"

Performance Notes

Zeek is hungry. A 1 Gbps saturated connection will chew ~2 cores and 8 GB RAM. For a home lab doing 10-100 Mbps average, one core and 2 GB RAM is fine.

If you’re on a Raspberry Pi: don’t. Zeek needs real hardware.

If you’re on a beefy server: Zeek scales well. Throw threads at it (-t flag), pin it to cores, run PF_RING for kernel bypass.

One More Thing: Beaconing Detection

Malware and C2 frameworks “phone home” on a regular schedule (every hour, every 12 hours, etc.). Zeek can detect this with a simple script:

detect-beaconing.zeek
# Simplified beaconing detection: same destination, regular intervals
global beacon_candidates: table[addr, addr, port] of vector of time;
event connection_established(c: connection) {
local key = [c$id$orig_h, c$id$resp_h, c$id$resp_p];
if (key !in beacon_candidates)
beacon_candidates[key] = vector();
beacon_candidates[key] += c$start_time;
# If we've seen 5+ connections to the same dest:port, check intervals
if (|beacon_candidates[key]| >= 5) {
local intervals: vector of interval = vector();
for (i in [0 : |beacon_candidates[key]| - 1])
intervals += (beacon_candidates[key][i+1] - beacon_candidates[key][i]);
# Calculate stddev; if too low, it's probably beaconing
# (This is pseudocode — real implementation needs stats)
print fmt("BEACON_SUSPECT: %s -> %s:%d (intervals: %s)",
key[0], key[1], key[2], intervals);
}
}

Real beaconing detection is more nuanced (you need proper stats, allow for jitter), but you get the idea.

Wrapping Up: Your Network Forensics Foundation

Zeek won’t prevent breaches. It won’t stop malware. But it’ll document everything that happens on your network in excruciating detail, and let you ask questions later.

“Did anyone exfiltrate data last week?” Query dns.log and http.log.

“What’s that device doing at 3 AM?” Check conn.log.

“Is this file known malware?” Hash it from files.log, check VirusTotal.

That’s forensics. That’s investigation. That’s what separates “I got hacked and have no idea what happened” from “I got hacked, here’s exactly what they did, and here’s the evidence.”

Set up a SPAN port on your lab switch, spin up Zeek in Docker, point it at the mirror, and let it document. In six months, you’ll have a baseline of your network. And when something does go sideways, you’ll have the receipts.

Your 2 AM self — the one who’s trying to figure out why there’s a weird connection in the logs — will thank you.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Previous Post
NVMe Heatsinks That Actually Cool
Next Post
Neo4j vs ArangoDB: Graph DB Showdown

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts