1. Overview

In this tutorial, we’ll look at how we can configure our Linux systems to prevent port scanners from identifying the kinds of services and ports in our system. There are various ways to protect our computers from Nmap port scans. We’ll look at iptables and Intrusion Detection Systems (IDS).

2. Using iptables

In this section, we’ll create iptable rules to control the traffic. Mainly, we’ll filter the traffic entering our system based on ports and source IP addresses. It’s advisable to block/close any unused port. This saves us from potential attacks on those ports.

2.1. Creating Default Rules

Now, let’s look at some necessary default rules. First, we’ll need to allow all ESTABLISHED connections and ongoing sessions through iptables.

First, let’s add two lines to allow incoming and outgoing traffic on the loopback interface:

$ sudo iptables -A INPUT -i lo -j ACCEPT
$ sudo iptables -A OUTPUT -o lo -j ACCEPT

Next, let’s allow ESTABLISHED and RELATED incoming traffic on the host:

$ sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Then, let’s allow ESTABLISHED outgoing connections. These are mainly responses to already made connections:

$ sudo iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED -j ACCEPT

Next, let’s allow connections between the internal and external connections:

$ sudo iptables -A FORWARD -i enp0s2 -o enp0s3 -j ACCEPT

Lastly, let’s drop all other invalid traffic:

$ sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

2.2. Example of Rules to Protect Against Nmap Scans

In this section, we’ll create rules to filter and drop identified Nmap scans. First, let’s add a few basic rules to get started:

$ sudo iptables -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j DROP
$ sudo iptables -A INPUT -p tcp --tcp-flags ACK,URG URG -j DROP

These rules will generally drop any incoming traffic that contains any of the specified flags. We can configure our system to allow this kind of traffic from specific or trusted sources. Further, we can configure this kind of traffic to be dropped on certain interfaces or ports.

Subsequently, we need to add a few more rules to harden our system further. Let’s add these rules on the adapter facing the internet:

$ sudo iptables -A INPUT -p tcp -i enp0s3 -m state --state NEW -m recent --set
$ sudo iptables -A INPUT -p tcp -i enp0s3 -m state --state NEW -m recent --update --seconds 30 --hitcount 10 -j DROP
$ sudo iptables -A FORWARD -p tcp -i enp0s3 -m state --state NEW -m recent --set
$ sudo iptables -A FORWARD -p tcp -i enp0s3 -m state --state NEW -m recent --update --seconds 30 --hitcount 10 -j DROP

Now, we can see the results when we run the nmap command:

$ sudo nmap -Pn 192.168.139.131
Nmap scan report for 192.168.139.131
Not shown: 983 filtered tcp ports (no-response)
PORT STATE SERVICE
23/tcp closed telnet
111/tcp closed rpcbind
139/tcp closed netbios-ssn
993/tcp closed imaps
995/tcp closed pop3s
1434/tcp closed ms-sql-m
3306/tcp closed mysql
5900/tcp closed vnc

Another scan would give:

$ sudo nmap -sS 192.168.139.131
Nmap scan report for 192.168.139.131
Not shown: 991 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
23/tcp closed telnet
25/tcp closed smtp
53/tcp closed domain
80/tcp closed http

From this example, we can see there are 991 TCP ports in a filtered state. It’s important we note that the filtered state is the most secure state as it prevents the attacker from getting any information on the kind of service running on that particular port.

After performing the scan, we can further add specific rules for ports that have been identified as open:

$ sudo iptables -A INPUT -p tcp -s 192.168.0.147 --destination-port 22 -i enp0s3 -j ACCEPT

Also, we can explicitly block traffic from the target IP address using iptable rules like:

$ sudo iptables -A INPUT -i enp0s3 -s 192.168.0.147 -j DROP

Next, let’s save the iptable rules we’ve written:

$ sudo iptables-save > ./ip.rules

Lastly, we can restore these settings on boot when we run:

$ sudo iptables-restore < ./ip.rules

