TCP Tarpit and Port Scanning. Barricades on Both Sides.

TCP Tarpit and Port Scanning. Barricades on Both Sides.

Introduction

Thousands of articles have been written about port scanning, discussing techniques, methodologies, tools... Yet, even in such a seemingly straightforward topic, there are several less-covered nuances.

If you regularly scan subnets, participate in bug bounties, perform penetration testing, or automate this process, then this article might be useful to you.

Have you ever encountered a situation where scanning a small subnet takes an unreasonably long time? Have you seen hosts with all 65,535 ports open? Well, okay, maybe not all 65,535, but at least a few hundred or thousand.

If you have to do a lot of scanning, you have likely come across this.

Many firewalls, gateways, routers, IDS/IPS have functionality to combat port scanning by misleading the scanner and increasing its computational and temporal resource consumption.

This defense technique may be called different names depending on the vendor, but here we will use the term "Tarpit."

What is TARPIT?

Tarpitting, or simply Tarpit, is a method of combating port scanning, email spam, and other types of attacks on network devices. It involves establishing a fictitious TCP connection through which data transmission is possible.

It significantly slows down port scanning, rendering its results irrelevant and causing the scanner to waste time and resources.

I recommend reading the article at https://en.wikipedia.org/wiki/Tarpit_(networking) for more information.

How It Works (Using Port Scanning as an Example)

Instead of ignoring the connection request (DROP) or sending an RST (REJECT) to the scanner, a SYN/ACK packet is sent to the scanner, and the TCP window size is set to zero. As a result, the scanner perceives the port as open and the connection as established, but no data can be transmitted over this connection.

The logic of port scanning protection can be represented in the form of a diagram:

Figure 1: Diagram illustrating the logic of port scanning protection.


In practice, the operation of the protection may look something like this:

  1. Port scanner is launched.
  2. The firewall protecting the scanned host analyzes incoming packets and detects that packets from a specific IP address are targeting different TCP ports. If this activity exceeds a certain threshold, the IP address is added to a special IP list.
  3. The firewall protecting the scanned host checks the src-ip of incoming TCP packets for inclusion in this IP list. If the IP address is on the list, the firewall tarpits each connection attempt.
  4. The port scanner registers the port as open.
  5. If additional options and NSE plugins are used during the scanning, such as service version detection (-sV flag), nmap will send multiple packets to the "open" ports and wait in vain for a response. Depending on the options and timeouts, such scanning can take hours.
  6. As a result, after a prolonged waiting period, the scanner ends up with strange and useless results.

.

Fig. 2: Illustration of port scanning protection in action.

Illustration of tarpit operation.

Let's consider the same scenario of scanning ports 80, 443, and 3389 (where port 3389 is open) and scan these ports.

Figure 3 shows the response of a host without firewall protection. If a port is open, the scanned host sends a packet with SYN/ACK flags, and if it's closed, it sends RST/ACK. There are no obstacles for scanning, and it occurs quickly without surprises, providing accurate results.

Fig. 3: Port scanning of an unprotected host.

Figure 4 demonstrates the behavior of a host protected by a firewall. Requests to closed/unauthorized ports are ignored (DROP policy). Scanning takes more time due to the retransmission of packets by the scanner and the timeouts associated with these operations. The results of such scanning remain accurate, as all open ports will be detected.


Fig. 4: Port scanning of a host protected by a firewall with a DROP policy, without port scanning protection.

The firewall can monitor unwanted network activity (attempting to check multiple ports) and add the scanner's src-ip to a blacklist, ignoring all packets from that IP. Figure 5 illustrates this scenario. Scanning will take more time due to the reasons mentioned above, and no open ports will be identified.

Fig. 5: Port scanning of a host protected by a firewall with port scanning protection and a DROP policy.

When using port scanning protection with the tarpit policy, the scanner will receive a positive result for all port status checks (see Fig. 6). Seeing such a "open" port, the scanner will futilely attempt to interact with it. This not only devalues the results of such scanning but can also significantly slow it down if the scanner utilizes additional options and modules (service version detection, operating system identification, plugin execution, etc.).

Fig. 6: Port scanning of a host protected by a firewall with port scanning protection and the TARPIT policy.

Some products where tarpitting can be used include:

  • Linux iptables (likely requires the installation of xtables-addons)
  • Mikrotik RouterOS
  • Untangle Linux

