🛡️🔒Threat intelligence script updated for use with nftables🗜️

🛡️🔒Threat intelligence script updated for use with nftables🗜️

🔥 Enhance your network security with this article on the extra threat intelligence script installed on SD-WAN aggregators! 🚀 Learn how this powerful script helps scrub level 1 threats, ensuring your network stays protected from potential risks. Don't leave your network vulnerable – dive into our article now to discover how to bolster your security defenses!🛡️🔒

·

6 min read

Many moons ago I create a threat intelligence script based on iptables.

Here is the update variant for use with nftables. What is neat about this script is that it scrubs any level 1 threats automatically and is updated daily.

First install the prerequisites.

sudo apt-get install iprange grepcidr

Place the file extra-threatblock.sh in the directory /usr/local/sbin.

#!/bin/bash
# Extra Threatblock: A threat intelligence script by r00igev@@r/mybroadband
# Version 0.0.3 November 2023
# usage extra-threatblock.sh <configuration file>
# eg: extra-threatblock.sh /etc/extra/threatblock/extra-threatblock.conf
#
PATH=/usr/sbin:/usr/local/bin:/usr/bin:/bin:

function exists() { command -v "$1" >/dev/null 2>&1 ; }

# Check that configuration file has been specified
if [[ -z "$1" ]]; then
  echo "Error: please specify a configuration file, e.g. $0 /etc/extra/threatblock/extra-threatblock.conf"
  exit 1
fi

# Shellcheck source=extra-threatblock.conf
if ! source "$1"; then
  echo "Error: can't load configuration file $1"
  exit 1
fi

# Check if all commands exist
if ! (exists curl && exists egrep && exists grep && exists nft && exists sed && exists sort && exists wc && exists iprange && exists figlet); then
  echo >&2 "Error: searching PATH fails to find executables among: curl egrep grep nft sed sort wc iprange figlet"
  exit 1
fi

figlet -f slant Extra Threatblock
echo "Extra enabled gateway. Driving SD-WAN adoption in South Africa! https://fusionsdwan.co.za"

# Check if BLOCK_POLICY is defined
if [ -z "$BLOCK_POLICY" ]; then
  echo "Error: BLOCK_POLICY is not defined. Please set the BLOCK_POLICY variable."
  exit 1
fi

# Check if WAN_IF is defined
if [ -z "$WAN_IF" ]; then
  echo "Error: WAN_IF is not defined. Please set the WAN-IF variable."
  exit 1
fi

# Do CIDR optimization if set
DO_OPTIMIZE_CIDR=no
if exists iprange && [[ ${OPTIMIZE_CIDR:-yes} != no ]]; then
  DO_OPTIMIZE_CIDR=yes
fi

if [ -f "$EXTERNAL_BLOCKLIST_EXCEPTIONS" ]; then
   EXTERNAL_EXCEPTIONS_TMP=$(mktemp)
   for exception in $(sed -r -e 's/\s*#.*$//;/^$/d' "$EXTERNAL_BLOCKLIST_EXCEPTIONS")
      do
         exception_array+=( "$exception" )
         echo $exception >> $EXTERNAL_EXCEPTIONS_TMP
   done
fi

# Create the nftables set if needed (or abort if does not exist and FORCE=no)
# Check if the template file exists
if [ ! -f "$NFT_TEMPLATE" ]; then
  echo "Error: Template file not found at $NFT_TEMPLATE."
  exit 1
fi

# Use the nft command to delete the table
nft delete table inet "$BLOCKLIST_NAME" 2>/dev/null

echo "nftables table '$BLOCKLIST_NAME' dropped."

# Read the template file, replaces ${WAN_IF} and ${BLOCK_POLICY} with the actual values, and create the nftables table
nft -f <(sed -e "s/\${BLOCK_POLICY}/$BLOCK_POLICY/g" -e "s/\${WAN_IF}/$WAN_IF/g" "$NFT_TEMPLATE")

echo "nftables table '$BLOCKLIST_NAME' created with with WAN interface $WAN_IF and block policy $BLOCK_POLICY."

