Defending your linux box from brute force ssh attacks using TCP_Wrappers

Asteris

Overview:

SSHblock is intended to dynamically and automatically stop SSH-based dictionary attacks. Even if you have secure passwords and are sure no attacker will be able to get in, it can still be annoying to have your logs littered with “failed password” messages. SSHblock automatically blocks any IP address that fails an SSH login too many times too quickly, and automatically unblocks it after a while. It is possible to let the tcp wrapper start a script whenever a connection is made, and let this script add rules to /etc/hosts.allow, if the connecting host should be blocked.

Step 1: Create a file called ‘sshblock.sh’ using your favorite editor in my case I usually use ‘VIM’ to do my job and then copy the code below..

#! /bin/sh
#
# ----------------------------------------------------------------------------
# "THE CAPPUCHINO-WARE LICENSE"
# Rainer Wichmann <[email protected]> wrote this file. As long as you
# retain this notice you can do whatever you want with this stuff. If we
# meet some day, and you think this stuff is worth it, you can buy me a
# cappuchino in return. Rainer Wichmann @ Oct 30, 2005
# ----------------------------------------------------------------------------
#
#      Add the following three lines to the bottom of /etc/hosts.allow:
#
#      #__START_SSHBLOCK__
#      #__END_SSHBLOCK__
#      sshd : ALL : spawn (/usr/local/bin/sshblock.sh %a)&
#
#
############################################
#
# Configurable parameters. NO SPACE before
#   or after the '='.
#
############################################

# your own domain
DONTBLOCK=192.168.1
DONTBLOCK=10.20.11

# block host if more than BURST_MAX connections within BURST_TIM seconds
BURST_MAX=5
BURST_TIM=60

# remove block after PURGE_TIM seconds
#PURGE_TIM=3600
PURGE_TIM=21600

# the temporary file: do not use a world writeable directory
tmpfile=/root/hosts.allow

############################################
#
# Nothing to change below
#
############################################

PATH="/bin:/usr/bin:/sbin:/usr/sbin"; export PATH

# the only argument is the remote IP address
#
if [ -z "$1" ]; then
    logger -p auth.err "sshblock: called without argument"
    exit 1
fi

#
# date must support the %s format (seconds after the epoch)
#
now=`date +%s`
if [ $? -ne 0 ]; then
    logger -p auth.err "sshblock: the date command exited on error"
    exit 1
fi

debug=1
host=`echo "$1" | sed s%.*:%%`
host_pending=0
LOCKDIR="${tmpfile}.lock"

cleanup()
{
  logger -p auth.err "sshblock: caught signal ... cleaning up."
  /bin/rm -f "${tmpfile}"
  /bin/rmdir "${LOCKDIR}"
}

trap '/bin/rm -f "${tmpfile}"; /bin/rmdir "${LOCKDIR}"'  0
trap "cleanup; exit 2" 1 2 3 15
#
# A lockfile will not work, because 'root' can write anyway.
# However, 'mkdir' an existing directory will fail even for root
#
until (umask 222; mkdir $LOCKDIR) 2>/dev/null   # test & set
do
   set x `ls -ld "${LOCKDIR}"`
   logger -p auth.err "sshblock: waiting for user $4 (working since $7 $8)"
   sleep 1
done

 





rm -f "$tmpfile"; touch "$tmpfile"

grep "$host" /etc/hosts.allow | grep 'DENY' | egrep '^sshd' >/dev/null 2>&1
if [ $? -eq 0 ]; then
    logger -p auth.err "sshblock: connection by blocked host ${host}"
fi