3. Using an Intrusion Detection System

In this section, let’s look at how we can use an IDS to protect against Nmap scans. Intrusion Detection Systems are network security technology created to detect possible attacks against a target application. An IDS monitors and logs traffic.

We can configure it to take particular actions when certain traffic is detected. Some IDS can also work as Intrusion Prevention Sytems (IPS). There are various network IDS available. For example, some of the commonly used ones are Snort and Surricata.

Let’s look at Snort.

3.1. Installing Snort

Snort can work both as an IDS and an IPS. We can configure Snort as a packet sniffer or packet logger (for debugging purposes), or it can be configured as a network intrusion prevention system.

Next, let’s use apt-get to install Snort:

$ sudo apt-get update
$ sudo apt-get install -y snort

Lastly, let’s confirm the installation:

$ snort --version
   ,,_     -*> Snort! <*-
  o"  )~   Version 2.9.15.1 GRE (Build 15125) 
   ''''    By Martin Roesch & The Snort Team: http://www.snort.org/contact#team
           Copyright (C) 2014-2019 Cisco and/or its affiliates. All rights reserved.
           Copyright (C) 1998-2013 Sourcefire, Inc., et al.
           Using libpcap version 1.10.1 (with TPACKET_V3)
           Using PCRE version: 8.39 2016-06-14
           Using ZLIB version: 1.2.11

We should ensure that the following files exist. Otherwise, we need to create them:

$ sudo mkdir /usr/local/etc/rules
$ sudo mkdir /usr/local/etc/so_rules/
$ sudo mkdir /usr/local/etc/lists/
$ sudo touch /usr/local/etc/rules/local.rules
$ sudo touch /usr/local/etc/lists/default.blocklist
$ sudo mkdir /var/log/snort

3.2. Creating Snort Rules

In our configurations, we’ll use 192.168.0.102 as the IP address of the computer we’re protecting. Then, we’ll use the following options with the rules we’re creating:

  • msg stands for the message to display with the alert.
  • sid is a snort rule ID. Numbers under one million are reserved and we shouldn’t use them.
  • rev stands for revision number, which makes it easier to maintain the rules.
  • flag option checks if the specified flag is present in the TCP header.

Next, let’s add our custom rule to create alerts on specific Nmap scans. We add our custom rules to the local.rules file we created:

$ sudo vi /usr/local/etc/rules/local.rules

First, let’s write a rule to identify the Nmap TCP scan:

alert tcp any any -> 192.168.0.102 22 (msg: "NMAP TCP Scan";sid:10000005; rev:2; )

Secondly, let’s add another rule to alert UDP scans:

alert udp any any -> 192.168.0.102 any ( msg:"Nmap UDP Scan"; sid:1000010; rev:2; )

Next, let’s add rules to alert on XMAS, FIN, and NULL Scan:

alert tcp any any -> 192.168.0.102 22 (msg:"Nmap XMAS Tree Scan"; flags:FPU; sid:1000006; rev:3; )
alert tcp any any -> 192.168.0.102 22 (msg:"Nmap FIN Scan"; flags:F; sid:1000008; rev:4;)
alert tcp any any -> 192.168.0.102 22 (msg:"Nmap NULL Scan"; flags:0; sid:1000009; rev:5; )

Let’s save the rules and use Nmap to test. First, let’s run snort:

$ sudo snort -A console -q -c /etc/snort/snort.conf -i enp0s3

snort.conf is the main configuration file. We can also run our custom rules file directly by specifying its path:

$ sudo snort -A console -q -c /usr/local/etc/rules/local.rules -i enp0s3

Additionally, we can use the community rules defined in the /etc/snort/rules directory.

Let’s run a few additional Nmap scans:

$ sudo nmap -sT 192.168.0.102 -p22
$ sudo nmap -sX 192.168.0.102 -p22
$ sudo nmap -sF 192.168.0.102 -p22
$ sudo nmap -sN 192.168.0.102 -p22

The first scan tests for TCP SYN/Connect while the subsequent ones test for TCP Null, FIN, and XMAS scans, respectively.

As the Nmap scans run, on our terminal, we’ll see:

$ sudo snort -A console -q -c /usr/local/etc/rules/local.rules -i enp0s3
10/02-09:38:34.474108  [**] [1:10000005:2] NMAP TCP Scan [**] [Priority: 0] {TCP} 192.168.0.105:42818 -> 192.168.0.102:22
10/02-09:38:34.474500  [**] [1:10000005:2] NMAP TCP Scan [**] [Priority: 0] {TCP} 192.168.0.105:42818 -> 192.168.0.102:22
10/02-09:39:31.962333  [**] [1:1000006:1] Nmap XMAS Tree Scan [**] [Priority: 0] {TCP} 192.168.0.105:64492 -> 192.168.0.102:22
10/02-09:39:32.062685  [**] [1:10000005:2] NMAP TCP Scan [**] [Priority: 0] {TCP} 192.168.0.105:64494 -> 192.168.0.102:22
10/02-09:39:32.062685  [**] [1:1000006:1] Nmap XMAS Tree Scan [**] [Priority: 0] {TCP} 192.168.0.105:64494 -> 192.168.0.102:22
10/02-09:40:17.038395  [**] [1:1000008:1] Nmap FIN Scan [**] [Priority: 0] {TCP} 192.168.0.105:47171 -> 192.168.0.102:22
10/02-09:40:17.138684  [**] [1:10000005:2] NMAP TCP Scan [**] [Priority: 0] {TCP} 192.168.0.105:47173 -> 192.168.0.102:22
10/02-09:40:17.138684  [**] [1:1000008:1] Nmap FIN Scan [**] [Priority: 0] {TCP} 192.168.0.105:47173 -> 192.168.0.102:22
10/02-09:41:05.934639  [**] [1:1000009:1] Nmap NULL Scan [**] [Priority: 0] {TCP} 192.168.0.105:56172 -> 192.168.0.102:22
10/02-09:41:06.034858  [**] [1:10000005:2] NMAP TCP Scan [**] [Priority: 0] {TCP} 192.168.0.105:56174 -> 192.168.0.102:22
10/02-09:41:06.034858  [**] [1:1000009:1] Nmap NULL Scan [**] [Priority: 0] {TCP} 192.168.0.105:56174 -> 192.168.0.102:22
10/02-09:41:06.264484  [**] [1:10000005:2] NMAP TCP Scan [**] [Priority: 0] {TCP} 192.168.0.105:41500 -> 192.168.0.102:22

The rules we’d defined have matched the protocols we targeted. We can now analyze these logs (using Wireshark) and write firewall rules that mitigate against them.

Secondly, after analyzing our Snort logs, we can write rules to drop specific activities. For example, we can write a rule to drop any attempt for a NULL scan:

drop tcp any any -> 192.168.0.102 22 (msg:"Nmap NULL Scan"; flags:0; sid:1000009; rev:5; )

Alternatively, we can use other technologies like fail2ban to automate the actions taken when certain alerts are triggered.
Fail2ban is an intrusion prevention tool that monitors log files for specified patterns/signatures and takes action against IP addresses that display malicious activity.

After going through the logs, if fail2ban detects entries that match the filters defined, it marks them as failed attempts. Fail2ban bans or blocks the source IP address when the failed attempts threshold has been met or exceeded.

We should note that Nmap scans can take various forms, and we need to create additional rules or modify existing ones to detect specific scan types or patterns, such as SYN scans, FIN scans, or Xmas scans.

4. Conclusion

In this article, we’ve looked at how to configure iptables and use intrusion detection to protect against Nmap port scans. Even though it’s entirely difficult to prevent port scans for computers facing the internet, we can use various methods to protect a computer from port scanners.

Updating these rules regularly increases their effectiveness. But, we should be mindful when writing iptable rules as we can inadvertently block necessary connections.