A revelation during my studies with SANS revealed a lot of open source tools that I find amazing. One of those is the tool Zeek (formerly Bro) IDS. While I have enjoyed and been enriched by my studies of SiLK, Snort, Suricata, Tshark and TCPDump, Zeek is the tool that jumps out to me as that offering greatest potential to learn about and explore networks.

In this blog entry, we’re going to create a single-node Zeek sensor on our virtual host and turn it loose monitoring the network tap we have between our Core switch and the ESXi host. I am starting with Ubuntu 20.04 again, a minimal install, so we can get up and running and have some consistency with the ELK host we are also running. I know lots of distributions run these applications on CentOS/RedHat as well, and there are plenty of good blogs on installing it for yum/RPM based distributions, but we’ll stick with my feeble limitations for now😉

We’ll want to provision this VM instance with at least 2 cores and 8 GB of RAM, but I doubled both due to the well stocked server having some room. We will also want at least 2 interfaces. The first should be mapped to the management VLAN, and the second should be mapped via a dedicated vSwitch, which will be configured to accept copies of all of the traffic from the tap.

For the tap interface, I ensure that I have it marked as a promiscuous port on the VM side as well:

sudo ifconfig ens192 promisc

So let’s get the machine happy and willing to accept the rest of the install – I get SSH running so I can access it, and then install some dependencies.

sudo apt-get update && sudo apt-get upgrade -y
sudo apt-get install openssh-server

sudo apt-get install cmake make gcc g++ python3-pip flex \ bison  libpcap-dev libssl-dev python-dev swig zlib1g-dev \ libmaxminddb-dev git net-tools

For GeoIP, we will need files from MaxMind to enable the mapping. If we’re are using this in conjunction with ELK, as I am in this blog series, we’ve already registered an account and downloaded the files. So, in this case I am going to just use the same *.mmdb files I un-tarred before and place them on the Zeek host as well.

First, I create a good directory for them to reside on the Zeek VM:

sudo mkdir --parents /usr/share/GeoIP

We can move mmdb files over from another host via SFTP, SCP or Dropbox. On a Windows host (my Jumpbox), I used SCP (no need for a client/server config, etc.):

scp *.mmdb zeekuser@10.5.8.28:~/

And now move them over into our new home for the GeoIP DBs: 

<code>sudo cp *.mmdb /usr/share/GeoIP

Now we can go ahead and tackle the bulk of installing Zeek. It is available in installation packages, Dpkgs, etc, but the latest and best approach is to get it straight from the GitHub repo. Anything we want to do to customize this install, like add GeoIP, will need us to tackle it with a build from source, so that is why we avoid the quicker routes:

<code>git clone --recursive https://github.com/zeek/zeek

This will create a full copy of that repo in the directory we executed the clone from. In my case, my home directory.  Let’s jump into the top level of that repo and set the configure script in motion so we can bake in the GeoIP features:

cd <mark>zeek</mark>
./configure --with-geoip=/usr/share/GeoIP

Now we can make and install from the source you just configured. Compilers are black magic voodoo! This will be some slooooow slogging, so don’t expect to complete this while you watch.

sudo make
sudo make install

Zeek is installed, but our system has no idea – because we compiled from source we didn’t get all of the automatic Path changes or links in systemctl.  So let’s go ahead and  remedy that.  First, lets make a symlink that helps us make zeek’s binary executable, and then we can make a permanent change to the system path and save it in our .bashrc profile:

sudo ln --symbolic /usr/local/zeek/bin/zeek /usr/bin/<mark>zeek</mark>
export PATH=$PATH:/usr/local/zeek/bin
echo "export PATH=$PATH:/usr/local/zeek/bin" >> .bashrc

Likewise, we’ll need to have zeekctl executable from any path: 

<code>sudo ln --symbolic /usr/local/zeek/bin/zeekctl /usr/bin/zeekctl

These above actions should, finally, have Zeek installed on our Ubuntu 20.04 image in all of its glory. But wait, there is more!!! Now we need to actually CONFIGURE Zeek so that it can do the job we’ve hired it to do.  As with any IDS, understanding which networks we care about:

<code>sudo nano /usr/local/zeek/etc/networks.cfg

I actually manage to use (abuse?) all three public ranges, so this is what my networks.cfg looks like:


<em># List of local networks in CIDR notation, optionally followed by a</em>

<em># descriptive tag.</em>

<em># For example, "10.0.0.0/8" or "fe80::/64" are valid prefixes.</em>

10.0.0.0/8          Production networks
172.16.0.0/12       Infrastructure networks
192.168.0.0/24      Lab Use networks

Way back in the beginning we provisioned the VM with a 2nd interface, and in our case we noted that it was ens192. So now we’ll need to modify the node.cfg file to ensure that we are monitoring it. Please note, we could use this same file to help us deploy a distributed, scalable system, but in my case I am going to just stick with a standalone. So let’s open up node.cfg!