while read -r line
do
  case "$line" in
      *TIMESTAMP*)
          read follow;
          case "$follow" in
              sshd*)
                  date1=`echo $line | cut -d ' ' -f 2`
                  ddiff=$(($now - $date1))
                  if [ $ddiff -lt ${PURGE_TIM} ]; then
                      echo "$line" >>"$tmpfile"
                      echo "$follow" >>"$tmpfile"
                  else
                      purge=`echo $follow | awk '{ print $3 }'`
                      logger -p auth.info "sshblock: purging host $purge"
                      test -z "$debug" || echo "purging host $purge"
                  fi
                  ;;
              *)
                  echo "$line" >>"$tmpfile"
                  echo "$follow" >>"$tmpfile"
                  logger -p auth.err "sshblock: /etc/hosts.allow corrupt"
                  test -z "$debug" || echo "/etc/hosts.allow corrupt"
                  test -z "$debug" || echo "follow: $follow"
                  ;;
          esac
          ;;
      *PENDING*)
          date=`echo $line | cut -d ' ' -f 2`
          freq=`echo $line | cut -d ' ' -f 3`
          pend=`echo $line | cut -d ' ' -f 4`
          if [ x"$host" = x"$pend" ]; then
              host_pending=1
          fi
          ddiff=$(( $now - $date ))
          if [ $ddiff -lt ${BURST_TIM} -a x"$host" = x"$pend" ]; then
              if [ $freq -ge ${BURST_MAX} ]; then
                  echo "#TIMESTAMP $now" >>"$tmpfile"
                  echo "sshd : ${host} : DENY" >>"$tmpfile"
                  logger -p auth.info "sshblock: blocking $host"
                  test -z "$debug" || echo "blocking $host"
              else
                  freq=$(( $freq + 1 ))
                  echo "#PENDING $date $freq $pend" >>"$tmpfile"
              fi
          elif [ $ddiff -gt ${BURST_TIM} ]; then
              test -z "$debug" || echo "remove $host from pending"
          else
              echo "$line" >>"$tmpfile"
          fi
          ;;
      *END_SSHBLOCK*)
          if [ ${host_pending} -eq 0 ]; then
              case "$host" in
                  "$DONTBLOCK"*)
                      echo "$line" >>"$tmpfile"
                      test -z "$debug" || echo "$host not added to pending"
                      ;;
                  127.0.0*)
                      echo "$line" >>"$tmpfile"
                      test -z "$debug" || echo "$host not added to pending"
                      ;;
                  *)
                      echo "#PENDING $now 1 $host" >>"$tmpfile"
                      echo "$line" >>"$tmpfile"
                      ;;
              esac
          else
              echo "$line" >>"$tmpfile"
          fi
          ;;
      *)
          echo "$line" >>"$tmpfile"
          ;;
  esac
done < /etc/hosts.allow if [ $? -eq 0 ]; then diff "$tmpfile" /etc/hosts.allow >/dev/null 2>&1
    if [ $? -ne 0 ]; then
        cat "$tmpfile" >/etc/hosts.allow
    fi
fi

Then after that save and close the file that was created by pressing ESC :wq from your keyboard

or if you want to download you can get the zip file below

File: sshblock.zip (1.81 KB)
MD5 checksum: 535e2d9e819281d9fc8de06621bbc3f7
SHA-1 checksum: 2dca5029bab8ec7358578eea12b31d9ffdce6c72

Step 2: Change the permission and owner of the file that you created and move it to ‘/usr/local/bin’ folder from your terminal console type the following

chown root:root /usr/local/bin/sshblock.sh
chmod 755 /usr/local/bin/sshblock.sh

Step 3: Append the text with bold text to your hosts.allow file

 #__START_SSHBLOCK__
 #__END_SSHBLOCK__
 sshd : ALL : spawn (/usr/local/bin/sshblock.sh %a)&

And then after that it will be looking like this below

[[email protected] log]# vim /etc/hosts.allow
 #
 # hosts.allow This file contains access rules which are used to
 # allow or deny connections to network services that
 # either use the tcp_wrappers library or that have been
 # started through a tcp_wrappers-enabled xinetd.
 #
 # See 'man 5 hosts_options' and 'man 5 hosts_access'
 # for information on rule syntax.
 # See 'man tcpd' for information on tcp_wrappers
 #
 #__START_SSHBLOCK__
 #__END_SSHBLOCK__
 sshd : ALL : spawn (/usr/local/bin/sshblock.sh %a)&

Step 4: Start to test if its working by doing SSH 2 or 3 times in your server that you installed tcp_wrapper. After your done with the testing you can go back and check the file if you catch something. do a 'cat /etc/hosts.allow' in your file and you should have the same result just like below.

[[email protected] log]# cat /etc/hosts.allow
 #__START_SSHBLOCK__
 #PENDING 1457075328 1 192.168.0.36
 #__END_SSHBLOCK__

Notice there is new entry #PENDING 1457075328 1 192.168.0.36 if you keep retrying to SSH it will change to #BLOCK.

1 Comment on "Defending your linux box from brute force ssh attacks using TCP_Wrappers"

  1. Awesome

Leave a comment

Your email address will not be published.