{"id":183,"date":"2024-03-14T09:00:00","date_gmt":"2024-03-14T09:00:00","guid":{"rendered":"http:\/\/justruss.tech\/index.php\/2024\/03\/14\/getting-started-with-zeek-for-network-threat-hunting\/"},"modified":"2026-05-13T12:59:35","modified_gmt":"2026-05-13T12:59:35","slug":"getting-started-with-zeek-for-network-threat-hunting","status":"publish","type":"post","link":"https:\/\/justruss.tech\/index.php\/2024\/03\/14\/getting-started-with-zeek-for-network-threat-hunting\/","title":{"rendered":"Getting Started with Zeek for Network Threat Hunting"},"content":{"rendered":"<p>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<br \/>\ncaptures because you are querying data, not hunting through hex.<\/p>\n<h3>Installation on Ubuntu 22.04<\/h3>\n<pre>echo \"deb http:\/\/download.opensuse.org\/repositories\/security:\/zeek\/xUbuntu_22.04\/ \/\" \\\n  | sudo tee \/etc\/apt\/sources.list.d\/security:zeek.list\ncurl -fsSL https:\/\/download.opensuse.org\/repositories\/security:zeek\/xUbuntu_22.04\/Release.key \\\n  | gpg --dearmor | sudo tee \/etc\/apt\/trusted.gpg.d\/security_zeek.gpg &gt; \/dev\/null\nsudo apt update &amp;&amp; sudo apt install zeek -y<\/pre>\n<p>Configure the monitored interface in <code>\/etc\/zeek\/node.cfg<\/code>:<\/p>\n<pre>[zeek]\ntype=standalone\nhost=localhost\ninterface=eth0<\/pre>\n<p>Add JSON output to <code>\/etc\/zeek\/local.zeek<\/code>:<\/p>\n<pre>redef LogAscii::use_json = T;\nredef LogAscii::json_timestamps = JSON::TS_ISO8601;<\/pre>\n<h3>Log file locations and what they contain<\/h3>\n<p>Logs write to <code>\/opt\/zeek\/logs\/current\/<\/code> by default. The most useful for hunting:<\/p>\n<ul>\n<li><code>conn.log<\/code> \u2014 every connection: src\/dst IP, port, protocol, duration, bytes in\/out, connection state<\/li>\n<li><code>dns.log<\/code> \u2014 every DNS query: query name, type, response, TTL, answers<\/li>\n<li><code>ssl.log<\/code> \u2014 TLS sessions: JA3\/JA3S hashes, SNI, certificate subject, validation status<\/li>\n<li><code>http.log<\/code> \u2014 HTTP requests: method, URI, user-agent, response code, MIME type<\/li>\n<li><code>files.log<\/code> \u2014 file transfers: hash (MD5\/SHA1), MIME type, source connection<\/li>\n<li><code>weird.log<\/code> \u2014 protocol anomalies Zeek could not parse cleanly<\/li>\n<\/ul>\n<h3>Hunting beaconing with zeek-cut and awk<\/h3>\n<p>Beaconing is regular outbound connections at consistent intervals. Extract connection intervals per destination from conn.log:<\/p>\n<pre>cat conn.log | zeek-cut -d ts id.orig_h id.resp_h id.resp_p proto duration \\\n  | awk -F\"\\t\" \"{print $2, $3, $4, $1}\" \\\n  | sort \\\n  | awk \"\n    BEGIN {prev_key=\\\"\\\"; prev_ts=0}\n    {\n      key = $1 FS $2 FS $3\n      if (key == prev_key &amp;&amp; prev_ts &gt; 0) {\n        diff = $4 - prev_ts\n        print key, diff\n      }\n      prev_key = key\n      prev_ts = $4\n    }\n  \" | sort -k4 -n | head -50<\/pre>\n<p>Destinations with many small, consistent interval values are beacon candidates. Standard deviation under 5 seconds across 20+ samples is a strong indicator.<\/p>\n<h3>DNS tunnelling detection<\/h3>\n<p>DNS tunnelling encodes data in subdomain labels. The query names are long and the label count is high. Extract suspicious queries:<\/p>\n<pre>cat dns.log | zeek-cut query qtype_name answers \\\n  | awk -F\"\\t\" \"length($1) &gt; 50 {print}\" \\\n  | sort | uniq -c | sort -rn | head -20<\/pre>\n<p>Also useful \u2014 query rate per domain, which surfaces tunnelling tools doing fast repeated lookups:<\/p>\n<pre>cat dns.log | zeek-cut query \\\n  | awk -F\".\" \"NF &gt; 3 {print $(NF-1)\".\"$NF}\" \\\n  | sort | uniq -c | sort -rn | head -20<\/pre>\n<h3>JA3 hunting in ssl.log<\/h3>\n<p>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:<\/p>\n<pre>cat ssl.log | zeek-cut ja3 id.orig_h id.resp_h server_name \\\n  | grep -E \"a0e9f5d64349fb13191bc781f81f42e1|51c64c77e60f3980eea90869b68c58a8\"<\/pre>\n<p>The first hash is a common Cobalt Strike default. The second is associated with Metasploit Meterpreter. Neither is guaranteed \u2014 operators change them \u2014 but default deployments often do not bother.<\/p>\n<h3>Integrating with Elastic<\/h3>\n<p>Point Filebeat at the Zeek log directory with the Zeek module enabled:<\/p>\n<pre>filebeat modules enable zeek\n# Edit \/etc\/filebeat\/modules.d\/zeek.yml:\n- module: zeek\n  connection:\n    enabled: true\n    var.paths: [\"\/opt\/zeek\/logs\/current\/conn.log\"]\n  dns:\n    enabled: true\n    var.paths: [\"\/opt\/zeek\/logs\/current\/dns.log\"]\n  ssl:\n    enabled: true\n    var.paths: [\"\/opt\/zeek\/logs\/current\/ssl.log\"]<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Zeek is one of those tools that looks overwhelming at first.<br \/>\nAfter a few weeks of using it in a lab environment, here is how I actually got it to a useful state.<\/p>\n","protected":false},"author":0,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[13],"tags":[],"class_list":["post-183","post","type-post","status-publish","format-standard","hentry","category-threat-hunting"],"_links":{"self":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/183","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"replies":[{"embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/comments?post=183"}],"version-history":[{"count":2,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/183\/revisions"}],"predecessor-version":[{"id":225,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/posts\/183\/revisions\/225"}],"wp:attachment":[{"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/media?parent=183"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/categories?post=183"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/justruss.tech\/index.php\/wp-json\/wp\/v2\/tags?post=183"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}