Using iptables to block brute force SSH scanning attacks

We saw everyday (and often multiple times a day) traces of brute force attacks on our server: many failed attempts to log into our server with SSH using the same or different usernames. Very often the username used were parts of domains hosted on our server. Only once did it succeed (on an old server we were about to get rid of and only had a few private domains) but even when they fail it’s just disturbing to have auth.log filled with thousands of message a day related to such attacks.

Of course in the beginning, we just started adding an iptables rule to drop packets from the IP address which kept coming up in auth.log:

# iptables -A INPUT -s -j DROP

It basically means  that all packets with the specified IP addressed will be dropped.

It is OK if it just happens once in a while but if have to do it for multiple IP addresses a day, it really calls for an automated solution. So we then introduced a script running as a cron job:




/sbin/iptables -n -L | /bin/grep DROP | awk '{ print $4; }' > $ALREADYDENIED

rm /tmp/hack.ip.*.log
grep -I "Invalid user " /var/log/auth.log* | awk '{ print $10; }' | sed '/^$/d' | awk '{ print $0 > "/tmp/hack.ip."$1".log" }'
wc -l /tmp/hack.ip.*.log | /bin/grep -v " total" | /bin/grep -v " insgesamt" | sed 's,/tmp/,,g' | sed 's/hack.ip.//g' | sed 's/.log//g' | awk '{ print $2" : "$1; }' | sort -k3 -n -r | /bin/grep " : [0-9][0-9]" | awk '{ print $1; }' > $IPLIST

while read line

if /bin/grep -Fxq "$line" $ALREADYDENIED
echo "Access already denied to IP address: $line"
echo "Denying access to IP address: $line" | tee -a $TEMPLOG
/sbin/iptables -A INPUT -s $line -j DROP | tee -a $TEMPLOG

done < $IPLIST
echo "-------------------------------------------------------------"

if [ -s $TEMPLOG ] ; then
$MAIL -s "Denied access to some IP addresses" $EMAIL < $TEMPLOG
rm -f $TEMPLOG

It basically goes through all auth.log* log files and looks for failed ssh login attempts and if an IP address is found at least 10 times (i.e. a two digits number of time), it will add an iptables rule. It also checks before that which DROP rules already exist so that we don’t add duplicate rules. In the end it sends me an email with the list of affected IP addresses.

This worked great. The only problem is that it adds quite a lot of rules to our firewall configuration. It has 2 main disadvantages:

  • At some point in time, the firewall will start needed much more time to start because of the thousands of rules.
  • When I list all rules, I get a huge list and need much more time to actually find what I need (and if I forget the -n argument and it start resolving IP addresses, it can last forever…).

So we decided to implement a few iptables rules to get rid of these guys temporarily and hope they’ll give up and it will save us a a permanent iptables rule. In order to do it , you need to be able to temporarily store who opened a connection to port 22 so that we can figure out whether it’s opening to many connections or not. This can be done using the iptables module “recent”. So for the following rules, you’ll need this module installed:

iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set
iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --update --seconds 60 --hitcount 5 -j DROP

The first rule writes with the –set option the IP address of a new connection on port 22 and a time stamp in the proc file /proc/net/ipt_recent/DEFAULT.
The second rule adds with –update  another timestamp to the file and checks whether there were 5 timestamps within the last 60 seconds. If it is the case, the connection is dropped. These rules only affect SSH access, so other services might still be vulnerable.

Note that on the iptables level, we do not know whether the login with ssh failed or not so you might also sometimes temporarily lock out a user who just happened to need 5 connections and opened them all within 1 minute. But then he just needs to wait for a minute and things are fine again.

Using the combination of both the script above and the iptables rules above proved to work quite fine.

Leave a Reply

Your email address will not be published. Required fields are marked *