Working with suricata

This is a follow up to my last post in which I described how to setup suricata as a IPS which bridges traffic between two interfaces using af-packet (and all that in a virtual machine). Here I’m showing how to work with suricata in general – or rather – how I work with suricata.

Setup / Configuration additions

tcmalloc

There seem to be some performance and memory usage improvements when you use libtcmalloc. You can install it by issuing:

apt-get install libtcmalloc-minimal4

Then edit the systemd script for example using systemctl edit suricata and write the following Service-Definition into it so that suricata uses libtcmalloc_minimal.so.4:

~# cat /etc/systemd/system/suricata.service.d/override.conf
[Service]
Environment="LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libtcmalloc_minimal.so.4"

suricata-update

To be honest I was a little bit confused about the way the files and folders were available after installation in Debian 12 (Bookworm). According to the documentation there should have been an update.yaml. Then if you issue suricata-update it will place it’s rules to /var/lib/suricata/rules – But in debian they’re loaded from /etc/suricata/rules. Now some guides show to use suricata-update -o /etc/suricata/rules… So you download all the rules once and never update them because the update location is in /var/lib/suricata/rules if you don’t change this…

Shall we fix this?

First let’s get the update.yaml:

cp /usr/lib/python3/dist-packages/suricata/update/configs/update.yaml update.yaml

As explained before, suricata-update, unless you tell it differently loads its rules from /var/lib/suricata/rules. However. In Debian it loads them from /etc/suricata/rules. Hence modify suricata.yaml:

default-rule-path: /var/lib/suricata/rules

Now edit the following part in your update.yaml:

# A list of local rule sources. Each entry can be a rule file, a
# directory or a wild card specification.
local:
  # A directory of rules.
  - /etc/suricata/rules
  # A single rule file.
  #- /etc/suricata/rules/app-layer-events.rules
  # A wildcard.
  # - /etc/suricata/rules/*.rules

Also add the rules you like using suricata-update enable-source (do a update-sources and list-source first) and comment the sources in update.yaml:

# Remote rule sources. Simply a list of URLs.
sources:
  # Emerging Threats Open with the Suricata version dynamically replaced.
  # - https://rules.emergingthreats.net/open/suricata-%(__version__)s/emerging.rules.tar.gz
  # The SSL blacklist, which is just a standalone rule file.
  # - https://sslbl.abuse.ch/blacklist/sslblacklist.rules

You may also want to remove all rules from /etc/suricata/rules to not get duplicate-rules messages when updating the rules.

Suricata not blocking anything

Indeed, by default Suricata does not block anything. This is not a bug. Check the rules:

# grep "^reject" *

# grep "^drop" *
stream-events.rules:drop tcp any any -> any any (msg:"SURICATA STREAM 3way handshake toclient data injection suspected"; flow:to_client; stream-event:3whs_ack_data_inject; classtype:protocol-command-decode; sid:2210057; rev:1;)

suricata.rules:drop tcp any any -> any any (msg:"SURICATA STREAM 3way handshake toclient data injection suspected"; flow:to_client; stream-event:3whs_ack_data_inject; classtype:protocol-command-decode; sid:2210057; rev:1;)

There is only one rule which would in fact block (Drop). All the other rules will just alert. Pretty much like an IDS should work. Let’s take a look at what I get:

# awk '{$1=""; $2=""; $3=""}1' fast.log | sed 's_\[\*\*\].*__g' | sed 's_ group [0-9]*__g' | sort | uniq -c | sort -h

[..]

    100    GPL RPC portmap listing UDP 111 
    103    SURICATA STREAM 3way handshake excessive different SYN/ACKs 
    176    ET SCAN Suspicious inbound to PostgreSQL port 5432 
    216    ET SCAN Suspicious inbound to mySQL port 3306 
    223    SURICATA UDPv4 invalid checksum 
    236    SURICATA STREAM Last ACK with wrong seq 
    241    GPL ICMP_INFO PING speedera 
    325    ET SCAN Suspicious inbound to MSSQL port 1433 
    333    SURICATA SMTP no server welcome message 
    431    ET SCAN LibSSH Based Frequent SSH Connections Likely BruteForce Attack 
    488    SURICATA STREAM excessive retransmissions 
    520    ET COMPROMISED Known Compromised or Hostile Host Traffic 
   1094    ET INFO SSH-2.0-Go version string Observed in Network Traffic - Inbound 
   1115    SURICATA Applayer Detect protocol only one direction 
   1413    ET SCAN Potential SSH Scan 
   1470    ET 3CORESec Poor Reputation IP 
   4933    SURICATA TCPv4 invalid checksum 
   5576    ET CINS Active Threat Intelligence Poor Reputation IP 
  10309    ET DROP Dshield Block Listed Source 
  12872    GPL ICMP_INFO PING *NIX 

