Posts Tagged: rules

Snort IPS With NFQ (nfqueue) Routing on Ubuntu

Overview

This guide will show you how to configure Snort to run inline using the NFQUEUE DAQ (referred to as NFQ). This allows your Snort server to use iptables to route traffic between any number of subnets, with Snort evaluating all traffic passing through the system. This guide will assume some knowledge of routing and IP addressing, especially as it is implemented under Linux, as well as some experience setting up Snort. Configuring Snort as an inline NIPS with NFQ is more complicated than setting snort up as a NIDS, and is more complicated than setting up Snort as a NIPS using the AFPACKET DAQ. The complexity is due to the use of iptables and the need to understand IP routing. I will assume that you have installed Snort before, so I may gloss over some concepts and explanations in this guide. If you find yourself having difficult with installing and configuring Snort, I recommend you first work through my complete guide on installing Snort which should give you a good foundation on understanding how to install and configure Snort.

This guide has been written and tested on Ubuntu 16 x64. It should work on most currently supported versions of Ubuntu and Debian derivatives, but your mileage may vary.

NFQUEUE versus AFPACKET

On Ubuntu, you can run Snort two different ways in inline mode, with AFPACKET or with NFQ. AFPACKET is simpler to setup (see my guide here), but only lets you bridge sets of paired interfaces. This means you can bridge eth0 with eth1 (pass traffic between them), and also bridge eth2 with eth3, but you can not pass traffic between eth0 and eth4. A benefit of AFPACKET is that you can install an inline Snort machine without any changes to IP addressing, routing, or networking changes. NFQ on the other hand lets you leverage the power of iptables to make routing decisions. It’s more complicated to setup (it requires you as a systems administrator to understand Linux routing), but is more powerful for network security. You can also use iptables to blottck traffic before Snort even sees it (to filter out noisy traffic sources), or use additional tools that interface with iptables to increase security (like psad or fwsnort).

IPv6 Notes

Enabling NFQ with IPv6 is very similar to IPv4, the only thing to note is that you will have to run a separate instance of Snort to process IPv6 packets, and setup another iptables rule to forward IPv6 packets to a second NFQUEUE using the ip6tables command. Essentially you follow this guide twice, once for IPv4, and once for IPv6, making changes as necessary.

Steps

This guide will go through the following steps:

  1. Network Configuration
  2. Route Configuration
  3. Enable Kernel IP forwarding on the System
  4. Software Installation
  5. Software configuration
  6. Enabling NFQUEUE
  7. Enabling additional firewall rules

One thing to note: for simplicity, we will enable routing for all traffic passing through the system before we lock down the firewall rules. Because of this, I highly recommend that you test this on a development network to understand how it works before you implement on a production network. In a real environment, you would want your firewall rules in place before you enabled routing of traffic on a device that bridged networks with different security levels. For this guide, it’s much simpler to apply firewall rules at the end for the purpose of learning. I have noted a number of excellent online resources and books at the end of this guide to help understand networking, iptables, and network security.

Network Configuration

For this guide, we will use the following network, with our Snort router having three interfaces on three different networks:

The 172.16.0.0/24 network is our internal admin or management network, and will not be routed by Snort. No traffic should pass between the 172.16.0.0 network and the other networks. This is how we will SSH into this snort system for management purposes, and if configured: Snort could send log data back to a logging server on this network. It is good practice to keep a separate network (through VLANS or separate hardware) for management purposes. We won’t lock down this network at first, but will apply firewall rules at the end of this article to prevent traffic from entering this network across our Snort router.

The 10.0.0.0/24 network is our outbound network. All traffic not destined for a locally connected network will be directed out this network to the next hop gateway (in this case the ISP router). Note that the computer on this network has the Snort router as it’s default gateway. While this works, normally you’d configure the computer to have the actual external gateway (10.0.0.1 in this case) as the gateway. It’s set this way to simplify testing.

The 192.168.0.0/24 network is our client network (screened subnet). Hosts will be configured on this network to use the Snort server as it’s gateway. Traffic passing between this network and the 10.0.0.0 network will be scanned by Snort, and will be dropped and/or logged if suspicious.

Because this server is running Ubuntu 16, the interface names no longer follow the ethX standard (eth0, eth1, …). Instead, interfaces names are assigned as Predictable Network Interface Names. This means you need to check the names of your interfaces using ifconfig -a. In my case, what was originally eth0 is now ens160.

Since you are using your Snort system as a router, you’ll want static interfaces on each address. You don’t want dynamic (DCHP) addresses for the interfaces on this system because any clients on that subnet will use the IP address of the Snort system as their gateway. For any interface that Snort will process traffic on, you need to disable LRO and GRO (there’s an explanation of this in my complete guide on installing Snort). For any interface that sends traffic to an external network, you’ll need a gateway configured.

Edit /etc/network/interfaces as an admin:

sudo vi /etc/network/interfaces

Configure your network interfaces:

# Routed subnet
auto ens160
iface ens160 inet static
address 10.0.0.2
netmask 255.255.255.0
network 10.0.0.0
broadcast 10.0.0.255
gateway 10.0.0.1
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off

# Host Subnet (internal network / screened subnet)
auto ens224
iface ens224 inet static
address 192.168.0.2
netmask 255.255.255.0
network 192.168.0.0
broadcast 192.168.0.255
# no gateway required
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off

# Management Subnet (internal network, not routed)
auto ens192
iface ens192 inet static
address 172.16.0.2
netmask 255.255.255.0
network 172.16.0.0
broadcast 172.16.0.255
# no gateway
# snort not processing traffic on this interface, so no need to disable LRO or GRO

Setting up Routes

