Zeek (formerly named Bro, renamed in 2018) is an open-source network security monitoring (NSM) framework rather than a simple packet capture or display tool. Where Wireshark, Tshark, and tcpdump focus on showing individual packets, and NetworkMiner focuses on carving artifacts, Zeek operates at a higher level of abstraction: it sits passively on network traffic (live or from a pcap file), reconstructs full application-layer sessions, and generates rich, structured, tab-separated (or JSON) log files describing every connection, DNS lookup, HTTP transaction, TLS handshake, file transfer, SSH session, and dozens of other protocol interactions — continuously, at scale, over long periods of time.
Zeek’s power comes from its event-driven scripting engine. As Zeek’s core parses traffic, it generates a stream of events (connection_established, http_request, dns_request, file_new, etc.), and Zeek scripts (written in Zeek’s own domain-specific language) subscribe to and react to these events. This makes Zeek extremely extensible: analysts and security teams write custom detection scripts that trigger alerts, extract files, tag traffic, or correlate behavior across many connections — something individual-packet tools cannot easily do.
Zeek is heavily used in security operations centers (SOCs), enterprise network security monitoring, threat hunting, and behavioral/anomaly detection at scale, often alongside tools like Suricata (IDS/IPS) and SIEM platforms that ingest Zeek’s logs. In Kali Linux, Zeek is available via the official repositories or the Zeek project’s own APT repository, and is commonly used for both live sensor deployment and offline forensic analysis of large pcap files.
How to Install
Zeek may not be present in the default Kali repositories in all releases, so the recommended method is to add the official Zeek OBS (openSUSE Build Service) repository:
# Install prerequisites
sudo apt update
sudo apt install -y curl gnupg lsb-release
# Add the Zeek repository (Debian-based, adjust release codename if needed)
echo 'deb http://download.opensuse.org/repositories/security:/zeek/Debian_12/ /' | \
sudo tee /etc/apt/sources.list.d/security:zeek.list
curl -fsSL https://download.opensuse.org/repositories/security:zeek/Debian_12/Release.key | \
sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/security_zeek.gpg
sudo apt update
sudo apt install zeek -y
Alternatively, if available directly in Kali’s repos:
sudo apt update
sudo apt install zeek -y
Add Zeek’s binaries to your PATH (commonly installed under /opt/zeek):
echo 'export PATH=/opt/zeek/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
Verify installation:
zeek --version
Expected output:
zeek version 6.2.1
Check ZeekControl (used for managing a running deployment), if installed:
zeekctl status
Expected output:
Name Type Host Status Pid Started
zeek standalone localhost running 4821 19 Jul 10:15:03
Syntax
zeek [OPTIONS] [ <policy files / pcap file> ]
Common patterns:
zeek -r capture.pcap # Offline analysis of a pcap file
zeek -i eth0 # Live capture on interface eth0
zeek -r capture.pcap local # Offline analysis with the "local" default script bundle
zeekctl deploy # Deploy/restart a live monitoring cluster
zeek-cut id.orig_h id.resp_h < conn.log # Extract specific fields from a Zeek log
All Command-Line Options (Kali Linux)
-h, --help Print help and exit
-v, --version Print version information and exit
Capture:
-i <interface> Read packets live from the specified interface
-r <file> Read packets from the specified pcap file (offline)
-w <file> Write packets to the specified pcap file
-p <prefix> Add a prefix for capture filter reference
-f <filter> BPF filter to apply to captured traffic
-C Ignore checksum errors
-s Chroot to the Zeek log directory (privilege drop)
Execution / scripting:
-a Enter "analysis mode" (do not enforce IPv4-only)
-b Bare mode - do not load standard/default scripts
-e <zeek code> Execute a snippet of Zeek code before loading scripts
-j Use JSON output for logging instead of default TSV
-N <plugin> Show information about the named plugin
-R <trace> Rerun a saved trace against loaded scripts
-S Enable step-by-step debugging of the script engine
-U <file> Record state into the given file for restarting
Environment / deployment:
-B <options> Enable specific debug streams
-D Enable Zeek's own internal debug output
--pseudo-realtime[=speedup] Replay a pcap file with realistic timing
-G <file> Generate/read seeds from the given file
-Y Enable "auto-detect" of new event handlers
-z script tag Enable a specific analyzer tag
Environment for zeekctl (cluster/standalone management, separate command):
zeekctl install Install/update configuration to all nodes
zeekctl deploy Install and restart Zeek (common one-step deploy)
zeekctl start / stop / restart Control the Zeek process(es)
zeekctl status Show running status of all nodes
zeekctl diag Collect diagnostics for troubleshooting
zeekctl top Show resource usage of running Zeek processes
zeekctl netstats Show packet capture statistics
zeekctl cron Manage log rotation / periodic maintenance
Zeek’s own scripting language (.zeek files) is a separate, extensive syntax used for writing detection logic; it is loaded rather than passed as a flag, e.g. zeek -r capture.pcap my_script.zeek.
Basic Usage (Expected Output in Bash)
Analyze a pcap file offline (most common initial use):
zeek -r capture.pcap
Expected output (terminal):
(no verbose stdout by default; Zeek silently processes the file and writes
log files into the current working directory)
List the log files generated:
ls
Output:
conn.log dns.log files.log http.log
notice.log packet_filter.log reporter.log ssl.log
weird.log x509.log
View a sample of the primary connection log:
head -n 5 conn.log
Output:
#separator \x09
#set_separator ,
#empty_field (empty)
#unset_field -
#path conn
#fields ts uid id.orig_h id.orig_p id.resp_h id.resp_p proto service duration
1721385761.221034 CHhAvVGS1DHFjwGM9 192.168.1.10 51322 93.184.216.34 443 tcp tls 14.221
Practical Examples with Output
Example 1: Run Zeek against a pcap with the full default script bundle
zeek -r incident.pcap local
Output:
(Generates conn.log, dns.log, http.log, ssl.log, files.log, notice.log,
weird.log, and more in the current directory.)
Example 2: Live capture on an interface
sudo zeek -i eth0
Output:
listening on eth0
(Zeek runs continuously, writing/rotating logs such as conn.log every hour
by default until stopped with Ctrl+C.)
Example 3: View HTTP requests logged from a capture
cat http.log | zeek-cut ts id.orig_h host uri status_code
Output:
1721385762.001 192.168.1.10 example.com /index.html 200
1721385763.552 192.168.1.10 example.com /style.css 200
1721385770.113 192.168.1.10 malicious.biz /payload.exe 200
Example 4: View DNS queries logged from a capture
cat dns.log | zeek-cut ts id.orig_h query answers
Output:
1721385761.221 192.168.1.10 example.com 93.184.216.34
1721385762.884 192.168.1.10 malicious.biz 185.220.101.5
Example 5: Extract and review any files detected/extracted from traffic
cat files.log | zeek-cut ts fuid tx_hosts rx_hosts source mime_type filename
Output:
1721385770.552 FpqR8y2z 93.184.216.34 192.168.1.10 HTTP application/x-dosexec payload.exe
1721385780.100 FhT92kLm 151.101.1.69 192.168.1.10 HTTP application/pdf invoice.pdf
Example 6: Review generated notices (Zeek’s built-in alerting)
cat notice.log | zeek-cut ts id.orig_h note msg
Output:
1721385770.552 192.168.1.10 SSL::Invalid_Server_Cert SSL certificate validation failed for malicious.biz
1721385790.221 192.168.1.10 Scan::Port_Scan 192.168.1.10 scanned 20+ ports on 10.0.0.5
Example 7: Convert Zeek logs to JSON output for SIEM ingestion
zeek -r incident.pcap -j local
head -n 2 conn.log
Output:
{"ts":1721385761.221034,"uid":"CHhAvVGS1DHFjwGM9","id.orig_h":"192.168.1.10","id.orig_p":51322,"id.resp_h":"93.184.216.34","id.resp_p":443,"proto":"tcp","service":"tls","duration":14.221}
{"ts":1721385762.884112,"uid":"CTn82nQpz8x1abcd","id.orig_h":"192.168.1.10","id.orig_p":55123,"id.resp_h":"8.8.8.8","id.resp_p":53,"proto":"udp","service":"dns","duration":0.021}
Example 8: Replay a pcap in “pseudo-realtime” to simulate live traffic timing
zeek -r capture.pcap --pseudo-realtime local
Output:
(Zeek processes the file but paces event generation to match the original
packet timestamps, useful for testing live-response scripts against
recorded traffic.)
Example 9: Write and load a custom detection script
cat > detect_ftp.zeek << 'EOF'
event ftp_request(c: connection, command: string, arg: string)
{
if ( command == "USER" || command == "PASS" )
print fmt("FTP credential seen: %s %s from %s", command, arg, c$id$orig_h);
}
EOF
zeek -r ftp_capture.pcap detect_ftp.zeek
Output:
FTP credential seen: USER admin from 192.168.1.10
FTP credential seen: PASS Sup3rSecret! from 192.168.1.10
Example 10: Check Zeek’s own weird.log for protocol anomalies
cat weird.log | zeek-cut ts id.orig_h id.resp_h name
Output:
1721385900.112 192.168.1.10 93.184.216.34 TCP_ack_underflow_or_misc
1721385920.552 10.0.0.7 10.0.0.9 DNS_unmatched_reply
Example 11: Deploy Zeek as a persistent live sensor using zeekctl
sudo zeekctl deploy
sudo zeekctl status
Output:
checking configurations ...
installing ...
removing old policies ...
generating standalone-layout.zeek ...
generating local-networks.zeek ...
generating zeekctl-config.sh ...
stopping ...
starting zeek ...
Name Type Host Status Pid Started
zeek standalone localhost running 5122 19 Jul 11:02:10
Example 12: Search all connection logs for a specific suspicious IP
cat conn.log | zeek-cut id.orig_h id.resp_h id.resp_p proto | grep "185.220.101.5"
Output:
192.168.1.10 185.220.101.5 443 tcp
192.168.1.10 185.220.101.5 8080 tcp
Common Use Cases
- Continuous, always-on network security monitoring deployed at a network tap or SPAN port in a SOC environment.
- Threat hunting: querying months of structured
conn.log/dns.log/http.logdata for indicators of compromise, far faster than re-parsing raw pcaps. - Behavioral detection: writing custom Zeek scripts to flag anomalies such as beaconing intervals, DNS tunneling, or long-lived low-and-slow connections.
- File extraction and malware analysis: automatically carving files transferred over the network for later sandboxing/hash lookups.
- Compliance and audit logging: maintaining a structured, queryable record of all network activity for a given retention period.
- Feeding log pipelines: Zeek logs are commonly shipped into ELK/Splunk/SIEM platforms for correlation with endpoint and other telemetry.
- Offline forensic analysis of large pcap captures where individual-packet review in Wireshark would be impractically slow.
Automation with Bash
Automated batch processing of many stored pcap files into per-file log directories:
#!/bin/bash
# zeek_batch.sh - process a directory of pcaps with Zeek, one output folder each
PCAP_DIR="/data/pcaps"
OUT_DIR="/data/zeek_logs"
mkdir -p "$OUT_DIR"
for pcap in "$PCAP_DIR"/*.pcap; do
base=$(basename "$pcap" .pcap)
mkdir -p "${OUT_DIR}/${base}"
(cd "${OUT_DIR}/${base}" && zeek -r "$pcap" local)
echo "[*] Processed $pcap -> ${OUT_DIR}/${base}"
done
Daily log rotation and IOC extraction script (intended to run via cron on a live sensor):
#!/bin/bash
# daily_ioc_report.sh - extracts suspicious activity from the previous day's Zeek logs
LOGDIR="/opt/zeek/logs/$(date -d 'yesterday' +%Y-%m-%d)"
REPORT="/data/reports/ioc_report_$(date -d 'yesterday' +%Y%m%d).txt"
mkdir -p /data/reports
{
echo "=== Notices ==="
zcat "$LOGDIR"/notice.*.log.gz 2>/dev/null | zeek-cut ts id.orig_h note msg
echo ""
echo "=== Suspicious file transfers (.exe) ==="
zcat "$LOGDIR"/files.*.log.gz 2>/dev/null | zeek-cut tx_hosts rx_hosts mime_type filename | grep -i "exe"
echo ""
echo "=== Top talkers by connection count ==="
zcat "$LOGDIR"/conn.*.log.gz 2>/dev/null | zeek-cut id.orig_h | sort | uniq -c | sort -rn | head -10
} > "$REPORT"
echo "[*] Daily IOC report written to $REPORT"
Cron entry to run the report automatically each morning:
# crontab -e
0 6 * * * /usr/local/bin/daily_ioc_report.sh
Tips and Best Practices
- Always load the
localscript bundle (zeek -r file.pcap local) rather than running Zeek in bare mode unless you specifically need minimal processing;localenables sane default detections and logging. - Use
zeek-cutrather than rawawk/cuton Zeek TSV logs — it correctly respects the#fieldsheader and column ordering, which can change between log types and Zeek versions. - For production deployments, use
zeekctlto manage Zeek as a service (start/stop/restart/deploy) rather than invokingzeekdirectly — it handles log rotation, process supervision, and cluster configuration. - Enable JSON logging (
-jor theJSON::Logframework) when feeding logs into a SIEM or Elasticsearch, since JSON is easier to ingest than raw TSV. - Write custom
.zeekscripts incrementally and test them offline against representative pcaps (zeek -r test.pcap myscript.zeek) before deploying live. - Regularly review
weird.log— while noisy, it often surfaces genuinely malformed or evasive traffic that other logs miss. - Tune
local-networks.zeekto accurately reflect your internal address space; many built-in scripts (e.g., scan detection) depend on correctly distinguishing internal from external hosts. - Monitor Zeek’s own resource usage (
zeekctl top) on high-throughput links; consider Zeek clusters (multiple worker processes) for line-rate monitoring beyond what a single process can handle.
Troubleshooting
Problem: zeek: command not found after installation. Solution: Zeek typically installs to /opt/zeek/bin; ensure this is added to your PATH (export PATH=/opt/zeek/bin:$PATH) and persisted in ~/.bashrc.
Problem: No interfaces found / permission denied on live capture. Solution: Run with sudo, or grant the Zeek binary capture capabilities: sudo setcap cap_net_raw,cap_net_admin=eip /opt/zeek/bin/zeek.
Problem: Logs are empty or missing expected protocols after analysis. Solution: Confirm you loaded the local script bundle (bare mode disables most analyzers); verify the pcap actually contains the expected traffic using capinfos/tshark first.
Problem: zeekctl deploy fails with configuration errors. Solution: Run zeekctl check to validate the configuration before deploying; review /opt/zeek/etc/node.cfg and networks.cfg for syntax errors.
Problem: High packet drop rate during live monitoring on a busy link. Solution: Increase capture buffer sizes, use PF_RING/AF_PACKET-based high-performance capture if available, or deploy a Zeek cluster with multiple worker processes rather than a single standalone instance.
Problem: Custom Zeek script fails to load with a syntax error. Solution: Run zeek -a myscript.zeek or simply attempt to load it standalone to isolate the error; check the Zeek scripting reference for correct event signatures, since event handler arguments must exactly match Zeek’s built-in event definitions.
Problem: Log file timestamps appear in Unix epoch format and are hard to read. Solution: Use zeek-cut -d ts to convert epoch timestamps in the ts field to human-readable date format automatically.
References
- Official Zeek website: https://zeek.org/
- Zeek Documentation: https://docs.zeek.org/en/current/
- Zeek Script Reference: https://docs.zeek.org/en/current/script-reference/index.html
- Zeek GitHub repository: https://github.com/zeek/zeek
- Kali Linux Tools listing for Zeek: https://www.kali.org/tools/zeek/
- Zeek Log Format / zeek-cut documentation: https://docs.zeek.org/en/current/logs/index.html