I am pretty sure you’ll find a more nice command (Scroll down – I wrote another better one) to put together this list. I used awk to remove the first three columns of the log, sed to remove everything after [**] and sed to remove the group XX part. This allows sorting, which allows uniq -c to count the occurences and that can be sorted in a way that we get the above list. However. For a quick look the above should be fine.

Also, depending on the size of your logfiles you might not be able to use the above for parsing. If you have lots of log entries (high traffic) I would suggest to use something like an ELK stack for analysis.

Let us take a look at some of these rules now.

12872 GPL ICMP_INFO PING *NIX

This list (ICMP_INFO) gives you information about e.g. pings. So whenever someone is pinging your system(s) you’ll see this message. To be honest, I don’t want ping probes to fill my logs. And I also don’t want to block ping probes because I want to use ping probes for debug purposes (can I reach my system?). So this is not a useful rule for me.

So I create /etc/suricata/disable.conf using the contents of the documentation https://suricata-update.readthedocs.io/en/latest/update.html and I add the SIDs which contains the ping-rules. Don’t mind my grep commands – you’ll likely find a better way to achieve this :^) Also I’m not saying you should disable logging of pings. I’m just demonstrating how you COULD work with suricata.

~# cat /etc/suricata/disable.conf 
# suricata-update - disable.conf

# Example of disabling a rule by signature ID (gid is optional).
# 1:2019401
# 2019401

# Disabled ping logging
# grep -i icmp_info /var/lib/suricata/rules/suricata.rules | grep -v "^#"
# grep -i icmp_info /var/lib/suricata/rules/suricata.rules | grep -v "^#" | sed 's_.*sid:__g' | sed 's_; rev.*__g'

2100366
2100368
2100369
2100370
2100371
2100373
2100374
2100376
2100377
2100378
2100379
2100380
2100482
2100480

# Example of disabling a rule by regular expression.
# - All regular expression matches are case insensitive.
# re:heartbleed
# re:MS(0[7-9]|10)-\d+

# Examples of disabling a group of rules.
# group:emerging-icmp.rules
# group:emerging-dos
# group:emerging*

# Disable all rules with a metadata of "deployment perimeter". Note that metadata
# matches are case insensitive.
# metadata: deployment perimeter

Did you run an update with suricata-update yet? If not you should do. It will also show you that disabling works:

~# suricata-update 
<Info> -- Loading /etc/suricata/update.yaml
<Info> -- Using data-directory /var/lib/suricata.
<Info> -- Using Suricata configuration /etc/suricata/suricata.yaml
<Info> -- Using /etc/suricata/rules for Suricata provided rules.
<Info> -- Found Suricata version 6.0.10 at /usr/bin/suricata.
<Info> -- Loading /etc/suricata/disable.conf.
<Info> -- Loading /etc/suricata/suricata.yaml
<Info> -- Disabling rules for protocol http2
<Info> -- Disabling rules for protocol modbus
<Info> -- Disabling rules for protocol dnp3
<Info> -- Disabling rules for protocol enip
<Info> -- Fetching https://sslbl.abuse.ch/blacklist/sslblacklist.rules.
 100% - 1697921/1697921               
<Info> -- Done.
<Info> -- Fetching https://sslbl.abuse.ch/blacklist/ja3_fingerprints.rules.
 100% - 25718/25718                   
<Info> -- Done.
<Info> -- Fetching https://openinfosecfoundation.org/rules/trafficid/trafficid.rules.
 100% - 9855/9855                     
<Info> -- Done.
<Info> -- Fetching https://raw.githubusercontent.com/travisbgreen/hunting-rules/master/hunting.rules.
 100% - 76304/76304                   
<Info> -- Done.
<Info> -- Checking https://rules.emergingthreats.net/open/suricata-6.0.10/emerging.rules.tar.gz.md5.
<Info> -- Remote checksum has not changed. Not fetching.
<Info> -- Ignoring file rules/emerging-deleted.rules
<Info> -- Loaded 51400 rules.
<Info> -- Disabled 14 rules.
<Info> -- Enabled 0 rules.
<Info> -- Modified 0 rules.
<Info> -- Dropped 0 rules.
<Info> -- Enabled 131 rules for flowbit dependencies.
<Info> -- Backing up current rules.
<Info> -- Writing rules to /var/lib/suricata/rules/suricata.rules: total: 51400; enabled: 41170; added: 0; removed 0; modified: 14
<Info> -- Writing /var/lib/suricata/rules/classification.config
<Info> -- Testing with suricata -T.
<Info> -- Done.

