Quick and Dirty SSH brute force block script

Since most OpenSSH packages don’t support TCP Wrappers anymore (OpenSuse since 13.2), I hereby present to you my Quick and Dirty SSH brute force block scripts. Put in crontab for great succes:


file: collect_via_lastb.sh

#!/bin/bash
if  ! -n $1 || $1 == "" ]]; then
        echo 'param1: your username'
        exit
fi
for a_ip in `lastb -n 5000 -a | grep -v $1| cut -b61-32768 | sort -u | grep -v "0.0.0.0" | grep -v "localhost.localdomain" | grep -v "192.168." | sed -e 's/^\.//g'`; do
        if  `cat /etc/hosts.deny | grep -i $a_ip | wc -l` -eq 0 ]; then
                echo "sshd: $a_ip" >> /etc/hosts.deny
        fi
done


file: block_via_iptables_and_whitelist.sh

#!/bin/bash
if  ! -n $1 || $1 == "" ]]; then
        echo 'param1: whitelist domain'
        exit
fi
for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $1 | sed -e 's/sshd://g' | sort -ru`; do
        if  `echo $a_ip | grep -oE '[0-9\.]*' | head -n1 | wc -m` -gt 6 && `iptables -nL | grep $a_ip | wc -l` -eq 0 ]]; then
                echo -n "add "; iptables -I INPUT 1 -s $a_ip -j DROP; else echo -n "skip "
        fi
        echo $a_ip
done
iptables -I INPUT 1 -s $1 -j ACCEPT

This is quick and the use of the DenyHosts tool also remains possible.
Ofcourse, there are better tools such as Fail2Ban, which is more time consuming.

This is not a request for help, but a howto.

This will be moved to Unreviewed How To and FAQ and is CLOSED for the moment.

Moved from Install/Boot/Login and open again.

Less quick and dirty approach: I combined the two scripts into one time-efficient and non-iptables-polluting script.

block_via_lastb_iptables_and_whitelist.sh
#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and iptables
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change user_name, whitelist_domain to an external IP or domain from which you log in
#    */5 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh user_name whitelist_domain > /dev/null 2> /dev/null &

if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 2 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: whitelist domain or address' ; exit; fi
function add_to_iptables {
        if  `iptables -nL INPUT | grep $1 | wc -l` -eq 0 ]; then
                echo -n "added"; iptables -I INPUT 2 -s $1 -j DROP; else echo -n "checked";
        fi;  echo " $1"
}
function add_all {
        if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then
                echo "sshd: $1" >> /etc/hosts.deny
        fi
        add_to_iptables $1
}
#check if the machine has uptime less than a day and whitelist_domain is not in iptables, then add all from hosts.deny
if  `uptime | grep -v days | wc -l` -gt 0 && `iptables -L INPUT 1 | grep $2 | wc -l` -lt 1 ]]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do
                add_to_iptables $a_ip
        done
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 5000 -a | awk '{ print $10 }' | uniq -c | sed -e 's/ /0/g' | sort -u |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.0.|^::1|^\.)'`; do
        if  `echo "$a_ip" | grep -oE '[g-zG-Z\-]' | wc -l` -lt 1 ]; then
                add_all $a_ip
        else
                add_all `host $a_ip | awk '{ print $4 }'`
        fi
done
if  `iptables -L INPUT 1 | grep $2 | wc -l` -lt 1 ]; then iptables -I INPUT 1 -s $2 -j ACCEPT; fi

A better approach would be using ipset, IMHO.

Update:


#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and iptables.
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change user_name, whitelist_domain to an external IP or domain from which you log in:
#    */60 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh your_username your_ip > /dev/null 2> /dev/null

#if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 3 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: whitelist IP-address' ; exit; fi
function add_to_iptables {
        if  `iptables -nL INPUT | grep $1 | wc -l` -eq 0 ]; then
                iptables -I INPUT 2 -s $1 -j DROP; echo -n "added"; else echo -n "checked";
        fi;  echo " $1"
}
function add_all {
        if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then echo "sshd: $1" >> /etc/hosts.deny; fi
        add_to_iptables $1
}
#check if the machine has uptime less than a day and whitelist_domain is not in iptables, then add all from hosts.deny
if  `uptime | grep -v days | wc -l` -gt 0 || `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do add_to_iptables $a_ip; done
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 5000 -a | awk '{ print $10 }' | sort | uniq -c | sed -e 's/ /0/g' | sort |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.|^::1|^\.)'`; do
                add_all $a_ip