Because we don’t have additional subnets connected to the 192.1668.0.0 or 172.16.0.0 networks, we don’t need to specify any routes. We only configured one gateway on this system (10.0.0.1), so all traffic not destined to a local subnet will be sent to that gateway. To verify, use the ip route command:

noah@snort-router:~ $ ip route
default via 10.0.0.1 dev ens160 onlink 
10.0.0.0/24 dev ens160  proto kernel  scope link  src 10.0.0.2 
172.16.0.0/24 dev ens192  proto kernel  scope link  src 172.16.0.2 
192.168.0.0/24 dev ens224  proto kernel  scope link  src 192.168.0.2 

If you have multiple subnets configured off of one of those other two subnets, you would need to add a gateway for that subnet in your interfaces file, and you would need to add a static route.

Sidebar: A more complicated example (don’t follow these steps unless you have more complicated routing needs): if you had the 192.168.99.0/24 subnet connected to your 192.168.0.0/24 subnet, reachable through 192.168.0.3, as shown highlighted in red in the image below:

you would need to add two commands to the network interface for ens224: One to specify a gateway, and one to add this route. Since you now have multiple gateways on your Snort server, you want to tell the system which gateway is the default gateway (the gateway for all traffic that it does not have an explicit route for). Interface ens224 on the snort server would now look like:

# Host Subnet (internal network / screened subnet)
# Additional routing example
auto ens224
iface ens224 inet static
address 192.168.0.2
netmask 255.255.255.0
network 192.168.0.0
broadcast 192.168.0.255
gateway 192.168.0.3
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off
up ip route add 192.168.99.0/24 via 192.168.0.3 || true

While ens160 with the default route will look like:

# Routed subnet
# Additional routing example
auto ens160
iface ens160 inet static
address 10.0.0.2
netmask 255.255.255.0
network 10.0.0.0
broadcast 10.0.0.255
gateway 10.0.0.1
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off
up ip route add default via ens160 || yes

At this time, you should verify that you can ping hosts in every network from your Snort server, as well as external hosts via your gateway. Use the ip route commmand as well as tracepath to verify this is setup right. Do not continue until all this is working.

Firewalls

At this point, even though the system can reach all connected networks, the system will not pass traffic between the networks. Traffic from a host on the management network can not reach any hosts on the screened network, and vice versa. This is by design. Enabling this is easy, however you may want to restrict traffic between certain networks before enabling this option, for security purposes. For simplicity, we will not enable firewall rules at this time, so if your system is a live system connected to both internal sensitive networks and public networks, you may want to read through the entire article first to understand how to add firewall rules to protect the system before enabling routing.

Enable Kernel IP forwarding

Use vi to edit /etc/sysctl.conf

Un-comment the line:

net.ipv4.ip_forward=1			

Reboot the system. At this time, you should now be able to ping between two devices connected to different networks. If your routing is setup correctly, you should be able to ping out the default route as well. If pinging between locally connected subnets work, but you can’t ping to external subnets, you probably need to look at the routing further down the line, do those devices have a path back to your locally connected subnets? If you have a large network, you may want to look at enabling a routing protocol on your Snort server to exchange routing information (probably OSPF or RIP, depending on you network configuration and level of comfort).

Installing DAQ and Snort With NFQ support

If you have a Snort system up and running, you will probably need to re-compile DAQ and Snort for this guide. The reason is that to enable NFQ, you need to install libraries prior to compiling DAQ.

Start by installing all pre-requisites for Snort:

sudo apt-get install -y build-essential libpcap-dev libpcre3-dev libdumbnet-dev bison flex zlib1g-dev liblzma-dev openssl libssl-dev

We need the development libraries for Nghttp2. On Ubuntu 16 this is simple:

# Ubuntu 16 only (not Ubuntu 14)
sudo apt-get install -y libnghttp2-dev

On Ubuntu 14, we do this from scratch:

# Ubuntu 14 only (not Ubuntu 16)
sudo apt-get install -y autoconf libtool pkg-config
cd ~/snort_src
wget https://github.com/nghttp2/nghttp2/releases/download/v1.17.0/nghttp2-1.17.0.tar.gz
tar -xzvf nghttp2-1.17.0.tar.gz
cd nghttp2-1.17.0
autoreconf -i --force
automake
autoconf
./configure --enable-lib-only
make
sudo make install

Finally we need the NFQ specific libraries:

sudo apt-get install libnetfilter-queue-dev

We will make a directory for all the source tarball files:

mkdir ~/snort_src
cd ~/snort_src

Download and install Data Acquisition library (DAQ) from the Snort website:

cd ~/snort_src
wget https://www.snort.org/downloads/snort/daq-2.0.6.tar.gz
tar -xvzf daq-2.0.6.tar.gz
cd daq-2.0.6
./configure
make
sudo make install

after the ./configure option, you will see the DAQ modules that are enabled. You must have NFQ enabled here, as seen below:

Build AFPacket DAQ module.. : yes
Build Dump DAQ module...... : yes
Build IPFW DAQ module...... : yes
Build IPQ DAQ module....... : no
Build NFQ DAQ module....... : yes  <<<<< MUST BE YES
Build PCAP DAQ module...... : yes
Build netmap DAQ module.... : no

Now we are ready to install Snort from source:

cd ~/snort_src
wget https://snort.org/downloads/snort/snort-2.9.9.0.tar.gz
tar -xvzf snort-2.9.9.0.tar.gz
cd snort-2.9.9.0
./configure --enable-sourcefire
make
sudo make install

Run the following command to update shared libraries:

sudo ldconfig

Since the Snort installation places the Snort binary at /usr/local/bin/snort, it is common to create a symlink to /usr/sbin/snort:

sudo ln -s /usr/local/bin/snort /usr/sbin/snort

