14.2.23. Using branch rule set with external script that adds rules "on the fly" to prevent ssh scanning attacks

Branch rule sets created in the Firewall Builder GUI get translated into user-defined chains (iptables) or anchors (pf) in the generated configuration. It is not required however that you put any rules in this branch rule set. If it is left empty, it won't make packet checks and return back to the top level rule that called it right away. Such an empty rule set can be very useful if you populate it with rules using some external script after firewall policy has been loaded. In the following example I use this idea to add firewall policy rules dynamically to block SSH scanners. The goal is to build policy rules to do the following:

  1. Always permit SSH from the internal network to the firewall. Our algorithm for identification of SSH scanners is based on the log records of failed login attempts, so it is important to have a rule to permit SSH from inside. Without this rule, if the administrator made a typo entering the password, this could trigger the next rule for the source address they tried to connect from and block them.

  2. If the source IP address of the SSH client that tries to connect was identified as an SSH scanner, block connection

  3. Permit all other SSH connections from all sources.

This policy is rather permissive but it can easily be modified to suite more strict security requirements.

I start with an existing firewall policy. The rules I am going to add to block SSH scans do not depend on other rules in the policy. First, I create a new policy rule set with name "block_ssh". This rule set is not the "top rule set", so generated iptables rules will be placed in the chain "block_ssh". I do not add any rules here. Rules will be added to this chain by an external script.

Figure 14.72. Creating a "block_ssh" Rule Set

Creating a "block_ssh" Rule Set

Create rule #0 in the main policy to permit SSH to the firewall from internal network, then another one where the destination the firewall itself, the service is "ssh", the direction "Inbound" and action is "Branch". Open the action in the editor by double-clicking it, then drag the object representing rule set "block_ssh" into the well in the action editor panel. The idea is to first permit SSH to the firewall from the internal net (rule #0), but for attempts to connect to the firewall on the SSH port from other sources pass control to chain "block_ssh". If that chain does not block the SSH session, the next rule #2 permits it.

Figure 14.73. Setting the "Chain" Action

Setting the "Chain" Action

Here is what the iptables commands generated for rules 0-2 look like. Note that although the script creates the chain "block_ssh", it does not put any rules in it.

# ================ Table 'filter', rule set Policy
# Policy compiler errors and warnings:
# Rule 0 (global)
$IPTABLES -A INPUT -p tcp -m tcp  -s \
      --dport 22  -m state --state NEW  -j ACCEPT 
# Rule 1 (global)
$IPTABLES -N block_ssh
$IPTABLES -A INPUT -p tcp -m tcp  --dport 22  -j block_ssh 
# Rule 2 (global)
$IPTABLES -A INPUT -p tcp -m tcp  --dport 22  -m state --state NEW  -j ACCEPT 

I am using swatch to watch the log and add iptables rules with addresses of scanners to the chain "block_ssh". The screen shot below shows the contents of the swatch configuration file /root/.swatchrc. This configuration makes swatch detect log lines added by SSH when an attempt is made to log in using an invalid user account or invalid password. Swatch then runs script /root/swatch/block_ssh_scanner.sh.

# cat /root/.swatchrc

watchfor /sshd\[\d+\]: Failed password for invalid user (\S+) from (\S+)/
echo bold
exec "/root/swatch/block_ssh_scanner.sh $2"

watchfor /sshd\[\d+\]: Failed password for (\S+) from (\S+)/
echo bold
exec "/root/swatch/block_ssh_scanner.sh $2"

watchfor /sshd\[\d+\]: Did not receive identification string from (\S+)/
echo bold
exec "/root/swatch/block_ssh_scanner.sh $1"

watchfor /sshd\[\d+\]: Invalid user (\S+) from (\S+)/
echo bold
exec "/root/swatch/block_ssh_scanner.sh $2"

The following script adds an iptables rule to chain "block_ssh" and also adds the address of the scanner to the file /root/swatch/ssh_scan_addresses to avoid duplications in the future.

# cat /root/swatch/block_ssh_scanner.sh

test -z "$addr" && exit 1
grep $addr /root/swatch/ssh_scan_addresses && exit 0

cmd="iptables -A block_ssh -s $addr -j DROP"
echo "$cmd" >> /root/swatch/ssh_scan_addresses

Here is the command line you can use to start the swatch daemon. Add this command to the /etc/rc.d/rc.local script to start it when you reboot your machine.

/usr/bin/swatch --daemon --tail-file=/var/log/secure --use-cpan-file-tail </dev/null &

This method of blocking SSH scan attacks is effective but might be too "sharp". It will block access from legitimate machines outside your network as soon as you mistype your password even once. This can be dangerous because you'll block yourself until you either restart the firewall or remove the blocked address from iptables rules in chain "block_ssh". SSH access to the firewall from the internal network is always permitted because of the rule #0, so this setup will not cut you off the firewall completely. Using SSH keys for authentication instead of the password when you log in from outside is a good way to avoid this problem.


This example was intended to demonstrate how a branch rule set can be used in combination with external script that populates rule set. There are better ways to block SSH scanners, for example using the iptables module "recent" which solves a problem of blocking legitimate client addresses after a user mistypes the password. Module "recent" can block an address for a limited period of time, which should be enough for the SSH scanner to time out and go away, yet the user who mistyped their password will be able to log in again some time later. The shell script that adds iptables commands to the chain "block_ssh" or addresses to the module recent table can also be improved to only add them after they appear in the SSH log a few times to avoid blocking client addresses after single error entering password.


Copyright © 2000-2012 NetCitadel, Inc. All rights reserved.
 Using free CSS Templates.