Next Previous Contents

4. Detecting and blocking scans of the boundary network

A great tool for detecting scanning activity is Abacus Portsentry. You configure it to listen for network traffic on certain ports, and if a given host generates a goodly amount of traffic to those ports in a short period of time, a warning is generated and action is taken. It is intended to detect and react to the second stage of an attack scan: scanning all of the ports on a computer to see what services it is running, in an attempt to detect vulnerable services or determine what operating system is in use.

Though Portsentry is powerful, it is also simple (which is good), and is only designed to protect a single host. We can, however, use transparent proxy to enable Portsentry to detect scans of the entire boundary network. Using transparent proxy we will redirect scanning traffic from the hosts on the boundary network to the firewall itself, where all of the scanning traffic will be received by Portsentry.

4.1 Configuring Portsentry

To configure Portsentry to detect scans, edit the Portsentry configuration file (e.g. /etc/portsentry/portsentry.conf) and set the TCP_PORTS and UDP_PORTS lists to the “always hostile” port list you've compiled.

Transparent proxy is configured via firewall rules. For example, if we wished the gateway to receive all of the HTTP (80/tcp) traffic passing through it, we could run a firewall command similar to this:

/sbin/ipchains -A input -i $IFACE -p tcp -d $ANY 80 -j REDIRECT 8080

Meaning: any traffic coming in on interface $IFACE, protocol TCP, destined to port 80 on any machine anywhere, will be redirected to port 8080 on the local host. This illustrates the original goal of Transparent Proxy: proxying HTTP traffic without having to explicitly configure each individual local network client to use the proxy.

To use transparent proxy to allow PortSentry to trap scans of the entire boundary network, we must run a series of commands like:

/sbin/ipchains -A input -i $INET_IF -d $ANY 111 -j REDIRECT 111

Thus, any traffic coming into the outer firewall that is destined for port 111 on any host on the boundary network (whether or not it exists) will be redirected to port 111 on the firewall itself, where Portsentry is listening.

And:

/sbin/ipchains -A input -i $INET_IF -d 192.168.0.128/27 21 -j REDIRECT 12345

Thus, any traffic coming into the outer firewall that is destined for port 21 on any host in an unused address range on the boundary network will be redirected to port 12345 on the firewall itself, where Portsentry is listening.

The traffic destined for unused ranges on the boundary network must be redirected to some port on the firewall where portsentry is listening. This may not be the same port that the traffic was originally destined to, if there are legitimate servers for those ports elsewhere on the boundary network.

This is best done in a couple of loops near the top of your firewall script, after your private-IP and antispoofing rules:

# flytrap for port scanners and other d00ds - entire boundary network
for port in 98 111 161 162 512 513 514 515 1433 9704 12345 12346 16959 22222 27374 31337 32771 32772 32773 32774 33270 34555 35555 54320 54321
do
    /sbin/ipchains -A input  -j REDIRECT $port -l -i $INET_IF -p tcp -d $ANY $port
    /sbin/ipchains -A output -j DENY              -i $INET_IF -p tcp -s $ANY $port
    /sbin/ipchains -A input  -j REDIRECT $port -l -i $INET_IF -p udp -d $ANY $port
    /sbin/ipchains -A output -j DENY              -i $INET_IF -p udp -s $ANY $port
done

# trap scans on unused blocks of our Class-C
for port in 21 22 23 25 53 80 1080 1723 3128 3389 8080
do
    /sbin/ipchains -A input  -j REDIRECT 12345  -l -i $INET_IP -p tcp -d 192.168.0.128/27 $port
    /sbin/ipchains -A output -j DENY               -i $INET_IP -p tcp -s 192.168.0.128/27 $port
    /sbin/ipchains -A input  -j REDIRECT 12345  -l -i $INET_IP -p tcp -d 192.168.0.192/27 $port
    /sbin/ipchains -A output -j DENY               -i $INET_IP -p tcp -s 192.168.0.192/27 $port
done

These scripts are, of course, subject to more refinement based on your needs and are only presented as an illustration.

Portsentry's reaction to a scan is configurable. For our purposes, we'll block the scanning host by running the following script.

/etc/portsentry/portsentry.blacklist:

ATTACKER=$1
# record the attacker's IP address
echo "${ATTACKER}/32" >> /etc/IP-BlackList
# to manage load, don't log non-SYN traffic
/sbin/ipchains -A i-i-blk -j DENY   -p tcp ! -y -s $ATTACKER
/sbin/ipchains -A i-i-blk -j DENY               -s $ATTACKER -l
# let tarpit ACKs back out, block the rest
/sbin/ipchains -A o-i-blk -j ACCEPT -p tcp      -d $ATTACKER
/sbin/ipchains -A o-i-blk -j DENY               -d $ATTACKER

Tell Portsentry to run this script in response to a scan by editing /etc/portsentry/portsentry.conf and setting the following parameter:

KILL_ROUTE="/etc/portsentry/portsentry.blacklist $TARGET$"

4.2 The i-i-blk and o-i-blk firewall chains

The i-i-blk (input-internet-block) and o-i-blk (output-internet-block) firewall chains used by Portsentry are built near the top of the main firewall script; we re-read /etc/IP-Blacklist so that a firewall restart won't suddenly let a scanner or worm back into the boundary network:

# The blacklist
if [ -s /etc/IP-BlackList ]
then
    /sbin/ipchains -N i-i-blk
    /sbin/ipchains -A input  -i $INET_IF -j i-i-blk 
    /sbin/ipchains -N o-i-blk
    /sbin/ipchains -A output -i $INET_IF -j o-i-blk 
    (
    /usr/bin/perl -n -e 's/#.*//;
      if (
           /([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+\/[0-9]+)/
         ) {print "$1\n";}' /etc/IP-BlackList |\
    uniq | tail -200 | sort | uniq | \
    while read IP_TO_BLOCK
    do
        # to manage load, don't log non-SYN traffic
        /sbin/ipchains -A i-i-blk -j DENY   -p tcp ! -y -s $IP_TO_BLOCK
        /sbin/ipchains -A i-i-blk -j DENY               -s $IP_TO_BLOCK -l
        # let tarpit ACKs back out, block the rest
        /sbin/ipchains -A o-i-blk -j ACCEPT -p tcp      -d $IP_TO_BLOCK
        /sbin/ipchains -A o-i-blk -j DENY               -d $IP_TO_BLOCK
    done 
    ) &
fi
# The flytrap code goes AFTER this point

This script only re-blocks up to 200 of the most-recently-seen scanning hosts. This is done to limit system load and to allow for scanners from dynamic IP addresses: we don't want to block or tarpit a dialup or DHCP IP address permanently. Also, even if the scanning traffic is coming from a static IP address, a compromised host being used for scanning may be resecured or the scanner may eventually be booted from their ISP if they are scanning from their own computer.

Note that the i-i-blk and o-i-blk chains must be built in your firewall script BEFORE the rules that redirect traffic to Portsentry, or the firewall will never actually block the attacking IP address.

The first part of our scan-trap is done. We can now detect scans on our boundary network and block the scanner in real time.


Next Previous Contents