The last step of our Snort installation is to test that the Snort binary runs. Execute Snort with the -V flag, which causes Snort to print the current version. You should see output similar to the following:

noah@snort-router:~ $ snort -V

   ,,_     -*> Snort! <*-
  o"  )~   Version 2.9.9.0 GRE (Build 56) 
   ''''    By Martin Roesch & The Snort Team: http://www.snort.org/contact#team
           Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using libpcap version 1.7.4
           Using PCRE version: 8.38 2015-11-23
           Using ZLIB version: 1.2.8

noah@snort-router:~ $

Now we want to verify that Snort can see the NFQ DAQ. We do this by running snort with the –daq-list flag:

noah@snort-router:~ $ snort --daq-list
Available DAQ modules:
pcap(v3): readback live multi unpriv
nfq(v7): live inline multi	     <<<<<<  Need this line
ipfw(v3): live inline multi unpriv
dump(v3): readback live inline multi unpriv
afpacket(v5): live inline multi unpriv

At this point, Snort has been compiled and installed with the NFQ DAQ. Next is configuration.

Configuring Snort

Most of this configuration comes from my longer articles on installing Snort. I’ll skip over some of the details of certain steps, but more in-depth information is available in my full Snort install guide. First off, for security reasons we want Snort to run as an unprivileged user. We create a snort user and group for this purpose:

sudo groupadd snort
sudo useradd snort -r -s /sbin/nologin -c SNORT_IDS -g snort

Next, we need to create a number of files and folders that Snort expects when running in NIDS mode.  We will then change the ownership of those files to our new snort user:

# Create the Snort directories:
sudo mkdir /etc/snort
sudo mkdir /etc/snort/rules
sudo mkdir /etc/snort/rules/iplists
sudo mkdir /etc/snort/preproc_rules
sudo mkdir /usr/local/lib/snort_dynamicrules
sudo mkdir /etc/snort/so_rules

# Create some files that stores rules and ip lists
sudo touch /etc/snort/rules/iplists/black_list.rules
sudo touch /etc/snort/rules/iplists/white_list.rules
sudo touch /etc/snort/rules/local.rules
sudo touch /etc/snort/sid-msg.map

# Create our logging directories:
sudo mkdir /var/log/snort
sudo mkdir /var/log/snort/archived_logs

# Adjust permissions:
sudo chmod -R 5775 /etc/snort
sudo chmod -R 5775 /var/log/snort
sudo chmod -R 5775 /var/log/snort/archived_logs
sudo chmod -R 5775 /etc/snort/so_rules
sudo chmod -R 5775 /usr/local/lib/snort_dynamicrules

# Change Ownership on folders:
sudo chown -R snort:snort /etc/snort
sudo chown -R snort:snort /var/log/snort
sudo chown -R snort:snort /usr/local/lib/snort_dynamicrules

We now need to move the following files from the extracted Snort tarball to the snort configuration folder:

cd ~/snort_src/snort-2.9.9.0/etc/
sudo cp *.conf* /etc/snort
sudo cp *.map /etc/snort
sudo cp *.dtd /etc/snort

cd ~/snort_src/snort-2.9.9.0/src/dynamic-preprocessors/build/usr/local/lib/snort_dynamicpreprocessor/
sudo cp * /usr/local/lib/snort_dynamicpreprocessor/

Disable all rule files:

sudo sed -i 's/include \$RULE\_PATH/#include \$RULE\_PATH/' /etc/snort/snort.conf

Now We need to edit our snort configuration file:

sudo vi /etc/snort/snort.conf

Set your internal networks here. I am choosing to have Snort consider the 192.168.0.0 and 172.16.0.0 networks as internal networks. This maters for rule processing, where many rules look for attacks against computers in your HOME_NET subnet ranges:

# line 45 (no spaces between IP addresses)
ipvar HOME_NET [192.168.0.0/24,176.16.0.0/24]

Next we need to tell Snort about the locations of all the folders we created earlier. I have included the line numbers after the hash so you can more easily find the setting (do not write the line number, just change the path to match what is below):

var RULE_PATH /etc/snort/rules						# line 104
var SO_RULE_PATH /etc/snort/so_rules				# line 105
var PREPROC_RULE_PATH /etc/snort/preproc_rules		# line 106

var WHITE_LIST_PATH /etc/snort/rules/iplists		# line 113
var BLACK_LIST_PATH /etc/snort/rules/iplists		# line 114

Enable the Local rules file. Un-comment the following line (line 545) by deleting the hash from the beginning of the line:

include $RULE_PATH/local.rules

Now we need to configure Snort to use the NFQ DAQ an run inline mode. You can choose to pass these options from the command line, but it’s neater to do it here in the configuration file. Add the following three lines to enable the NFQ DAQ in inline mode, looking at NFQ queue number 4 for packets (we will configure iptables later to pass all routed packets to this same numbered queue):

# at line 168, add the following new lines:
config daq: nfq
config daq_mode: inline
config daq_var: queue=4

Save the file, and now we need to test the configuration. To test: we run the following command. The T flag indicates we want to test, the c flag specifies the snort.conf file, and the Q flag tells snort that we’re working in inline mode (required later for Drop rules to actually drop, instead of just alerting):

$ sudo snort -T -c /etc/snort/snort.conf -Q

You should have similar output to the following (truncated for clarity):

Enabling inline operation
Running in Test mode

...

nfq DAQ configured to inline.
Decoding Raw IP4

        --== Initialization Complete ==--

   ,,_     -*> Snort! <*-
  o"  )~   Version 2.9.9.0 GRE (Build 56) 
   ''''    By Martin Roesch & The Snort Team: http://www.snort.org/contact#team
           Copyright (C) 2014-2016 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using libpcap version 1.7.4
           Using PCRE version: 8.38 2015-11-23
           Using ZLIB version: 1.2.8

           Rules Engine: SF_SNORT_DETECTION_ENGINE  Version 3.0  <Build 1>
           Preprocessor Object: SF_SIP  Version 1.1  <Build 1>
           Preprocessor Object: SF_SSLPP  Version 1.1  <Build 4>
           Preprocessor Object: SF_SDF  Version 1.1  <Build 1>
           Preprocessor Object: SF_SMTP  Version 1.1  <Build 9>
           Preprocessor Object: SF_GTP  Version 1.1  <Build 1>
           Preprocessor Object: SF_DNP3  Version 1.1  <Build 1>
           Preprocessor Object: SF_DCERPC2  Version 1.0  <Build 3>
           Preprocessor Object: SF_DNS  Version 1.1  <Build 4>
           Preprocessor Object: SF_POP  Version 1.0  <Build 1>
           Preprocessor Object: SF_IMAP  Version 1.0  <Build 1>
           Preprocessor Object: SF_MODBUS  Version 1.1  <Build 1>
           Preprocessor Object: SF_FTPTELNET  Version 1.2  <Build 13>
           Preprocessor Object: SF_SSH  Version 1.1  <Build 3>
           Preprocessor Object: SF_REPUTATION  Version 1.1  <Build 1>