<code>sudo nano /usr/local/zeek/etc/node.cfg

My pertinent configuration lines look like this:

<em>#[<mark>zeek</mark>]</em>
type=standalone
host=10.5.8.28
interface=ens192

Lastly, we modify the zeekctl.cfg file in the same directory. First, I set up my email address for collecting Zeek messages.

Hot tip – make use of the Gmail email tagging by appending “+ to your username to make filtering on the Gmail side super easy!

Next, we slow log rotation to every day because I dislike needing to reassemble a day on the fly and don’t see so much traffic. Then I ensure I store them for 30 days.


## Global ZeekControl configuration file.

###############################################

# Mail Options

# Recipient address for all emails sent out by <mark>Zeek</mark> and ZeekControl.

MailTo = *******+security@gmail.com

###############################################

# Logging Options

# Rotation interval in seconds for log files on manager (or standalone) node.

# A value of 0 disables log rotation.

LogRotationInterval = 86400

# Expiration interval for archived log files in LogDir.  Files older than this

# will be deleted by "zeekctl cron".  The interval is an integer followed by

# one of these time units:  day, hr, min.  A value of 0 means that logs

# never expire.

LogExpireInterval = 30 day

# Enable ZeekControl to write statistics to the stats.log file.  A value of 1

# means write to stats.log, and a value of 0 means do not write to stats.log.

StatsLogEnable = 1

# Number of days that entries in the stats.log file are kept.  Entries older

# than this many days will be removed by "zeekctl cron".  A value of 0 means

# that entries never expire.

StatsLogExpireInterval = 30

And that covers my changes here! But what else can the zeekctl.cfg file tell us? The latter portion of that file shows where a lot of important follow-up items are.  The SitePolicyScripts (/usr/local/zeek/share/zeek/site/local.zeek by default) tells us where we need to make changes to how certain engines within Zeek role, what signatures we enable, etc.  LogDir points to where we can expect to scrape our logs using FileBeats and send them along to ELK. CfgDir is, interestingly enough, where we found our node, network, and zeekctl configuration files.

Here is a handy summary of those paths, just as a quick reference:

Useful LocationPath
Logs/usr/local/zeek/logs/
Policy Script/usr/local/zeek/share/zeek/site/local.zeek
Active logs (current interval)/usr/local/zeek/logs/current/

So what else does Zeek need us to change? Zeek, by default, logs in ASCII format.  This is useful for CLI-based tools like zeek-cut, but makes parsing it for SIEM consumption or scraping via APIs difficult.  So let’s open up the local.zeek policy and make a couple of changes.

<code>sudo nano /usr/local/zeek/share/zeek/site/local.<mark>zeek</mark>

In there, we can un-comment any engines we would like engaged. While I am there, let’s change a salt (no peeking!), enable HTTP Webapp Detection, Heartbleed detection, and VLAN & MAC logging, and add in the line for JSON logging, policy/tuning/json-logs.zeek. Pertinent portions are shown below:

##! Local site policy. Customize as appropriate.
##!
##! This file will not be overwritten when upgrading or reinstalling!

# Installation-wide salt value that is used in some digest hashes, e.g., for

# the creation of file IDs. Please change this to a hard to guess value.

redef digest_salt = "*****************************";

# This script logs which scripts were loaded during each run.

@load misc/loaded-scripts

# Apply the default tuning scripts for common tuning settings.

@load tuning/defaults
@load policy/tuning/json-logs.<mark>zeek</mark>

~~~SNIP~~~

# Load all of the scripts that detect software in various protocols.

@load protocols/ftp/software
@load protocols/smtp/software
@load protocols/ssh/software
@load protocols/http/software

# The detect-webapps script could possibly cause performance trouble when

# running on live traffic.  Enable it cautiously.

@load protocols/http/detect-webapps

~~~SNIP~~~

# Uncomment the following line to enable detection of the heartbleed attack. Enabling

# this might impact performance a bit.

@load policy/protocols/ssl/heartbleed

# Uncomment the following line to enable logging of connection VLANs. Enabling

# this adds two VLAN fields to the conn.log file.

@load policy/protocols/conn/vlan-logging

# Uncomment the following line to enable logging of link-layer addresses. Enabling

# this adds the link-layer address for each connection endpoint to the conn.log file.

@load policy/protocols/conn/mac-logging

# Uncomment this to source zkg's package state

# @load packages

Well that wasn’t so bad, was it?  All we need to do now is use zeekctl to deploy our zeek instance together with the policies we have defined and start collecting!

Zeekctl has a lot of cool commands we can use, but the most common will be deploy, start, status, and stop.  We’ll talk more about these in a future post:

zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/bin$ sudo zeekctl ?