done
if  `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]; then iptables -I INPUT 1 -s $2 -j ACCEPT; fi

Update: lookup ipaddress of domainnames reported by lastb


#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and iptables.
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change user_name, whitelist_domain to an external IP or domain from which you log in:
#    */60 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh your_user_name your_ip > /dev/null 2> /dev/null

if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 3 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: whitelist IP-address' ; exit; fi
function add_to_iptables_ip {
        if  `iptables -nL INPUT | grep $1 | wc -l` -eq 0 ]; then
                iptables -I INPUT 2 -s $1 -j DROP; echo -n "added"; else echo -n "checked";
        fi;  echo " $1"
}
function add_to_iptables {
        if  `echo $1 | egrep -v '[a-zA-Z]' | wc -l` -lt 1 ]; then
                add_to_iptables_ip `dig $1 | grep 'IN' | grep 'A' | tail -n 1 | rev | awk '{ print $1 }' | rev`
        else
                add_to_iptables_ip $1
        fi
}
function add_all {
        if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then echo "sshd: $1" >> /etc/hosts.deny; fi
        add_to_iptables $1
}
#check if the machine has uptime less than a day and whitelist_domain is not in iptables, then add all from hosts.deny
if  `uptime | grep -v days | wc -l` -gt 0 || `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do add_to_iptables $a_ip; done
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 5000 -a | awk '{ print $10 }' | sort | uniq -c | sed -e 's/ /0/g' | sort |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.|^::1|^\.)'`; do
                add_all $a_ip
done
if  `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]; then iptables -I INPUT 1 -s $2 -j ACCEPT; fi

update: lookup ipaddress only when not already in deny.hosts (because if so then it was already added to iptables by the script).


#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and iptables.
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change remote login user_name and server_ip:
#    */60 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh user_name server_ip > /dev/null 2> /dev/null

if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 3 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: your modem IP address' ; exit; fi
function add_to_iptables_ip {
        if  `iptables -nL INPUT | grep $1 | wc -l` -eq 0 ]; then
                iptables -I INPUT 2 -s $1 -j DROP; echo -n "added"; else echo -n "checked";
        fi;  echo " $1"
}
function add_to_iptables {
        if  `echo $1 | egrep -v '[a-zA-Z]' | wc -l` -lt 1 ]; then
                if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then
                        new_ip=`dig $1 | grep 'IN' | grep 'A' | tail -n 1 | rev | awk '{ print $1 }' | rev`
                        if  `cat /etc/hosts.deny | grep -i $new_ip | wc -l` -eq 0 ]; then echo "sshd: $new_ip" >> /etc/hosts.deny; fi
                        add_to_iptables_ip $new_ip
                else
                        echo checked $1
                fi
        else
                add_to_iptables_ip $1
        fi
}
function add_all {
        add_to_iptables $1; if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then echo "sshd: $1" >> /etc/hosts.deny; fi
}
#check if the machine has uptime less than a day and whitelist_domain is not in iptables, then add all from hosts.deny
if  `uptime | grep -v days | wc -l` -gt 0 || `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do
                if  `echo $1 | egrep -v '[a-zA-Z]' | wc -l` -gt 0 ]; then add_to_iptables_ip $a_ip; fi
        done
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 5000 -a | awk '{ print $10 }' | sort | uniq -c | sed -e 's/ /0/g' | sort |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.|^::1|^\.)'`; do
                add_all $a_ip
done
if  `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]; then iptables -I INPUT 1 -s $2 -j ACCEPT; fi

Update:


#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and iptables.
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change remote login user_name and server_ip:
#    */60 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh user_name server_ip > /dev/null 2> /dev/null

if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 2 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: your modem IP address' ; exit; fi
function add_to_iptables_ip {
        if  `iptables -nL INPUT | grep $1 | wc -l` -eq 0 ]; then
                iptables -I INPUT 2 -s $1 -j DROP; echo -n "added"; else echo -n "checked";
        fi;  echo " $1"
}
function add_to_iptables {
        if  `echo $1 | egrep -v '[a-zA-Z]' | wc -l` -lt 1 ]; then
                if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then
                        new_ip=`dig $1 | grep 'IN' | grep 'A' | tail -n 1 | rev | awk '{ print $1 }' | rev`
                        if  -n $new_ip && `cat /etc/hosts.deny | grep -i $new_ip | wc -l` -eq 0 ]]; then echo "sshd: $new_ip" >> /etc/hosts.deny; fi
                        add_to_iptables_ip $new_ip
                else
                        echo checked $1
                fi
        else
                add_to_iptables_ip $1
        fi
}
function add_all {
        add_to_iptables $1; if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then echo "sshd: $1" >> /etc/hosts.deny; fi
}
#check if the machine has uptime less than a day and whitelist_domain is not in iptables, then add all from hosts.deny
if  `uptime | grep -v days | wc -l` -gt 0 || `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do
                if  `echo $a_ip | egrep -v '[a-zA-Z]' | wc -l` -gt 0 ]; then add_to_iptables_ip $a_ip; fi
        done
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 5000 -a | awk '{ print $10 }' | sort | uniq -c | sed -e 's/ /0/g' | sort |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.|^::1|^\.)'`; do
                add_all $a_ip