Similar functionality is likely available in Cisco, Juniper, Huawei, Fortinet Fortigate, Imperva, pfSense, and other products. However, I couldn't quickly find the specific names and configuration examples for this feature in these products.

And of course, it's worth mentioning standalone programs that implement this protection technique:

Tarpit in the Wild

Let's say you have never encountered such protection before (or simply didn't know you encountered it) and want to see how it looks in action. The first thing that comes to mind is searching for hosts on Shodan with an open, less popular port.

https://www.shodan.io/search?query=port:20547

There are plenty of results available. Hosts labeled "No data returned" are likely what we're looking for. If you open the details of such a host, you will see the following: [provide an image or description of the details of the host with tarpit protection]

How to apply it to protect your nodes?

Implementing tarpit significantly complicates the scanning of your network by automated tools and bots. Most likely, they will ignore your hosts due to timeouts and won't obtain any results. This protection will also be unpleasant for attackers and other curious individuals.

Let's consider the configuration of port scanning protection with Tarpit using Mikrotik RouterOS as an example:

/ip firewall filter


add action=add-src-to-address-list address-list=PortScanDetected \

address-list-timeout=1h chain=input comment="Anti PortScan Basic" \

in-interface-list=WAN protocol=tcp psd=21,3s,3,1 src-address-list=\

!Whitelist


add action=add-src-to-address-list address-list=PortScanDetected \

address-list-timeout=1h chain=input comment="Anti PortScan UDP" \

in-interface-list=WAN protocol=udp psd=21,3s,3,1 src-address-list=\

!Whitelist


add action=tarpit chain=input comment=BanPortScan in-interface-list=WAN \

protocol=tcp src-address-list=PortScanDetected


The first and second rules are used to identify IP addresses involved in scanning, and those addresses are added to the src-address-list. The third rule applies tarpit to all TCP connections from addresses in this list.

When configuring this type of protection, it's important to have whitelists in place to prevent your resources from accidentally being affected or to prevent attackers from adding your IP addresses to the blacklist through spoofing.

By the way, you can implement other criteria for IPs to enter a special address list, such as:

  • Assigning an additional unused IP to the router, creating a kind of honeypot that adds all requests to the address list.
  • Adding entries to the address list for accessing certain popular ports that you don't use.
  • Adding entries based on suspicious activity detected by an IPS system (e.g., Snort, Suricata), such as exceeding the threshold for authentication errors on different services.

How to account for tarpitted hosts when scanning subnets?

Now let's look at tarpit from a different perspective, putting ourselves in the shoes of a pentester. The task is to scan several large subnets. How can we reduce scanning time and make the results more accurate?

I couldn't find a ready-made solution for popular scanners like Nmap, Naabu, RustScan, or Masscan. Yes, of course, it's possible to reduce timeouts, increase the number of scanning threads, set time limits for scanning a single host, and so on. But this cannot be considered a complete solution because there may be no results for some hosts (due to timeouts), while all ports may appear "open" for others.

With a high probability, protected hosts will either be lost or ignored.

Attempting to improve nmap

You might find it interesting that the tarpit issue was discussed in nmap mailing lists back in 2014. Jay Bosamiya wrote a patch for nmap that added the --ignore-after flag, allowing skipping hosts with a certain number of open ports. However, the topic fizzled out, and the patch was never accepted.

If you're interested, you can review the details here:

https://seclists.org/nmap-dev/2014/q2/545

https://seclists.org/nmap-dev/2014/q3/164

You can even try building nmap with this feature by taking the code from the repository:

https://svn.nmap.org/nmap-exp/jay/ignore-after/

Example Dockerfile:

FROM alpine:3.17


# Install dependencies