Snort successfully validated the configuration!
Snort exiting

If you are seeing similar output, then you’re in a good place. If not, troubleshoot the errors (usually Snort will output line number where the error was found) before continuing.

Testing a Rule

We will add a simple rule to detect ICMP traffic to verify that we’re detecting and passing traffic correctly. Add the following to our local.rules file:
/etc/snort/rules/local.rules:

alert icmp any any -> $HOME_NET any (msg:"ICMP test detected"; GID:1; sid:10000001; rev:001; classtype:icmp-event;)

Since we have made changes to the file that snort loads (local.rules), it is a good idea to test the configuration file again:

sudo snort -T -c /etc/snort/snort.conf -Q

If successful, you should be able to scroll up through the output and see that Snort has loaded our one rule:

		+++++++++++++++++++++++++++++++++++++++++++++++++++
		Initializing rule chains...
		1 Snort rules read
			1 detection rules
			0 decoder rules
			0 preprocessor rules
		1 Option Chains linked into 1 Chain Headers
		0 Dynamic rules
		+++++++++++++++++++++++++++++++++++++++++++++++++++

		+-------------------[Rule Port Counts]---------------------------------------
		|             tcp     udp    icmp      ip
		|     src       0       0       0       0
		|     dst       0       0       0       0
		|     any       0       0       1       0
		|      nc       0       0       1       0
		|     s+d       0       0       0       0
		+----------------------------------------------------------------------------

Configuring NFQUEUE

I will assume you don’t have any entries in your iptables at this time. You can verify with the following command:

$ sudo iptables -L

which should show the following output:

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

We want to add a rule to our iptables that makes every packet that is going to be forwarded (not destined for local delivery) to be scanned by Snort. To do this, we add a single rule that moves all forwarded traffic (from the FORWARD queue) to NFQUEUE queue number 4, which is the same queue we specified in our snort.conf configuration above, where Snort is listening for it (I chose queue 4 arbitrarily, you could use any number, as long as it matched the snort.conf queue). To add this rule to the FORWARD chain, we run the following:

sudo iptables -I FORWARD -j NFQUEUE --queue-num=4

Since Snort is not running right now, the system will deliver traffic to that queue, but it will not be processed and will drop (we can change this later to a fail-open option if wanted). Let’s run Snort with the following flags to see traffic being processed:

-A console                    the console option prints fast mode alerts to stdout
-q                            Quiet. Don't show banner and status report.
-u snort                      run snort as the following user after startup
-g snort                      run snort as the following group after startup
-c /etc/snort/snort.conf      the path to our snort.conf file
-Q                            Run inline mode (required to drop packets)

Run Snort with the command below:

sudo /usr/local/bin/snort -A console -q -u snort -g snort -c /etc/snort/snort.conf -Q

If you ping from one machine to another machine, you should see alerts show on your snort machine. You can’t ping to or from the snort machine directly, because this rule only looks at traffic not destined for the local machine.

06/07-14:38:16.275283  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.88 -> 192.168.0.10
06/07-14:38:17.274174  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.88 -> 192.168.0.10
06/07-14:38:18.273215  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.88 -> 192.168.0.10

You have to use ctrl-c to stop snort from running after the above output.

To test that snort will Drop rather than just alert on your rules, edit your local.rules and replace the first word “alert” with “drop”. Re-run Snort as above and try pinging again. You should see alerts, and your host’s should not be able to ping. Note that all other traffic is permitted, our rule is only blocking ICMP traffic. You should see alerts specifying the Drop action:

