I have snort3 running on my router configured in IPS mode. Based on a post by @xxxx in this thread, I wanted to see that the setup was actually blocking rule hits.
My test was to create the following rule and then simply run ping www.google.com.
alert icmp any any <> any any (msg:"TEST ALERT"; icode:0; itype:8; sid:10000010; rev:001;)
I expected the ping traffic to timeout but it did not. Snort did record it in the log so I believe that the rule is valid. Why is snort not blocking this traffic?
Note - I tried both 'drop' and 'reject' for the action_override and neither gave the desired effect of stopping the ping traffic.
Yes this is a problem reject works but only with Tcp traffic because the reset that Snort sends causes the connection to collapse because Icmp and Udp do not have such a thing their traffic goes through. You can also test with the reputation module there you can see that Icmp goes through to the blocked Ip but connections with the browser are reset.
The problem is that the FW4 firewall is obviously bypassing Afpacket I could easily tell by using the same blocklist in Snort and Banip both showed the same ip block at the same time but only Banip really blocked it. It seems that snort only gets a copy of the data stream while the actual data stream remains untouched.
I am deep in the weeds here, running off on tangents and trying (and failing) at all sorts of configuration. I'm using John's single rule from the first post as my ruleset, and pinging the router (VM) on its wan interface.
local.lua
config = 'IPS' -- 'IDS' or 'IPS'
pipeline = 'afpacket' -- 'afpacket' or 'nfq'
if config == 'IDS' then
mode = tap
action = 'alert'
else
mode = inline
snort = { ['-Q'] = true }
action = 'block' -- 'block' or 'drop' or 'reject' or ???
end
if pipeline == 'afpacket' then
inputs = { 'eth0', 'br-lan' }
vars = {}
else
inputs = { '4', '5', '6' } -- to match queue numbers in 'inet snort' table
vars = { 'device=eth0', 'queue_maxlen=8192', }
end
--------------------------------------------------------------------------------
ips = {
mode = mode,
variables = default_variables,
action_override = action,
include = RULE_PATH .. '/snort.rules',
}
daq = {
inputs = inputs,
module_dirs = { '/usr/lib/daq', },
modules = {
{
name = pipeline,
mode = mode,
variables = vars,
}
}
}
snort-table.sh
#!/bin/sh
verbose=false
disable_offload()
{
# From https://forum.openwrt.org/t/snort-3-nfq-with-ips-mode/161172
# https://blog.snort.org/2016/08/running-snort-on-commodity-hardware.html
local wan=$(uci get network.wan.device)
if ethtool -k $wan | grep -q -E '(tcp-segmentation-offload|receive-offload): on' ; then
ethtool -K $wan gro off lro off tso off 2> /dev/null
fi
}
disable_offload
nft list tables | grep -q 'snort' && nft flush table inet snort
nft -f - <<TABLE
table inet snort {
chain IPS {
type filter hook forward priority filter; policy accept;
counter queue flags bypass to 4-6
}
}
TABLE
$verbose && nft list table inet snort
exit 0
From WS = 10.1.1.186, I ping -c4 router.
IDS + afpacket - WS gets 4 responses; router shows four alerts in the log file. Completely as expected.
IPS + afpacket - WS gets 4 responses; router shows one alert. Should not have gotten responses. If I change action from block to drop, I see 4 and 4, just like with IDS...
Also of note is that the counter always shows zero, meaning I've got something broken in my nfq implementation somewhere, it's simply never passing packets to snort...
Create the queue as described in the post Snort 3 + NFQ with IPS mode - #18 by efahl the original commands have several problems including that the queue is always deleted when the firewall updates itself and the configuration is more powerful because the traffic is distributed to multiple queues so the limitation of a single queue is bypassed.
Oh that's crap what you are doing better pass the parameters by start line because the parameters are changeable for example it would be better to use 4 queues and more for your bandwidth because you have only one 4 threads unfortunately only 4 are possible that means you would have to change in the script of Efahl the line:
counter queue flags bypass to 4-6
to: counter queue flags bypass to 4-7.
Then you start snort with the parameters:
snort -q -c "/etc/snort/snort.lua" -i "4" -i "5" -i "6" - i "7" --daq-dir /usr/lib/daq --daq nfq -Q -z 4 -s 64000 --daq-var queue_maxlen=8192
As you can see with another queue also the z parameter has to be changed and this is easier solved with the command line.
Yeah, I'm a coder from waaaay back, so I put everything into the config files and minimize the command line.
(Aside: I'm working toward being able to specify all this stuff in UCI /etc/config/snort as settings, then generating the appropriate config when /etc/init.d/snort is launched.)
snaplen can be put into the config as a parameter of the daq section:
-z/--max-packet-threads (and many other CLI options) may be specified in the snort values:
The coding is not so useful in this case because the command line overwrites the values of the snort.lua also you can see so well with which important parameters Snort runs and for a not coder is not so nice because a missing/incorrect character quickly leads to the abort because of syntax error you must always remember that not every user is a programmer so I find the use of lua as a config file also quite off the old snort.conf files were better there. What would make sense would be a script where you enter the desired number of queues and which then automatically adjusts the number of queues in the queue start script and the i and z parameters in the service file.
Oh yes the variables = { 'device=eth1' } variable can be omitted for nfq I have not noticed any difference between being present and not being present.
Yes, exactly, and setting up the nft tables correspondingly. Here's a very rough draft of my current thinking.
# cat /etc/config/snort
config snort 'snort'
option enabled '1'
option config_dir '/etc/snort/'
option mode 'ips' # or 'ids', maybe better names 'detectonly' and 'prevent'?
option mode_action 'block' # 'alert', 'reject', don't know what makes sense yet
option method 'nfq' # or 'afpacket' or ???
option nfq_queue_count '4'
option ... maybe put max queue length and snaplen in here, too.
Once I get it (a lot) more mature, I'll get with @darksky as I believe John is the current maintainer of the OpenWrt snort package, and see if we can make this whole thing a lot easier to deploy. It's pretty wild right now, I've got a lot of questions yet about how various things behave.
My current experiments have gotten to the point where I can block
Test router is 10.1.1.20 on the WAN and 192.168.1.1 on the LAN.
LAN -> router-eth0
Sun May 28 14:56:28 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 192.168.1.121 -> 10.1.1.20
WAN -> router-eth0
Sun May 28 16:06:29 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 10.1.1.200 -> 10.1.1.20
LAN -> WAN
Sun May 28 16:06:52 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 192.168.1.121 -> 10.1.1.200
Sun May 28 16:07:46 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 192.168.1.121 -> 8.8.8.8
LAN -> router-br-lan
Sun May 28 16:08:27 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 192.168.1.121 -> 192.168.1.1
router -> router
Sun May 28 18:14:55 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 10.1.1.20 -> 10.1.1.20
Sun May 28 18:15:36 2023 auth.info snort: [1:10000010:1] "TEST ALERT" {ICMP} 192.168.1.1 -> 192.168.1.1
If I ping from the router to anything else, it gets through, e.g., ping -c4 8.8.8.8 (real WAN) or 10.1.1.200 (testing WAN) or 192.168.1.121 (testing LAN) all respond and no log entries are generated.
I'm using three queues, each in their own chain, along with three threads in snort:
inet snort table with three chains
# nft list table inet snort
table inet snort {
chain input_ips {
type filter hook input priority mangle; policy accept;
counter queue flags bypass to 4
}
chain forward_ips {
type filter hook forward priority mangle; policy accept;
counter queue flags bypass to 5
}
chain prerouting_ips {
type filter hook prerouting priority mangle; policy accept;
counter queue flags bypass to 6
}
}
Has anyone been able to block pings originating from the router itself? (This seems like a major item, as if your router is compromised, lateral movement through the network is really trivial.)
Has anyone found a good reference for rule syntax? My attempts to create an ICMPv6 equivalent test rule have all failed.
As long as afpaket doesn't work properly, it falls out as an ips, there is only nfq and there no reject works, stay only alert drop and block, but I thought I had read somewhere that block kills the connection right away, drop would be the better choice. Pcap is a good IDS because it can also be bound to virtual network devices. The names are good, everyone understands that.
The problem that you can ping from the router could be due to the queue, the nftables makes differences between local and external packets according to my knowledge, because as it is in my Nftables table, the queue is in it with hook forward, but the local rules are under hook input/output. You'll probably need to create an extra queue for local traffic first and bind it to Snort.
//edit
nft 'add chain inet snort local { type filter hook output priority filter ; }'
nft insert rule inet snort local counter queue num 7 bypass
with this rules snort can block the output traffic from the router self.
It is ignoring the logging to /mnt/mmcblk0p3/alert_fast.txt which is defined in my ok.lua
If I do not hide kernel threads, I see CPU saturation on several cores during a speed test which limts bandwidth limiting from over 1000 Mbps without running snort to around 100-200 Mbps.
Yes that was to be expected that the bandwidth goes down Snort is very demanding has always been so. Sure the logging does not work for me it is but note that Snort creates multiple log files one per queue which then 0_alert_fast.txt 1_alert_fast.txt etc are called. The problem that Snort does not block local pings I have already solved in this thread this is due to the nature of the queue Nfttables makes a distinction between local traffic to and from the device and traffic passing through the device from other devices you need to create an extra queue with the hook input or output.