Zeek Packages
The Zeek Package Manager (zkg) gives access to hundreds of community-contributed and official Zeek packages that extend Zeek's default capabilities. Essential packages for security monitoring include JA3/JA3S fingerprinting, CISA CSET detections, hash-based file blocking, Sigma rule integration, and protocol-specific analyzers. Installing and managing packages with zkg takes minutes and significantly extends what Zeek logs and detects by default.
A newly deployed Zeek instance generates ssl.log without JA3 hashes. The analyst needs JA3 hashes to correlate with threat intel databases. Installing the JA3 package takes 2 minutes: zkg install ja3. After restarting Zeek, every ssl.log entry has ja3 and ja3s fields populated. The same workflow applies to every capability gap — identify the need, find the package, install, test.
Essential Security Packages
| Package | What It Adds | Install Command |
|---|---|---|
| ja3 | JA3 + JA3S hashes to ssl.log | zkg install ja3 |
| hassh | SSH client/server fingerprints (like JA3 for SSH) | zkg install hassh |
| corelight/codebreaker-detections | C2/malware framework detections | zkg install corelight/codebreaker-detections |
| intel-framework | IOC matching (block by IP/domain/hash/UA) | Built-in, configure intel feeds |
| community-id | Community ID to conn.log | zkg install community-id |
| zeek-quic | QUIC/HTTP3 dissection + logging | zkg install zeek-quic |
| zeek-detective | Lateral movement detection scripts | zkg install zeek-detective |
| zeek-eca | Encrypted channel analysis (ETA features) | zkg install zeek-eca |
| log-add-vlan-everywhere | Add VLAN ID to every log type | zkg install log-add-vlan-everywhere |
Package Management
#!/bin/bash
echo "=== List installed packages ==="
zkg list installed
echo ""
echo "=== Search for packages ==="
zkg search ja3
zkg search lateral
zkg search c2
echo ""
echo "=== Install essential packages ==="
zkg install ja3
zkg install hassh
zkg install community-id
zkg install zeek-quic
echo ""
echo "=== Verify package loaded (check local.zeek) ==="
cat /opt/zeek/share/zeek/site/local.zeek | grep -E "@load|zkg"
echo ""
echo "=== Configure Intel framework for IOC matching ==="
mkdir -p /opt/zeek/intel
# Create intel feed file (tab-separated format)
cat > /opt/zeek/intel/malicious_ips.dat << 'INTEL'
#fields indicator indicator.type meta.source meta.desc
1.2.3.4 Intel::ADDR manual C2 server from incident
evil.example.com Intel::DOMAIN manual Known phishing domain
INTEL
# Add to local.zeek
cat >> /opt/zeek/share/zeek/site/local.zeek << 'ZEEK'
@load frameworks/intel/seen
@load frameworks/intel/do_notice
redef Intel::read_files += { "/opt/zeek/intel/malicious_ips.dat" };
ZEEK
echo ""
echo "=== JA3 package verification ==="
echo "After restart, ssl.log should have ja3 field:"
echo "Check: head -20 /opt/zeek/logs/current/ssl.log | grep -c ja3"
echo ""
echo "=== HASSH - SSH fingerprinting ==="
# HASSH in ssh.log
echo "After hassh install and restart:"
echo "cat /opt/zeek/logs/current/ssh.log | head -5"
# Fields: hassh (client fingerprint), hasshServer (server fingerprint)
Intel Framework — IOC Matching
##! Configure Zeek Intel framework for automated IOC matching.
##! Add to /opt/zeek/share/zeek/site/local.zeek
@load frameworks/intel/seen
@load frameworks/intel/do_notice
# Define custom notice type for IOC hits
redef enum Notice::Type += {
Intel_IOC_Hit,
};
# Load intel feeds
redef Intel::read_files += {
"/opt/zeek/intel/malicious_ips.dat",
"/opt/zeek/intel/malicious_domains.dat",
"/opt/zeek/intel/malicious_urls.dat",
"/opt/zeek/intel/malicious_hashes.dat",
};
# Custom handler for IOC hits — write to custom log in addition to intel.log
event Intel::match(s: Intel::Seen, items: set[Intel::Item])
{
for ( item in items )
{
local msg = fmt("IOC match: %s [%s] source=%s desc=%s",
s$indicator, s$indicator_type,
item$meta$source,
item$meta$desc);
NOTICE([$note=Intel_IOC_Hit,
$msg=msg,
$identifier=cat(s$indicator)]);
}
}
Installing a Zeek package doesn't just add new detection scripts — it often modifies the schema of existing log files to add new fields. The ja3 package adds ja3 and ja3s fields to every ssl.log entry. The community-id package adds community_id to conn.log. The hassh package adds hassh and hasshServer to ssh.log. This means: (1) If you installed Zeek and started logging before installing packages, your historical logs won't have those fields, (2) After installing a package, you must restart Zeek (or run zeekctl deploy) for the changes to take effect, (3) Your SIEM parsing configuration must be updated to handle the new fields in log files, (4) If you're using index-time schemas (like Elasticsearch mappings), add the new fields to the mapping before the data starts flowing. The package-then-schema update order matters: install the package first, update the mapping before restarting Zeek, then restart to avoid schema mapping conflicts.
Q & A
Q: The ja3 package shows different JA3 values than Wireshark's JA3 dissector for the same packet. Why?
JA3 hash differences between tools are almost always due to GREASE filtering or TLS 1.3 version handling inconsistencies. Debugging steps: (1) Check which JA3 specification version each tool implements — the original Salesforce JA3 (2017) vs updated versions handle GREASE values differently. (2) In Wireshark's dissector: right-click on the TLS ClientHello, select "Packet Details," expand TLS → Handshake Protocol → Client Hello, and look for the Fingerprint field. Compare the intermediate "JA3 string" (not just the MD5 hash) between the two tools — the strings should match if the implementation is the same. (3) In Zeek: check the package version with zkg list installed | grep ja3 and compare against the Wireshark ja3 plugin version. (4) Manual verification: use the reference Python implementation from the Salesforce GitHub repo to compute the hash from the raw ClientHello bytes. This gives you the ground truth to compare both tools against. Different JA3 implementations are a known problem in the community — for production use, pick one tool and use it consistently rather than comparing across tools.