RUN apk add --update --no-cache \

   ca-certificates \

   libpcap \

   libgcc libstdc++ \

   subversion \

   libssl3 \

 && update-ca-certificates \

 && rm -rf /var/cache/apk/*


# Compile and install Nmap from sources

RUN apk add --update --no-cache --virtual .build-deps \

     libpcap-dev lua-dev linux-headers openssl-dev \

     autoconf g++ libtool make \

\

&& svn co https://svn.nmap.org/nmap-exp/jay/ignore-after/ \

&& cd ignore-after \

&& ./configure \

     --prefix=/usr \

     --sysconfdir=/etc \

     --mandir=/usr/share/man \

     --infodir=/usr/share/info \

     --without-zenmap \

     --without-nping \

     --without-nmap-update \

     --without-openssl \

     --with-liblua=/usr/include \

&& make \

&& make install \

\

&& apk del .build-deps \

&& rm -rf /var/cache/apk/*


ENTRYPOINT ["/usr/bin/nmap"]


The mentioned patch, although it works, is relevant for an older version of Nmap - 6.46. It's not advisable to use it in production, but it can be useful for exploration purposes.

docker build . -t 'nmap-ignore-after'

docker run --rm -it nmap_ignore_after --ignore-after 40 _TARGET_


Example of how it works:

docker run --rm -it nmap_ignore_after --ignore-after 40 _TARGET_ -d

Initiating SYN Stealth Scan at 05:30

Discovered open port 25/tcp on _TARGET_

Discovered open port 22/tcp on _TARGET_

Discovered open port 21/tcp on _TARGET_

...

Discovered open port 10003/tcp on _TARGET_

Discovered open port 981/tcp on _TARGET_


Completed SYN Stealth Scan at 05:33, 1.61s elapsed (1000 total ports)

Overall sending rates: 621.66 packets / s, 27352.91 bytes / s.

Nmap scan report for _TARGET_ [host ignored, too-many-open-ports]

Host is ignored, too-many-open-ports TTL 46 (0.053s latency).


Plugin for nmap

Obviously, before the main scanning, you need to identify and filter out protected hosts. There are various ways to do this, such as writing a bash/ruby/python wrapper for nmap. However, I decided to write an NSE plugin.

https://github.com/Onsec-io/nmap-tarpit

The plugin's logic is straightforward - provoke a tarpit response from the host. The plugin selects a specified number of random ports and attempts to establish a connection. If all of these random ports are open or the percentage of open ports exceeds the set value (default is 70%), the host is marked as a tarpit.

The goal is to identify tarpit hosts to handle them separately. The plugin is intended to be run with NMAP on the relevant subnets before the main port scanning process.

To save time, it's important to ensure that the host is alive before running the plugin. You can use the built-in host-discovery capabilities of nmap (for more details, see: https://nmap.org/book/man-host-discovery.html)

The example below includes several host-discovery techniques that increase the chances of finding live hosts in the subnet, even in the presence of packet filtering.

# nmap \

  -T4 \

  -sn \

  -PE \

  -PY \

  -PO \

  -PS21,22,25,80,443,8080 \

  -PA21,22,25,80,443,8080 \

  --script tarpit.nse \

  -oX output.xml \

  _TARGET_


So that you don't have to remember what these command-line options do:

-T4: Timing template settings (see https://nmap.org/book/performance-timing-templates.html) don't affect the NSE plugin, only the host-discovery process.

-sn: Don't perform port scanning. Port scanning is not required at this stage.

-PE: Host discovery using ICMP type 8 (echo request), i.e., regular ping.

-PY: Host discovery using the SCTP protocol.

-PO: IP Protocol Ping.

-PS21,22,25,80,443,8080: TCP SYN Ping. Additionally, it provokes the scanned host into a TARPIT.

-PA21,22,25,80,443,8080: TCP ACK Ping.

--script tarpit.nse: Include the tarpit NSE plugin.

-oX output.xml: Write the results to an XML file.

In this example, the -PS and -PA options are intentionally used with the port set 21, 22, 25, 80, 443, 8080. These are some of the most popular ports, which increase the chances of both host discovery and triggering the protection. -PS will further "irritate" the target. However, feel free to experiment, modify, or supplement this port list.

The tarpit.nse plugin has several arguments:

ports_count - The number of random ports in the range 1025-49151 to which connection requests will be sent.

scan1_timeout - Timeout for the first port scanning cycle. This cycle is used to provoke the host into triggering the tarpit. Default is 500ms.

scan2_timeout - Timeout for the second port scanning cycle. This is the main check stage. Default is 2500ms.

open_ports_percent - The percentage of open ports at which the host will be marked as a tarpit.

Example usage of tarpit.nse plugin arguments:

-script tarpit.nse --script-args "ports_count=25, scan1_timeout=300, scan2_timeout=1000, open_ports_percent=75" 


After the scanning is complete, a report will be saved in XML format.

Working with Nmap XML reports

There are many tools for parsing XML, and here we'll look at the xmlstarlet utility

(https://xmlstar.sourceforge.net/docs.php).

Obtaining information about the structure of the XML document.

Knowing the structure will help in writing queries.

xmlstarlet el -u output.xml

nmaprun

nmaprun/debugging

nmaprun/host

nmaprun/host/address

nmaprun/host/hostnames

nmaprun/host/hostnames/hostname

nmaprun/host/hostscript

nmaprun/host/hostscript/script

nmaprun/host/status

nmaprun/host/times

nmaprun/runstats

nmaprun/runstats/finished

nmaprun/runstats/hosts

nmaprun/verbose


Displaying attributes.

xmlstarlet el -a output.xml

nmaprun

nmaprun/@scanner

nmaprun/@args

nmaprun/@start

nmaprun/@startstr

nmaprun/@version

nmaprun/@xmloutputversion

nmaprun/verbose

nmaprun/verbose/@level

nmaprun/debugging

nmaprun/debugging/@level

nmaprun/host

nmaprun/host/@starttime

nmaprun/host/@endtime

nmaprun/host/status

nmaprun/host/status/@state

nmaprun/host/status/@reason

nmaprun/host/status/@reason_ttl

nmaprun/host/address

nmaprun/host/address/@addr

nmaprun/host/address/@addrtype

nmaprun/host/hostnames

nmaprun/host/hostnames/hostname

nmaprun/host/hostnames/hostname/@name

nmaprun/host/hostnames/hostname/@type

nmaprun/host/hostscript

nmaprun/host/hostscript/script

nmaprun/host/hostscript/script/@id

nmaprun/host/hostscript/script/@output

nmaprun/host/times

nmaprun/host/times/@srtt

nmaprun/host/times/@rttvar

nmaprun/host/times/@to


Show the IP addresses of all active hosts where TARPIT is NOT detected.

xmlstarlet sel -t -m "/nmaprun/host[not(hostscript/script[@id='tarpit'])]" -v "address/@addr" -n output.xml


xmlstarlet sel -t -m "/nmaprun/host[not(hostscript/script[@id='tarpit'])]" -v "concat(address/@addr,'  ', hostnames/hostname/@name)" -n output.xml


Show the IP addresses of all active hosts and the methods of their discovery.

xmlstarlet sel -t -m "/nmaprun/host/status[@state='up']" -v "concat(ancestor::host/address/@addr,'  ', ancestor::host/status/@reason)" -n output.xml


Display information only for hosts where TARPIT is detected.

xmlstarlet sel -t -m "/nmaprun/host/hostscript/script[@id='tarpit']" -v "ancestor::host/address/@addr" -n output.xml


xmlstarlet sel -t -m "/nmaprun/host/hostscript/script[@id='tarpit']" -v "ancestor::host/address[@addrtype='ipv4']/@addr" -n output.xml


xmlstarlet sel -t -m "/nmaprun/host/hostscript/script[@id='tarpit']" -v "concat(ancestor::host/address/@addr,' ', ancestor::host/hostnames/hostname/@name)" -n output.xml


Bypassing protection

In most cases, it's not possible to quickly determine which ports are actually open. However, I have encountered configuration errors where one of the following techniques helped:

# nmap -Pn -sA _TARGET_  #TCP ACK

# nmap -Pn -sW _TARGET_ #TCP Window

# nmap -Pn -sN _TARGET_  #Null

# nmap -Pn -sF _TARGET_  #FIN

# nmap -Pn -sX _TARGET_  #Xmas

# nmap -Pn --scanflags URGACKPSHRSTSYNFIN __TARGET__

For more information, see https://nmap.org/book/scan-methods.html

Here is an example where open ports are visible despite the tarpit.

In other cases, you will have to use slow scanning, scan from multiple source IPs, and so on.

Conclusion

Several conclusions can be drawn:

  1. Tarpit is not exotic and is encountered quite frequently. It is present in various vendors' systems and is sometimes discovered during audits. The search results in Shodan also support this thesis.
  2. Protection against port scanning using tarpit significantly slows down the scanner, forcing it to spend time and resources and distorting the results. Consider the possibility of using tarpit to protect your resources.
  3. When scanning nodes and subnets, be aware of the possible presence of such protection, identify protected hosts, and apply separate scanning settings to them.
  4. In some cases, it is possible to obtain information about open ports despite the protection. System administrators sometimes make configuration mistakes. Try bypassing techniques.

How do you deal with tarpit?

Author: 0x566164696D