06/07-14:42:23.935433  [Drop] [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.88 -> 192.168.0.10
06/07-14:42:24.942280  [Drop] [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.88 -> 192.168.0.10

If you were to reboot your computer, you would loose all your iptables configurations. To make them persistent, we need to export the current configuration to a text file, and tell the system to load that text file each time the network starts up. To Export the current settings, we use the iptables-save tool as follows:

sudo sh -c "iptables-save > /etc/network/iptables.rules"

To load the settings, we use the iptables-restore tool in our network interfaces file (/etc/network/interfaces) for our primary network interface, so now the configuration for ens160 looks like this:

auto ens160
iface ens160 inet static
address 10.0.0.2
netmask 255.255.255.0
network 10.0.0.0
broadcast 10.0.0.255
gateway 10.0.0.1
pre-up iptables-restore < /etc/network/iptables.rules
post-up ethtool -K ens160 gro off
post-up ethtool -K ens160 lro off

Reboot and test. You’ll need to manually re-start Snort for any traffic to be passed at this time. Remember that iptables is placing all routed traffic in NFQUEUE number 4, but if there is no application listening to that queue, traffic will not pass.

Fail open

The following is optional based on your security needs. If you want traffic to pass between networks when Snort is not running (fail-open mode), if Snort crashes or stops running for some reason, you’ll want to enable the –queue-bypass flag for the iptables rule. We will need to modify the current rule. First let’s check the state of all chains:

$ sudo iptables -L --line-numbers
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         
1    NFQUEUE    all  --  anywhere             anywhere             NFQUEUE num 4

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination  

we want to modify rule number 1 in the FORWARD chain. To do this, we run the following command, which Replaces (the -R flag) this rule, adding the queue-bypass option:

sudo iptables -R FORWARD 1 -j NFQUEUE --queue-num=4 --queue-bypass

you’ll want to re-save the running configuration as above (overwrite the text file), reboot, and test.

Additional Firewall Rules

Rather than try to fit a small guide for iptables on production systems into this guide, I will refer you to a couple of other excellent short guides on how to use iptables to secure your system. You should at a minimum block all access to the system (the INPUT queue) that isn’t management related or from the management network. Configuring Drop by default rather than accept is recommended.
Basic iptables lock-downs for a system.
More in-depth iptables lock-downs for a server.

At a minimum, to block traffic from going to the management network, you would want to insert the following rule:

sudo iptables -I FORWARD -o ens192 -j DROP

this blocks all routed traffic out the management interface. You have to insert this above the NFQUEUE rule, because once Snort permits the traffic, it does not go through other chains.

Final Steps

You will probably also want to setup PulledPork to download rules automatically, install barnyard2 for log processing, and BASE to view alerts in a GUI. All these steps can be found in my guide on snort, here: Installing Snort

Where to go From here

If you found yourself struggling to get routing working even before you got to the Snort portion of this guide, I highly recommend that you look into taking a networking course. I personally took the Cisco CCNA course offered by the local community college (I think it was only $45 USD a unit). I learned an amazing amount about how networks work, and it made me a much better systems administrator. Don’t think that because you’re a computer administrator that Cisco training isn’t for you, it will compliment your knowledge and abilities.

Books:
The CCNA Books offered by Cisco are excellent:
The CCNA official Cert Guide is what I used to pass my CCNA years ago, and are very well written textbooks. While it’s Cisco specific, most of the basic knowledge in these books are general enough to be useful to anyone working with networking.
The CCNA Routing and Switching Portable Command Guide is one of the few hard-copy books I keep on my shelf and refer to whenever i’m working on Cisco eqipment.

I found the O’reily Network Warior book to be quite satisfying, it gave a broad overview of a number of technologies related to network technology to really broaden my knowledge.

If you’re really serious about TCP/IP, the gold standard (and really heavy) book to have is the The TCP/IP Guide: A Comprehensive, Illustrated Internet Protocols Reference.

Links:
How to Enable IP Forwarding in Linux
Understanding the ip route command and IP routing in Linux.

Iptables Tutorial 1.2.2. If you want to know how it works at the architecture level (not the user level, and not the programming level), this is the guide you want to read. I found Chapters three through nine to be most helpful, but if you need some more info on TCP/IP, the first few chapters are quite good as well.

The second flowchart at this page is awesome for understanding how packets flow through iptables. Use it in conjunction with the iptables tutorial above.

the NFQUEUE options for iptables.

This article:
A Deep Dive into Iptables and Netfilter Architecture, may get a little deep into the internal workings of the kernel IP stack, but I found it fascinating. Compliments this SANS article as well.

A few relevant books that get good reviews, but which i haven’t read yet:
Linux iptables Pocket Reference
Linux Firewalls: Attack Detection and Response with iptables, psad, and fwsnort
How Linux Works: What Every Superuser Should Know

Feedback

I hope this article has been helpful to you. Please feel free to provide feedback, both issues you experienced and recommendations that you have. The goal of this guide was not just for you to create a Snort NIPS with NFQ, but to understand how all the parts work together, and get a deeper understanding of all the components, so that you can troubleshoot and modify your Snort NIDS with confidence.

Snort 2.9.9.x on Ubuntu – Part 3: Writing and Testing a Single Rule With Snort

  1. Installing Snort
  2. Configuring Snort to Run as a NIDS
  3. Writing and Testing a Single Rule With Snort
  4. Installing Barnyard2
  5. Installing PulledPork
  6. Creating Upstart Scripts for Snort on Ubuntu 14
  7. Creating systemD Scripts for Snort on Ubuntu 16
  8. Installing BASE
  9. Conclusion

Writing and Testing a Single Rule With Snort

In the previous two articles in this series, we installed Snort an configured it to run as a NIDS. In this article, we are going to create a rule which causes Snort to generate an alert whenever it sees an ICMP message. If you want, you can skip this section, as it is not required to get a Snort NIDS up and running, but it will help you to gain a much better understanding of how Snort rules are created and loaded.

Onward

In the previous article, we created the /etc/snort/rules/local.rules file and left it empty. We also edited the snort.conf file to tell Snort to load this local.rules file (when we un-commented the line: include $RULE_PATH/local.rules in snort.conf). When Snort starts, it will use the include directive in snort.conf to load all rules in local.rules. The local.rules file is a place where we can place rules that are specific to our environment, and is great for testing.

First, we need to edit the local.rules file:

sudo vi /etc/snort/rules/local.rules

input the following text and save the file:

alert icmp any any -> $HOME_NET any (msg:"ICMP test detected"; GID:1; sid:10000001; rev:001; classtype:icmp-event;)

What this rule says is that for any ICMP packets it sees from any network to our HOME_NET, generate an alert with the text ICMP test. The other information here (GID, REV, classtype) are used group the rule, and will be helpful when you install BASE.

Barnyard2 doesn’t read meta-information about alerts from the local.rules file. Without this information, Barnyard2 won’t know any details about the rule that triggered the alert, and will generate non-fatal errors when adding new rules with PulledPork (done in a later step). To make sure that barnyard2 knows that the rule we created with unique identifier 10000001 has the message ”ICMP Test Detected”, as well as some other information (please see this blog post for more information). We add the following two lines to the /etc/snort/sid-msg.map file:

#v2
1 || 10000001 || 001 || icmp-event || 0 || ICMP Test detected || url,tools.ietf.org/html/rfc792

(the #v2 tells barnyard2 that the next line is the version 2 format, rather than v1)

Since we have made changes to the file that snort loads (local.rules), it is a good idea to test the configuration file again:

sudo snort -T -c /etc/snort/snort.conf -i eth0

If successful, you should be able to scroll up through the output and see that Snort has loaded our rule:

		+++++++++++++++++++++++++++++++++++++++++++++++++++
		Initializing rule chains...
		1 Snort rules read
			1 detection rules
			0 decoder rules
			0 preprocessor rules
		1 Option Chains linked into 1 Chain Headers
		0 Dynamic rules
		+++++++++++++++++++++++++++++++++++++++++++++++++++

		+-------------------[Rule Port Counts]---------------------------------------
		|             tcp     udp    icmp      ip
		|     src       0       0       0       0
		|     dst       0       0       0       0
		|     any       0       0       1       0
		|      nc       0       0       1       0
		|     s+d       0       0       0       0
		+----------------------------------------------------------------------------

Now to test the rule.  We need to verify that Snort generates an alert when it processes an ICMP packet. We will launch Snort with the following options:

-A console                    the console option prints fast mode alerts to stdout
-q                            Quiet. Don't show banner and status report.
-u snort                      run snort as the following user after startup
-g snort                      run snort as the following group after startup
-c /etc/snort/snort.conf      the path to our snort.conf file
-i eth0                       the interface to listen on

Run Snort with the command below, modifying the parameters as required specific for your configuration:

sudo /usr/local/bin/snort -A console -q -u snort -g snort -c /etc/snort/snort.conf -i eth0

Note: If you are running Ubuntu 16, remember that your interface name is not eth0.

Once you have started Snort with the above command, you need use another computer or another terminal window to ping the interface that you directed Snort to listen on.  You should see output similar to the below on the terminal of the Snort machine:

10/31-02:27:19.663643  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:19.663675  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
10/31-02:27:20.658378  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:20.658404  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
10/31-02:27:21.766521  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:21.766551  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
10/31-02:27:22.766167  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:22.766197  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
^C*** Caught Int-Signal

You have to use ctrl-c to stop snort from running after the above output. What the above example shows is the 4 ICMP Echo Request and Reply messages between our Snort server (IP 10.0.0.64) and our other machine (10.0.0.74). If you look in /var/log/snort, you will also see a file with the name snort.log.nnnnnnnnnn (the n’s are replaced by numbers), which contains the same information that Snort printed to the screen.

Congratulations, if you have output similar to the above then you have successfully created a rule for Snort to alert on. Continue to the next section to Install Barnyard2.

Snort IPS Inline Mode on Ubuntu

Overview

This guide will cover configuring Snort 2.9.8.x as an NIPS (Network Intrusion Prevention System), also known as “inline” mode on Ubuntu. In inline mode Snort creates a bridge between two network segments, and is responsible for passing traffic bewteen the segments. It can inspect the traffic it passes, as well as drop suspicious traffic. This guide will assume that you have a basic familiarity with Snort, and you should have a Snort system setup and installed (Barnyard2, PulledPork, and a web GUI like BASE or Snorby are optional but helpful). You can follow any of my guides for installing Snort: my in-depth series of articles, my quick install guide, or my guide for installing OpenAppID. I recommend you follow the in-depth series of articles, paired with the OpenAppID article to give you the most fully-featured Snort system.

This guide is tested on Ubuntu 14.04 LTS x64, but should work on any current version of Ubuntu (12, 14, 15) as well as with similar distribution (Mint, Debian, Raspberry Pi, etc.).

Network Configuration

Snort in inline mode creates a transparent bridge between two network segments. What this means is that Snort has two network interfaces: each on a different network segment. You will configure these interfaces without an IP address and in promiscuous mode. When you run Snort it will listen for traffic on each interface. When a packet arrives on an interface, Snort will inspect the packet based on your rules, then either drop the packet, or send it out the other interface without any modification. Because of this the two network segments that snort bridges must be part of the same logical subnet (and therefore broadcast domain).

You can bridge multiple networks, but remember that you create a bridge between two networks, not between multiple networks (there is a 1 to 1 mapping between bridged networks). If you bridge eth1 to eth2, and bridge eth3 to eth4, traffic will not pass between the two bridges (traffic will not pass between eth1 and eth3 or eth4, for example). Traffic originating on eth1 will only go to eth2, and vice versa. The same holds true for traffic betwee eth3 and eth4.

You will also want a third network interface on the Snort system configured with an IP address for administrative access. If you are running Ubuntu 15.10, your interface names may be much different because interfaces names are assigned as Predictable Network Interface Names.

Note that because the Snort application is responsible for bridging (passing traffic between) the two network segments. If Snort is not running, computers on one network segment will not be able to communicate across the Snort system to computers on the other segment.

We need to configure each bridged interface without an IP address, in promiscuous mode, and disable LRO an GRO. Run sudo vi /etc/network/interfaces, and for each bridged interface, configure similar to the example below. Here I will use eth0 as my administrative interface (with an IP address assigned by DHCP), and I will configure eth1 and eth2 so that they can be bridged by Snort (no IP address, in promiscuous mode, and disabling LRO and GRO):

# Administrative interface
auto eth0
iface eth0 inet dhcp

# First bridged interface
auto eth1
iface eth1 inet manual
	up ifconfig $IFACE 0.0.0.0 up
	up ip link set $IFACE promisc on
	post-up ethtool -K $IFACE gro off
	post-up ethtool -K $IFACE lro off
	down ip link set $IFACE promisc off
	down ifconfig $IFACE down

# Second Bridged Interface
auto eth2
iface eth2 inet manual
	up ifconfig $IFACE 0.0.0.0 up
	up ip link set $IFACE promisc on
	post-up ethtool -K $IFACE gro off
	post-up ethtool -K $IFACE lro off
	down ip link set $IFACE promisc off
	down ifconfig $IFACE down

If you are bridging multiple networks, eth1 to eth2, and separately eth3 to eth4 for example, make sure every interface that will be bridged is configured as above. Use ifconfig -a to show all interfaces (including interfaces that aren’t configured) to determine your interface names.

Configuration

To run Snort in inline mode, you need to make a few modifications to your snort.conf, and add a few command line options when you run Snort (either from the command line, or from your startup script).

First, make sure you have the afpacket DAQ available. Run snort ‑‑daq-list and check the output for the DAQ libraries that are installed:

snort@snortIPS:~$ snort --daq-list
	Available DAQ modules:
	pcap(v3): readback live multi unpriv
	ipfw(v3): live inline multi unpriv
	dump(v3): readback live inline multi unpriv
	afpacket(v5): live inline multi unpriv

here you can see that afpacket is available, and can do inline in unprivileged mode. Next we add two lines to our snort.conf to enable afpacket in inline mode. Add these two lines to your snort.conf (around line number 168 is probably the correct place for it):

config daq: afpacket
config daq_mode: inline

Since we have modified our snort.conf, we should test that it works. Run the following command to have Snort verify the configuration. The parameters we pass to Snort will differ slightly from before, because we are enabling inline mode:

sudo snort -T -c /etc/snort/snort.conf -Q -i eth1:eth2

you’ll notice the -Q, and the -i eth1:eth2 flags are new. The -Q flag tells Snort to run in inline mode, while -i eth1:eth2 tells snort to bridge those two interfaces (to be inline between those two interfaces). If you are bridging multiple interfaces, you would use the following format:

sudo snort -T -c /etc/snort/snort.conf -Q -i eth1:eth2::eth3:eth4

where we have a double-colon separating the bridged interface sets.

Running Snort Inline

Now that we have verified that the Snort configuration is correct, let’s run Snort from the command line, and output alerts directly to the screen.
Add the following alert to your local.rules file, and make sure that Snort loads it by testing your configuration and scrolling up to see that the rule is loaded (if you need help with this, please see this article). This rule will generate an alert whenever it sees an ICMP message (a ping), which makes testing easy.

alert icmp any any -> $HOME_NET any (msg:"ICMP test detected"; GID:1; sid:10000001; rev:001; classtype:icmp-event;)

test snort again as above, and scroll up through the output to make sure the one ICMP rule loads. If it doesn’t show as loaded, make sure you have included your local.rules file in your snort.conf.

Next, we will run Snort in inline mode. We add a couple of flags here:
-A Console will cause snort to generate fast alerts out to the console (the screen for us to see)
-N will cause snort to not write any output files to your output directory.
Run the following:

sudo /usr/local/bin/snort -A console -Q -c /etc/snort/snort.conf -i eth1:eth2 -N

and once Snort is loaded (you’ll see the message: Commencing packet processing), generate a ping across the bridged interface. A good test is to have one computer (the client) on one side of the bridge with a static IP, and the gateway on the other side of the bridge. Ping the gateway from the client (they will both need IP addresses on the same subnet), and the traffic should cross the bridged interface. When Snort passes the traffic between the networks, you should see an alert generated on the screen (because of our ICMP rule created above). Snort should outpout something similar to the following:

02/15-19:30:05.602117  [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.1 -> 10.0.0.116

hit ctrl-c to stop Snort from running. The ping should have succeeded between the client and the gateway, because the ICMP rule was written as an alert rather than a drop rule (the first part of the rule). If you check the ARP table on your client, with arp -a, you will notice that the MAC address of the gateway is correct. This shows you that Snort is not modifying the packet in any way as it moves it from one network segment to the other. This is how DHCP requests and other traffic can correctly move between segments without any issues.

Next we will change the ICMP rule to drop traffic instead of generating an alert.

Dropping Traffic

To have Snort drop traffic, you just need to modify the rule created above in your local rules from alert to drop. It should now look like this:

drop icmp any any -> $HOME_NET any (msg:"ICMP test detected"; GID:1; sid:10000001; rev:001; classtype:icmp-event;)

restart Snort as above, and test your ping again between your client and the gateway. The ping should fail, and Snort should output something similar to the following:

02/15-19:33:38.952784  [Drop] [**] [1:10000001:1] ICMP test detected [**] [Classification: Generic ICMP event] [Priority: 3] {ICMP} 10.0.0.116 -> 10.0.0.1

Conclusion

This covers the very basics of setting up Snort in Inline mode. Future articles will discuss configuring PulledPork to work with inline mode (modifying rules automatically), as well as using OpenAppID to block types of traffic (youtube, facebook, etc).

If you have any feedback (recommendations or corrections), please let me know here.

Recommended Reading and Links

Useful Links:
Snort IPS using DAQ AFPacket
Network IDS & IPS Deployment Strategies
Snort Packet Acquisition manual
Snort DAQ readme
SANS Analysis of DAQ modules
Snort Talos DAQ Essentials

Books:
Snort IDS and IPS Toolkit (Jay Beale’s Open Source Security) (Kindle Version) – This is a good book for understanding how Snort works under the hood. It is a little old, but is still relevent and very detailed.
Snort Cookbook – This book is very helpful in showing how Snort can be run to meet specific needs (using recipes that describe specific situations).
Applied Network Security Monitoring: Collection, Detection, and Analysis – I haven’t read this book, but it is well reviewed, and covers NIDS at a much higher level than the other two books.

Snort 2.9.8.x on Ubuntu – Part 3: Writing and Testing a Single Rule With Snort

UPDATE: Snort 2.9.9.x has been released. Please see the updated series of articles here or my quick install guide here.

I am leaving this older guide online for anyone who wants to install this older version of Snort on Ubuntu, but you really should be using the updated guide for the 2.9.9.x version of Snort, since support for older versions of Snort are set to expire, and the updated guide is kept more up to date and includes BASE instead of Snorby for a Web GUI.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

  1. Installing Snort
  2. Configure Snort to Run as a NIDS
  3. Writing and Testing a Single Rule With Snort
  4. Installing Barnyard2
  5. Installing PulledPork
  6. Creating Upstart Scripts for Snort
  7. Creating systemD Scripts for Snort
  8. Installing Snorby on Ubuntu 12
  9. Installing Snorby on Ubuntu 14
  10. Installing Snorby on Ubuntu 15
  11. Conclusion

Writing and Testing a Single Rule With Snort

In the previous two articles in this series, we installed Snort an configured it to run as a NIDS. In this article, we are going to create a rule which causes Snort to generate an alert whenever it sees an ICMP message. If you want, you can skip this section, as it is not required to get a Snort NIDS up and running, but it will help you to gain a much better understanding of how Snort rules are created and loaded.

Onward

In the previous article, we created the /etc/snort/rules/local.rules file and left it empty. We also edited the snort.conf file to tell Snort to load this local.rules file (when we un-commented the line: include $RULE_PATH/local.rules in snort.conf). When Snort starts, it will use the include directive in snort.conf to load all rules in local.rules. The local.rules file is a place where we can place rules that are specific to our environment, and is great for testing.

First, we need to edit the local.rules file:

sudo vi /etc/snort/rules/local.rules

input the following text and save the file:

alert icmp any any -> $HOME_NET any (msg:"ICMP test detected"; GID:1; sid:10000001; rev:001; classtype:icmp-event;)

What this rule says is that for any ICMP packets it sees from any network to our HOME_NET, generate an alert with the text ICMP test. The other information here (GID, REV, classtype) are used group the rule, and will be helpful when you install Snorby.

Since we have made changes to the files that snort loads, it is a good idea to test the configuration file again:

sudo snort -T -c /etc/snort/snort.conf -i eth0

If successful, you should be able to scroll up through the output and see that Snort has loaded our rule:

		+++++++++++++++++++++++++++++++++++++++++++++++++++
		Initializing rule chains...
		1 Snort rules read
			1 detection rules
			0 decoder rules
			0 preprocessor rules
		1 Option Chains linked into 1 Chain Headers
		0 Dynamic rules
		+++++++++++++++++++++++++++++++++++++++++++++++++++

		+-------------------[Rule Port Counts]---------------------------------------
		|             tcp     udp    icmp      ip
		|     src       0       0       0       0
		|     dst       0       0       0       0
		|     any       0       0       1       0
		|      nc       0       0       1       0
		|     s+d       0       0       0       0
		+----------------------------------------------------------------------------

Now to test the rule.  We need to verify that Snort generates an alert when it processes an ICMP packet. We will launch Snort with the following options:

-A console                    the console option prints fast mode alerts to stdout
-q                            Quiet. Don't show banner and status report.
-u snort                      run snort as the following user after startup
-g snort                      run snort as the following group after startup
-c /etc/snort/snort.conf      the path to our snort.conf file
-i eth0                       the interface to listen on

Run Snort with the command below, modifying the parameters as required specific for your configuration:

sudo /usr/local/bin/snort -A console -q -u snort -g snort -c /etc/snort/snort.conf -i eth0

Note: If you are running Ubuntu 15.10, remember that your interface name is not eth0.

Once you have started Snort with the above command, you need use another computer or another terminal window to ping the interface that you directed Snort to listen on.  You should see output similar to the below on the terminal of the Snort machine:

10/31-02:27:19.663643  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:19.663675  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
10/31-02:27:20.658378  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:20.658404  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
10/31-02:27:21.766521  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:21.766551  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
10/31-02:27:22.766167  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.74 -> 10.0.0.64
10/31-02:27:22.766197  [**] [1:10000001:1] ICMP test detected [∗∗] [Classification: Generic ICMP event] [Priority:3] {ICMP} 10.0.0.64 -> 10.0.0.74
^C*** Caught Int-Signal

You have to use ctrl-c to stop snort from running after the above output. What the above example shows is the 4 ICMP Echo Request and Reply messages between our Snort server (IP 10.0.0.64) and our other machine (10.0.0.74). If you look in /var/log/snort, you will also see a file with the name snort.log.nnnnnnnnnn (the n’s are replaced by numbers), which contains the same information that Snort printed to the screen.

Congratulations, if you have output similar to the above then you have successfully created a rule for Snort to alert on. Continue to the next section to Install Barnyard2.