Zeek (formerly Bro) is a passive network analysis framework. Unlike Snort or Suricata which match signatures against packets, Zeek parses protocols and writes structured logs. The output is far more useful for threat hunting than raw packet
captures because you are querying data, not hunting through hex.
Installation on Ubuntu 22.04
echo "deb http://download.opensuse.org/repositories/security:/zeek/xUbuntu_22.04/ /" \ | sudo tee /etc/apt/sources.list.d/security:zeek.list curl -fsSL https://download.opensuse.org/repositories/security:zeek/xUbuntu_22.04/Release.key \ | gpg --dearmor | sudo tee /etc/apt/trusted.gpg.d/security_zeek.gpg > /dev/null sudo apt update && sudo apt install zeek -y
Configure the monitored interface in /etc/zeek/node.cfg:
[zeek] type=standalone host=localhost interface=eth0
Add JSON output to /etc/zeek/local.zeek:
redef LogAscii::use_json = T; redef LogAscii::json_timestamps = JSON::TS_ISO8601;
Log file locations and what they contain
Logs write to /opt/zeek/logs/current/ by default. The most useful for hunting:
conn.log— every connection: src/dst IP, port, protocol, duration, bytes in/out, connection statedns.log— every DNS query: query name, type, response, TTL, answersssl.log— TLS sessions: JA3/JA3S hashes, SNI, certificate subject, validation statushttp.log— HTTP requests: method, URI, user-agent, response code, MIME typefiles.log— file transfers: hash (MD5/SHA1), MIME type, source connectionweird.log— protocol anomalies Zeek could not parse cleanly
Hunting beaconing with zeek-cut and awk
Beaconing is regular outbound connections at consistent intervals. Extract connection intervals per destination from conn.log:
cat conn.log | zeek-cut -d ts id.orig_h id.resp_h id.resp_p proto duration \
| awk -F"\t" "{print $2, $3, $4, $1}" \
| sort \
| awk "
BEGIN {prev_key=\"\"; prev_ts=0}
{
key = $1 FS $2 FS $3
if (key == prev_key && prev_ts > 0) {
diff = $4 - prev_ts
print key, diff
}
prev_key = key
prev_ts = $4
}
" | sort -k4 -n | head -50
Destinations with many small, consistent interval values are beacon candidates. Standard deviation under 5 seconds across 20+ samples is a strong indicator.
DNS tunnelling detection
DNS tunnelling encodes data in subdomain labels. The query names are long and the label count is high. Extract suspicious queries:
cat dns.log | zeek-cut query qtype_name answers \
| awk -F"\t" "length($1) > 50 {print}" \
| sort | uniq -c | sort -rn | head -20
Also useful — query rate per domain, which surfaces tunnelling tools doing fast repeated lookups:
cat dns.log | zeek-cut query \
| awk -F"." "NF > 3 {print $(NF-1)"."$NF}" \
| sort | uniq -c | sort -rn | head -20
JA3 hunting in ssl.log
JA3 is an MD5 hash of specific TLS ClientHello fields (version, ciphers, extensions, elliptic curves, elliptic curve formats). Known malware families have published JA3 hashes. A quick check against common ones:
cat ssl.log | zeek-cut ja3 id.orig_h id.resp_h server_name \ | grep -E "a0e9f5d64349fb13191bc781f81f42e1|51c64c77e60f3980eea90869b68c58a8"
The first hash is a common Cobalt Strike default. The second is associated with Metasploit Meterpreter. Neither is guaranteed — operators change them — but default deployments often do not bother.
Integrating with Elastic
Point Filebeat at the Zeek log directory with the Zeek module enabled:
filebeat modules enable zeek
# Edit /etc/filebeat/modules.d/zeek.yml:
- module: zeek
connection:
enabled: true
var.paths: ["/opt/zeek/logs/current/conn.log"]
dns:
enabled: true
var.paths: ["/opt/zeek/logs/current/dns.log"]
ssl:
enabled: true
var.paths: ["/opt/zeek/logs/current/ssl.log"]