ZeekControl Version 2.3.0

  capstats [<nodes>] [<secs>]      - Report interface statistics with capstats
  check [<nodes>]                  - Check configuration before installing it
  cleanup [--all] [<nodes>]        - Delete working dirs (flush state) on nodes
  config                           - Print zeekctl configuration
  cron [--no-watch]                - Perform jobs intended to run from cron
  cron enable|disable|?            - Enable/disable "cron" jobs
  deploy                           - Check, install, and restart
  df [<nodes>]                     - Print nodes' current disk usage
  diag [<nodes>]                   - Output diagnostics for nodes
  exec <shell cmd>                 - Execute shell command on all hosts
  exit                             - Exit shell
  install                          - Update zeekctl installation/configuration
  netstats [<nodes>]               - Print nodes' current packet counters
  nodes                            - Print node configuration
  peerstatus [<nodes>]             - Print status of nodes' remote connections
  print <id> [<nodes>]             - Print values of script variable at nodes
  process <trace> [<op>] [-- <sc>] - Run <mark>Zeek</mark> with options and scripts on trace
  quit                             - Exit shell
  restart [--clean] [<nodes>]      - Stop and then restart processing
  scripts [-c] [<nodes>]           - List the <mark>Zeek</mark> scripts the nodes will load
  start [<nodes>]                  - Start processing
  status [<nodes>]                 - Summarize node status
  stop [<nodes>]                   - Stop processing
  top [<nodes>]                    - Show <mark>Zeek</mark> processes ala top

Commands provided by plugins:

  ps.<mark>zeek</mark> [<nodes>]                - Show <mark>Zeek</mark> processes on nodes' systems

zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/bin$

With that, we’ll deploy, which includes start, and then check the status to ensure it is up and running:

zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/bin$ sudo zeekctl deploy
checking configurations ...
installing ...

removing old policies in /usr/local/zeek/spool/installed-scripts-do-not-touch/site ...

removing old policies in /usr/local/zeek/spool/installed-scripts-do-not-touch/auto ...

creating policy directories ...
installing site policies ...
generating standalone-layout.<mark>zeek</mark> ...
generating local-networks.<mark>zeek</mark> ...
generating zeekctl-config.<mark>zeek</mark> ...
generating zeekctl-config.sh ...
stopping ...
stopping <mark>zeek</mark> ...
starting ...
starting <mark>zeek</mark> ...
zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/bin$ sudo zeekctl status
Name         Type       Host          Status    Pid    Started
<mark>zeek</mark>         standalone 10.5.8.28     running   23015  12 Jan 13:48:06
zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/bin$

Just so we can be certain that logs are indeed in place, let us go and see the log directory.  Look at all of that json goodness!

zeekuser@mac-<mark>zeek</mark>:~$ cd /usr/local/zeek/logs/current
zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/logs/current$ ls

capture_loss.log  dns.log  files.log  known_certs.log  known_services.log  notice.log  packet_filter.log  software.log  stats.log   stdout.log  weird.log

conn.log          dpd.log  http.log   known_hosts.log  loaded_scripts.log  ntp.log     sip.log            ssl.log       stderr.log  syslog.log  x509.log

zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/logs/current$ tail -3 conn.log

{"ts":1610477423.86057,"uid":"CKgq3e1BlmApXq14D8","id.orig_h":"10.5.8.48","id.orig_p":1900,"id.resp_h":"239.255.255.250","id.resp_p":1900,"proto":"udp","duration":0.2699289321899414,"orig_bytes":8916,"resp_bytes":0,"conn_state":"S0","local_orig":true,"local_resp":false,"missed_bytes":0,"history":"D","orig_pkts":30,"orig_ip_bytes":9756,"resp_pkts":0,"resp_ip_bytes":0,"orig_l2_addr":"00:18:dd:05:49:54","resp_l2_addr":"01:00:5e:7f:ff:fa"}

{"ts":1610477424.283288,"uid":"Cd72Q31eG3V6IWz5q8","id.orig_h":"10.5.8.6","id.orig_p":58639,"id.resp_h":"208.67.222.222","id.resp_p":443,"proto":"udp","conn_state":"SHR","local_orig":true,"local_resp":false,"missed_bytes":0,"history":"^d","orig_pkts":0,"orig_ip_bytes":0,"resp_pkts":1,"resp_ip_bytes":332,"orig_l2_addr":"00:50:56:96:6e:76","resp_l2_addr":"5c:5a:c7:cf:6c:4e"}

{"ts":1610477424.340513,"uid":"CoPMfg4DVe3Kbnycc3","id.orig_h":"10.5.8.6","id.orig_p":58318,"id.resp_h":"208.67.220.220","id.resp_p":443,"proto":"udp","conn_state":"SHR","local_orig":true,"local_resp":false,"missed_bytes":0,"history":"^d","orig_pkts":0,"orig_ip_bytes":0,"resp_pkts":1,"resp_ip_bytes":716,"orig_l2_addr":"00:50:56:96:6e:76","resp_l2_addr":"5c:5a:c7:cf:6c:4e"}

zeekuser@mac-<mark>zeek</mark>:/usr/local/zeek/logs/current$

Outstanding!