And how does all this help you to make Suricata block / drop packets now? If you configured suricata as an IPS like – then you need to modify the rules in a way that Suricata uses them to do blocking. Similarly to the above you would find the rules you want to block packets. Add them to drop.conf in /etc/suricata (taken from the documentation: https://suricata-update.readthedocs.io/en/latest/update.html#example-drop-conf)

# suricata-update - drop.conf
#
# Rules matching specifiers in this file will be converted to drop rules.
#
# Examples:
#
# 1:2019401
# 2019401
#
# re:heartbleed
# re:MS(0[7-9]|10)-\d+

The next entry in my logs (10309 ET DROP Dshield Block Listed Source) is one I would indeed use for blocking. If you grep for this one you will see it’s only one rule. The current SID is: 2402000. So I add the following to my drop.conf:

# 10309 hits    ET DROP Dshield Block Listed Source
2402000

After running suricata-update I see:

-- Loaded 51400 rules.
-- Disabled 14 rules.
-- Enabled 0 rules.
-- Modified 0 rules.
-- Dropped 1 rules.
-- Enabled 131 rules for flowbit dependencies.

Now let’s get back to my initial command to get a list of the log entries. I modified that command a bit and worked a little bit more with awk. Now here’s a list of dropped packets:

~# awk '/Drop/{$1="";$2="";$3="";$4="";sub(/\[.*/,"");gsub(/^[ \t]+|[ \t]+$/,"");sub(/group.*/,"");print}' fast.log | sort | uniq -c | sort -hr

   6505 IP dropped due to fail2ban detection
    638 ET DROP Dshield Block Listed Source 

You can also use this modified command to get a list of what’s going on (not just Drop / excluding drop):

~# awk '!/Drop/{$1="";$2="";$3="";$4="";sub(/\[.*/,"");gsub(/^[ \t]+|[ \t]+$/,"");sub(/group.*/,"");print}' fast.log | sort | uniq -c | sort -hr

  14691 ICMP_INFO PING *NIX
  11578 DROP Dshield Block Listed Source 
   6851 CINS Active Threat Intelligence Poor Reputation IP 
   5558 TCPv4 invalid checksum
   1724 3CORESec Poor Reputation IP 
   1716 SCAN Potential SSH Scan
   1413 INFO SSH-2.0-Go version string Observed in Network Traffic - Inbound
   1156 Applayer Detect protocol only one direction
    646 COMPROMISED Known Compromised or Hostile Host Traffic 
    517 SCAN LibSSH Based Frequent SSH Connections Likely BruteForce Attack
    513 STREAM excessive retransmissions
    388 SCAN Suspicious inbound to MSSQL port 1433
    333 SMTP no server welcome message
    262 ICMP_INFO PING speedera
    237 STREAM Last ACK with wrong seq
    235 SCAN Suspicious inbound to mySQL port 3306
    230 SCAN Suspicious inbound to PostgreSQL port 5432
    223 UDPv4 invalid checksum
    104 RPC portmap listing UDP 111
    103 STREAM 3way handshake excessive different SYN/ACKs
    101 DROP Spamhaus DROP Listed Traffic Inbound 

[..]

Of course this result will still contain the previously not dropped entries. So every now and then you should probably just start with a fresh log for this sort of analysis.

Use the variables, Luke!

Did you see in suricata.yaml that there are the following variables?

HTTP_SERVERS: "$HOME_NET"
SMTP_SERVERS: "$HOME_NET"
SQL_SERVERS: "$HOME_NET"
DNS_SERVERS: "$HOME_NET"
TELNET_SERVERS: "$HOME_NET"
AIM_SERVERS: "$EXTERNAL_NET"
DC_SERVERS: "$HOME_NET"
DNP3_SERVER: "$HOME_NET"
DNP3_CLIENT: "$HOME_NET"
MODBUS_CLIENT: "$HOME_NET"
MODBUS_SERVER: "$HOME_NET"
ENIP_CLIENT: "$HOME_NET"
ENIP_SERVER: "$HOME_NET"

If you grep for them in the rules you will see that these variables are used. Look at sid 2102259 for example. The rule’s message is: GPL SMTP EXPN overflow attempt and it references CVE-2002-1337 which itself might have been used to cause a buffer overflow in sendmail.

Now is there any good reason to check HTTP Traffic against this rule? No. That’s why the rule looks like this:

alert tcp $EXTERNAL_NET any -> $SMTP_SERVERS 25 (msg:"GPL SMTP EXPN overflow attempt"; [..]

My advise is: use the variables whenever possible. Because some, not all rules make use of them. And if you write your own rules you should try to use these variables as well.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.