BLOCKLIST_TMP=$(mktemp)
echo -n "Downloading blacklists:"
for i in "${BLOCKLISTS[@]}"
do
  IP_TMP=$(mktemp)
  (( HTTP_RC=$(curl -L -A "extra-threatblock/script/mybroadband" --connect-timeout 10 --max-time 10 -o "$IP_TMP" -s -w "%{http_code}" "$i") ))
  if (( HTTP_RC == 200 || HTTP_RC == 302 || HTTP_RC == 0 )); then # "0" because file:/// returns 000
    command grep -Po '^(?:\d{1,3}\.){3}\d{1,3}(?:/\d{1,2})?' "$IP_TMP" | sed -r 's/^0*([0-9]+)\.0*([0-9]+)\.0*([0-9]+)\.0*([0-9]+)$/\1.\2.\3.\4/' >> "$BLOCKLIST_TMP"
    [[ ${VERBOSE:-yes} == yes ]] && echo -n "."
  elif (( HTTP_RC == 503 )); then
    echo -e "\\nUnavailable (${HTTP_RC}): $i"
  else
    echo >&2 -e "\\nWarning: curl returned HTTP response code $HTTP_RC for URL $i"
  fi
  rm -f "$IP_TMP"
done

# Optimize CIDR
sed -r -e '/^(0\.0\.0\.0|10\.|127\.|172\.1[6-9]\.|172\.2[0-9]\.|172\.3[0-1]\.|192\.168\.|22[4-9]\.|23[0-9]\.)/d' "$BLOCKLIST_TMP"|sort -n|sort -mu >| "$BLOCKLIST"
if [[ ${OPTIMIZE_CIDR} == yes ]]; then
  if [[ ${VERBOSE:-no} == yes ]]; then
    echo -e "\\nAddresses before CIDR optimization: $(wc -l "$BLOCKLIST" | cut -d' ' -f1)"
  fi
  < "$BLOCKLIST" iprange --optimize - > "$BLOCKLIST_TMP" 2>/dev/null
  if [[ ${#exception_array[@]} > 0 ]]; then
    echo "Allowing for ${#exception_array[@]} exclusions from threatblock"
    echo "Addresses before removing exclusions: $(wc -l "$BLOCKLIST_TMP" | cut -d' ' -f1)"
    BLOCKLIST_WITH_EXCEPT_TMP=$(mktemp)
    iprange "$BLOCKLIST_TMP" --except "$EXTERNAL_EXCEPTIONS_TMP" > "$BLOCKLIST_WITH_EXCEPT_TMP" 2>/dev/null
    cp "$BLOCKLIST_WITH_EXCEPT_TMP" "$BLOCKLIST_TMP"
  fi
  if [[ ${VERBOSE:-no} == yes ]]; then
    echo "Addresses after CIDR optimization:  $(wc -l "$BLOCKLIST_TMP" | cut -d' ' -f1)"
  fi
  cp "$BLOCKLIST_TMP" "$BLOCKLIST"
fi

rm -f "$BLOCKLIST_TMP"

echo -n "Populating nft threatblock:"

# Define the name of the set and the filename containing IPs/subnets
set_name="hexceptions-v4"
blocklist_file="$EXTERNAL_EXCEPTIONS_TMP"
# Read the internal exceptions file line by line
while IFS= read -r line; do
  # Add the IP/subnet to the nftables set 2>/dev/null
  nft add element inet threatblock "$set_name" { $line }
  # Increment the count
done < "$blocklist_file"

# Define the name of the set and the filename containing IPs/subnets
set_name="blocklist-v4"
blocklist_file="$BLOCKLIST"
# Count for printing dots
count=0
# Number of IPs to add in each batch - too big and nft will not load it
batch_size=512
# Initialize a string to store IPs with commas
ip_batch=""
# Read the blocklist file line by line
while IFS= read -r line; do
  # Add the IP to the batch with a comma separator
  if [ -z "$ip_batch" ]; then
    ip_batch="$line"
  else
    ip_batch="$ip_batch,$line"
  fi
  # If the batch size is reached, add IPs and print a dot
  if (( count % batch_size == batch_size - 1 )); then
    nft add element inet threatblock "$set_name" { $ip_batch } 2>/dev/null
    ((count += 1))
    echo -n "."
    ip_batch="" # Clear the batch
  fi
  ((count++))
done < "$blocklist_file"
# Add any remaining IPs in the last batch
if [ -n "$ip_batch" ]; then
  nft add element inet threatblock "$set_name" { $ip_batch } 2>/dev/null
  ((count++))
  echo -n "."
fi
echo

if [[ ${VERBOSE:-no} == yes ]]; then
   echo "Threatblock completed. $count IPs/subnets added to $set_name."
fi

Make the script executable: sudo chmod +x /usr/local/sbin/extra-threatblock.sh

Create a directory for the config files: sudo mkdir /etc/extra/threatblock

Create a direct for the log files: /var/log/threatblock

Create a configuration file extra-threatblock.conf in /etc/extra-threatblock

BLOCKLIST_NAME=threatblock # Table used within nftables
BLOCKLIST_TMP=${BLOCKLIST_NAME}-tmp
BLOCKLIST_DIR=/etc/extra/threatblock
BLOCKLIST=${BLOCKLIST_DIR}/extra-threatblock.list
EXTERNAL_BLOCKLIST_EXCEPTIONS=${BLOCKLIST_DIR}/external-${BLOCKLIST_NAME}.exceptions
NFT_TEMPLATE=${BLOCKLIST_DIR}/nft-blocklist.template # Template file for nftables rules

VERBOSE=yes # Set to no for cron jobs, default to yes
FORCE=yes # Will create the nft table if it does not already exist
OPTIMIZE_CIDR=yes # Optimize block list by aggregating overlapping subnets

# Block policy: 'drop' or 'reject', default: 'drop'
BLOCK_POLICY=drop

# WAN interface
WAN_IF=eth0

# Blocklists of IPs being dropped
BLOCKLISTS=(
    "file://${BLOCKLIST_DIR}/${BLOCKLIST_NAME}-custom.list" # Optional, for your personal nemeses
    "https://reputation.alienvault.com/reputation.generic" # Alienvault
    "https://iplists.firehol.org/files/talosintel_ipfilter.ipset" # Talos
    "https://iplists.firehol.org/files/tor_exits_7d.ipset" # TOR exit nodes
    "https://cinsscore.com/list/ci-badguys.txt" # C.I. Army Malicious IP List
    "https://lists.blocklist.de/lists/all.txt" # blocklist.de attackers
    "https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt" # Emerging threats
    "https://rules.emergingthreats.net/blockrules/compromised-ips.txt" # Emerging threats
    "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset" # Firehol Level 1
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_shadowserver.txt" # Shadow server
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_shodan.txt" # Shodan
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_sogou.txt" # Sogou search engine
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_internet_cens.txt" # Internet census
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_diverseenvironment.txt" # Diverse environment
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_netsysres.txt" # Net Systems Research
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_rwth-aachen.txt" # AAChen
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_onyphe.txt" # onephe
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_stretchoid.txt" # Strechoid
    "https://gitlab.com/ohisee/block-shodan-stretchoid-census/raw/master/pf_table_openportsstats.txt" # OpenPortStats
    "https://iplists.firehol.org/files/normshield_high_webscan.ipset" # Normshield high risk scanners
    "https://raw.githubusercontent.com/stamparm/ipsum/master/levels/3.txt" # IPSUM
    "https://raw.githubusercontent.com/ipverse/rir-ip/master/country/ru/ipv4-aggregated.txt" # Russia se push
    "https://raw.githubusercontent.com/ipverse/rir-ip/master/country/ng/ipv4-aggregated.txt" # Nigeria se push
)
MAXELEM=131072

The nft template file named nft-blocklist.template is also located in /etc/extra/threatblock.

table inet threatblock {
        set hexceptions-v4 {
                type ipv4_addr
                flags interval
                auto-merge
        }
        set hexceptions-v6 {
                type ipv6_addr
                flags interval
                auto-merge
        }
        set blocklist-v4 {
                type ipv4_addr
                flags interval
                auto-merge
        }
        set blocklist-v6 {
                type ipv6_addr
                flags interval
                auto-merge
        }
        chain forward {
                type filter hook forward priority -1; policy accept;
                iifname lo accept
                ct state established,related accept
                iifname "${WAN_IF}" ip daddr @blocklist-v4 counter ${BLOCK_POLICY}
                iifname "${WAN_IF}" ip6 daddr @blocklist-v6 counter ${BLOCK_POLICY}
                iifname "${WAN_IF}" ip saddr @blocklist-v4 counter ${BLOCK_POLICY}
                iifname "${WAN_IF}" ip6 saddr @blocklist-v6 counter ${BLOCK_POLICY}
                counter
        }
        chain input {
                type filter hook input priority -1; policy accept;
                iifname lo accept
                ct state established,related accept
                iifname "${WAN_IF}" ip saddr @hexceptions-v4 counter accept
                iifname "${WAN_IF}" ip6 saddr @hexceptions-v6 counter accept
                iifname "${WAN_IF}" ip saddr @blocklist-v4 counter ${BLOCK_POLICY}
                iifname "${WAN_IF}" ip6 saddr @blocklist-v6 counter ${BLOCK_POLICY}
                counter
        }
        chain output {
                type filter hook output priority -1; policy accept;
                iifname lo accept
                ct state established,related accept
                iifname "${WAN_IF}" ip daddr @blocklist-v4 counter ${BLOCK_POLICY}
                iifname "${WAN_IF}" ip6 daddr @blocklist-v6 counter ${BLOCK_POLICY}
                counter
        }

The script uses an exceptions file named external-threatblock.exceptions and a custom list named threatblock-custom.list.

Ronald works connecting Internet inhabiting things at Fusion Broadband.