done
if  `iptables -nL INPUT 1 | grep $2 | wc -l` -lt 1 ]; then iptables -I INPUT 1 -s $2 -j ACCEPT; fi

Update: Also block outgoing traffic to prevent blocklisting of your server (because of spoofing attacks by others). Better rule order.


#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and iptables.
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change remote login user_name and server_ip:
#    */12 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh user_name server_ip > /dev/null 2> /dev/null

if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 3 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: your modem IP address' ; exit; fi
function add_to_iptables_ip_nocheck {
        iptables -I INPUT 1 -s $1 -j DROP; iptables -I OUTPUT 1 -s $1 -j DROP; echo added $1
}
function add_to_iptables_ip {
        if  `iptables -nL INPUT | grep $1 | wc -l` -eq 0 ]; then add_to_iptables_ip_nocheck $1; else echo checked $1; fi
}
function add_to_iptables {
        if  `echo $1 | egrep -v '[a-zA-Z]' | wc -l` -lt 1 ]; then
                if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then
                        new_ip=`dig $1 | grep 'IN' | grep 'A' | tail -n 1 | rev | awk '{ print $1 }' | rev`
                        if  -n $new_ip && `cat /etc/hosts.deny | grep -i $new_ip | wc -l` -eq 0 ]]; then echo "sshd: $new_ip" >> /etc/hosts.deny; fi
                        add_to_iptables_ip $new_ip
                else
                        echo checked $1
                fi
        else
                add_to_iptables_ip $1
        fi
}
function add_all {
        add_to_iptables $1; if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then echo "sshd: $1" >> /etc/hosts.deny; fi
}
#if server_ip is not at end of iptables, then add all from hosts.deny
if  `iptables -nL INPUT | tail -n1 | grep $2 | wc -l` -lt 1 ]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do
                if  `echo $a_ip | egrep -v '[a-zA-Z]' | wc -l` -gt 0 ]; then add_to_iptables_ip_nocheck $a_ip; fi
        done; iptables -A INPUT -s $2 -j ACCEPT
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 16384 -a | awk '{ print $10 }' | sort | uniq -c | sed -e 's/ /0/g' | sort |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.|^::1|^\.)'`; do
                add_all $a_ip
done

Update: on freshly installed systems, ‘/usr/sbin/’ is not in the Path.


#!/bin/bash
#    A simple script to block random SSH brute force login attempts, using lastb, hosts.deny and $IPTABLES.
#    Copy to /usr/local/sbin; chmod u+x /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh
#    Add to crontab with 'crontab -e',  change remote login user_name and server_ip:
#    */12 * * * * /usr/local/sbin/block_via_lastb_iptables_and_whitelist.sh user_name server_ip > /dev/null 2> /dev/null

IPTABLES='/usr/sbin/iptables'
if  `ps -elf | grep $0 | grep -v grep | wc -l` -gt 2 ]; then echo 'Already running.'; exit; fi
if  ! -n $1 || ! -n $2 ]]; then echo 'param1: your username'; echo 'param2: your modem IP address' ; exit; fi
function add_to_iptables_ip_nocheck {
        $IPTABLES -I INPUT 1 -s $1 -j DROP; $IPTABLES -I OUTPUT 1 -s $1 -j DROP; echo added $1
}
function add_to_iptables_ip {
        if  `$IPTABLES -nL INPUT | grep $1 | wc -l` -eq 0 ]; then add_to_iptables_ip_nocheck $1; else echo checked $1; fi
}
function add_to_iptables {
        if  `echo $1 | egrep -v '[a-zA-Z]' | wc -l` -lt 1 ]; then
                if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then
                        new_ip=`dig $1 | grep 'IN' | grep 'A' | tail -n 1 | rev | awk '{ print $1 }' | rev`
                        if  -n $new_ip && `cat /etc/hosts.deny | grep -i $new_ip | wc -l` -eq 0 ]]; then echo "sshd: $new_ip" >> /etc/hosts.deny; fi
                        add_to_iptables_ip $new_ip
                else
                        echo checked $1
                fi
        else
                add_to_iptables_ip $1
        fi
}
function add_all {
        add_to_iptables $1; if  `cat /etc/hosts.deny | grep -i $1 | wc -l` -eq 0 ]; then echo "sshd: $1" >> /etc/hosts.deny; fi
}
#if server_ip is not at end of $IPTABLES, then add all from hosts.deny
if  `$IPTABLES -nL INPUT | tail -n1 | grep $2 | wc -l` -lt 1 ]; then
        for a_ip in `cat /etc/hosts.deny | grep 'sshd:' | grep -iv $2 | sed -e 's/sshd://g'`; do
                if  `echo $a_ip | egrep -v '[a-zA-Z]' | wc -l` -gt 0 ]; then add_to_iptables_ip_nocheck $a_ip; fi
        done; $IPTABLES -A INPUT -s $2 -j ACCEPT
fi
#only block recent hosts with more than 9 login attempts
for a_ip in `lastb -n 16384 -a | awk '{ print $10 }' | sort | uniq -c | sed -e 's/ /0/g' | sort |  grep -v '000000' | cut -b9-54 | \
        egrep -v '(localhost.localdomain|127.0.0.1|^192.168.|^10.|^::1|^\.)'`; do
                add_all $a_ip
done

On 2015-08-28 21:16, 53K53ztZ6DS5L7n wrote:
>
> Update: on freshly installed systems, ‘/usr/sbin/’ is not in the Path.

Of course it is. It is in Mr Root’s path, not on others. No business of
them.


Cheers / Saludos,

Carlos E. R.

(from 13.1 x86_64 “Bottle” (Minas Tirith))

Hi,

I kinda like that idea of automating such task thanks for that. Now i have some suggestions about the script
The most important thing is to quote the variables. Now to list some part than can be improve IMHO.

  1. The >/dev/null 2 >/dev/null
    Although it is fine to just discard the errors or outpu (if any) in a script but IMHO it is wise to redirect that error/output into some log file. It makes it easier to debug if one can see the errors why it is failing. Also if you just want to discard the output then you can just use >/dev/null 2>&1

  2. The ps | grep
    In openSUSE there is a utility called pgrep and it also has a -c option to count how many process is running.
    A simple example is this


pgrep bash
 

pgrep -c bash

OTOH one can just use the flock utility to make sure that only one instance of a program/script is running at any given time.

  1. The cat | grep | wc
    grep can do just that without the cat and wc and here is an example.
grep -ic bash /etc/passwd
  1. The for ap_in cat
    Since you are parsing a file then the while read loop is a better choice instead of a for loop. A simple example.
while read -r file; do echo "$file"; done < /etc/passwd 

It is also an alternative to the second for loop which is parsing the output of the last command.
That being said your script will do just fine (at least it looks like it). All I have is suggestions so don’t get the wrong idea :wink:

Of course it is. It is in Mr Root’s path, not on others. No business of
them.

It is absent when running from cron on 13.2.

Hi,

Crons path is very limited among other things.

In an interactive shell the value of

echo "$PATH"

should show you it’s value.

Now to test via cron redirect the output in a log file. Create an entry that is something like this.

* * * * * echo "$PATH" > /tmp/mycronpath.log 2>&1 

The path and file name is arbitrary you can name place it anywhere you want as long as you have write access to it.
Just reading that log file should give you the answer you are looking for ;). Of course one can always define the value of PATH via the script itself or via the cron file entry. Good luck…

In crontabs and in scripts starrted by them, it is much better to use full pathes for every file (including commands) used (or use cd with the full path to make sure you know 100% what your working directory is).

On 2015-08-30 16:46, 53K53ztZ6DS5L7n wrote:
>
>>
>> Of course it is. It is in Mr Root’s path, not on others. No business of
>> them.
>>
> It is absent when running from cron on 13.2.

Well, that’s the reason that you have to specify the PATH at the start
of a crontab file.


Cheers / Saludos,

Carlos E. R.

(from 13.1 x86_64 “Bottle” (Minas Tirith))