Preventing Brute Force Attacks With BlockHosts On Debian Lenny

Want to support HowtoForge? Become a subscriber!
 
Submitted by falko (Contact Author) (Forums) on Wed, 2010-11-10 16:23. :: Debian | Security

Preventing Brute Force Attacks With BlockHosts On Debian Lenny

Version 1.0
Author: Falko Timme <ft [at] falkotimme [dot] com>
Follow me on Twitter
Last edited 08/18/2010

In this article I will show how to install and configure BlockHosts on a Debian Lenny system. BlockHosts is a Python tool that observes login attempts to various services, e.g. SSH, FTP, etc., and if it finds failed login attempts again and again from the same IP address or host, it stops further login attempts from that IP address/host. By default, BlockHosts supports services that use TCP_WRAPPERS, such as SSH, i.e. services, that use /etc/hosts.allow or /etc/hosts.deny, but it can also block other services using iproute or iptables.

This document comes without warranty of any kind! I do not issue any guarantee that this will work for you!

 

1 Preliminary Note

I have tested BlockHosts on a Debian Lenny system.

I will show you how to to use it with a service that uses /etc/hosts.allow or /etc/hosts.deny (sshd) and with a service that doesn't use TCP_WRAPPERS, e.g. Debian's ProFTPd package. Services that don't use /etc/hosts.allow or /etc/hosts.deny can be blocked by iproute or iptables.

I assume that OpenSSH and ProFTPd are both properly installed and working on your system.

 

2 Installing BlockHosts

As BlockHosts is written in Python, we must install Python now:

aptitude install python

Afterwards, we download and install BlockHosts like this:

cd /tmp
wget http://www.aczoom.com/tools/blockhosts/BlockHosts-2.5.0.tar.gz
tar xvfz BlockHosts-2.5.0.tar.gz
cd BlockHosts-2.5.0

python setup.py install --force

Now we have to edit /etc/blockhosts.cfg. Modify it as follows:

vi /etc/blockhosts.cfg

[...]
HOSTS_BLOCKFILE = "/etc/hosts.allow"
[...]
HOST_BLOCKLINE = ["ALL: ", " : deny"]
[...]
COUNT_THRESHOLD = 3
[...]
AGE_THRESHOLD = 12
[...]
LOGFILES = [ "/var/log/auth.log", "/var/log/proftpd/proftpd.log", ]
[...]
MAIL = True
[...]
NOTIFY_ADDRESS = 'root@localhost.localdomain'
[...]
SMTP_SERVER = "localhost"
SENDER_ADDRESS = 'BlockHosts <blockhosts-do-not-reply@localhost.localdomain>' [...] IPBLOCK = "iptables" [...]

In HOSTS_BLOCKFILE we can specify either /etc/hosts.allow or /etc/hosts.deny. It doesn't matter which one we choose. I'm using /etc/hosts.allow here. In the LOGFILES line we specify the log files that BlockHosts should look at. OpenSSH is logging failed login attempts to /var/log/auth.log, ProFTPd to /var/log/proftpd/proftpd.log. COUNT_THRESHOLD specifies the number of failed login attempts from the same host after which BlockHosts should block that host. AGE_THRESHOLD is the numer of hours after which blocked hosts get unblocked. IPBLOCK specifies if you'd like to block hosts with iptables or iproute in addition to adding these hosts to /etc/hosts.allow (or /etc/hosts.deny).

Next we must modify /etc/hosts.allow. First back up your current /etc/hosts.allow:

cp /etc/hosts.allow /etc/hosts.allow_orig

Then empty /etc/hosts.allow and put something like this into it:

cat /dev/null > /etc/hosts.allow
vi /etc/hosts.allow

#
# hosts.allow    This file describes the names of the hosts which are
#        allowed to use the local INET services, as decided
#        by the '/usr/sbin/tcpd' server.
#
# ----
# see "man 5 hosts_access" for details of the format of IP addresses,
#services, allow/deny options. Also see "man hosts_options"
#
# permanent whitelist addresses - this should always be allowed access
ALL: 127.0.0.1 : allow
# ALL: 192.168.0. : allow
# permanent blacklist addresses - this should always be denied access
# ALL: 10. : deny
# ----------------------------------------
# next section is the blockhosts section - it will add/delete entries in
# between the two marker lines (#---- BlockHosts Additions)
#---- BlockHosts Additions
#---- BlockHosts Additions
# ----------------------------------------
# finally, the command to execute the blockhosts script, based on
# connection to particular service or services:
sshd: ALL: spawn /usr/bin/blockhosts.py --verbose --mail --iptables \
     --echo "%c-%s" --check-ip "%h" >> /var/log/blockhosts.log 2>&1 & \
: allow
#---
# add --iproute to enable null-routing, or add --iptables to enable packet
# filtering, which blocks all network communication from blocked hosts
#---
# remove   >> /var/log/blockhosts.log 2>&1     if no logging to blockhosts.log
# is needed - without this, it will still log to syslog (minimally)
#sshd: ALL: spawn /usr/bin/blockhosts.py --verbose --echo "%c-%s" & : allow
#---
# above commands will use default config file - /etc/blockhosts.cfg, edit
# it as needed to specify local configuration options
# See "man hosts.allow" for info on %c and %s identifiers
# for non-verbose, with identification, to syslog only (/var/log/messages),
# triggered on any service (using ALL as first word):
#ALL: ALL: spawn /usr/bin/blockhosts.py --echo "%c-%s" & : allow
#----
# To test hosts.allow, and to find out exact names of SSH/FTP services,
# add this line to the beginning of hosts.allow, use ssh/ftp to connect
# to your server, and then look at the log (/var/log/messages or
# blockhosts.log) to see the name of the invoked service.
# IMPORTANT: after your test is done, remove this line from hosts.allow!
# Otherwise everyone will always have access.
#ALL : ALL: spawn (/usr/bin/blockhosts.py --verbose --echo "%c-%s" >> /var/log/blockhosts.log 2>&1 )& : allow
# -------------------------------------------------------------------------

In the first section you put hosts that you want to whitelist (e.g. 127.0.0.1). If you'd like to whitelist the whole 192.168.0 subnet, uncomment that line.

Then we must add these markers - BlockHosts will add blocked hosts between them:

#---- BlockHosts Additions
#---- BlockHosts Additions

The most important part is this one:

sshd: ALL: spawn /usr/bin/blockhosts.py --verbose --mail --iptables \
     --echo "%c-%s" --check-ip "%h" >> /var/log/blockhosts.log 2>&1 & \
: allow

Now whenever someone tries to log in using SSH, /usr/bin/blockhosts.py is started, checks the log files that we specified in /etc/blockhosts.cfg, and blocks all hosts with more than COUNT_THRESHOLD failed login attempts by adding them to /etc/hosts.allow and by using iptables (which will totally block these hosts from accessing your system). All actions will be logged to /var/log/blockhosts.log.

Now we need to initialize BlockHosts. First, we do this with the --dry-run option to see if there are no errors:

blockhosts.py --dry-run --verbose

The output could look like this:

server1:/tmp/BlockHosts-2.5.0# blockhosts.py --dry-run --verbose
blockhosts 2.5.0 started: 2010-08-18 14:16:56 CEST
 ... loaded /etc/hosts.allow, starting counts: blocked 0, watched 0
no logoffsets found, will read from beginning in logfile: /var/log/auth.log
 ... loading log file /var/log/auth.log, offset: 0
no logoffsets found, will read from beginning in logfile: /var/log/proftpd/proftpd.log
 ... loading log file /var/log/proftpd/proftpd.log, offset: 0
 ... discarding all host entries older than  2010-08-18 02:16:56 CEST
 ... final counts: blocked 0, watched 1
#---- BlockHosts Additions
#bh: ip:     192.168.0.2 :   1 : 2010-08-18 14:16:56 CEST

#bh: logfile: /var/log/auth.log
#bh: offset: 6763
#bh: first line:Feb 16 13:22:10 server1 login[1992]: pam_unix(login:session): session opened for user root by (uid=0)

#bh: logfile: /var/log/proftpd/proftpd.log
#bh: offset: 884
#bh: first line:Feb 16 14:59:18 server1.example.com proftpd[13157] server1.example.com:
 ProFTPD 1.3.1 (stable) (built Fri Feb 6 12:26:25 GMT 2009) standalone mode STARTUP

#---- BlockHosts Additions

# ----------------------------------------
# finally, the command to execute the blockhosts script, based on
# connection to particular service or services:

sshd: ALL: spawn /usr/bin/blockhosts.py --verbose --mail --iptables \
     --echo "%c-%s" --check-ip "%h" >> /var/log/blockhosts.log 2>&1 & \
: allow

#---
# add --iproute to enable null-routing, or add --iptables to enable packet
# filtering, which blocks all network communication from blocked hosts
#---
# remove   >> /var/log/blockhosts.log 2>&1     if no logging to blockhosts.log
# is needed - without this, it will still log to syslog (minimally)
#sshd: ALL: spawn /usr/bin/blockhosts.py --verbose --echo "%c-%s" & : allow
#---
# above commands will use default config file - /etc/blockhosts.cfg, edit
# it as needed to specify local configuration options

# See "man hosts.allow" for info on %c and %s identifiers

# for non-verbose, with identification, to syslog only (/var/log/messages),
# triggered on any service (using ALL as first word):
#ALL: ALL: spawn /usr/bin/blockhosts.py --echo "%c-%s" & : allow
#----
# To test hosts.allow, and to find out exact names of SSH/FTP services,
# add this line to the beginning of hosts.allow, use ssh/ftp to connect
# to your server, and then look at the log (/var/log/messages or
# blockhosts.log) to see the name of the invoked service.
# IMPORTANT: after your test is done, remove this line from hosts.allow!
# Otherwise everyone will always have access.
#ALL : ALL: spawn (/usr/bin/blockhosts.py --verbose --echo "%c-%s" >> /var/log/blockhosts.log 2>&1 )& : allow

# -------------------------------------------------------------------------
Commands (tentative) to run for IPTables filtering:
 ... created user-defined chain blockhosts
 ... creating jump from INPUT to blockhosts chain
 ... creating jump from FORWARD to blockhosts chain
 ... no email to send.
server1:/tmp/BlockHosts-2.5.0#

Looks ok, so we can run it without the --dry-run option:

blockhosts.py --verbose

This should look like this:

server1:/tmp/BlockHosts-2.5.0# blockhosts.py --verbose
blockhosts 2.5.0 started: 2010-08-18 14:20:20 CEST
 ... loaded /etc/hosts.allow, starting counts: blocked 0, watched 0
no logoffsets found, will read from beginning in logfile: /var/log/auth.log
 ... loading log file /var/log/auth.log, offset: 0
no logoffsets found, will read from beginning in logfile: /var/log/proftpd/proftpd.log
 ... loading log file /var/log/proftpd/proftpd.log, offset: 0
 ... discarding all host entries older than  2010-08-18 02:20:20 CEST
 ... final counts: blocked 0, watched 1
 ... created user-defined chain blockhosts
 ... creating jump from INPUT to blockhosts chain
 ... creating jump from FORWARD to blockhosts chain
 ... no email to send.
server1:/tmp/BlockHosts-2.5.0#

BlockHosts is now ready to check for failed SSH logins, but not for failed ProFTPd logins because Debian's ProFTPd doesn't check /etc/hosts.allow and /etc/hosts.deny, which means BlockHosts isn't invoked when someone tries to log in to ProFTPd (of course, if someone has COUNT_THRESHOLD or more failed log in attempts on ProFTPD and then tries to log in to OpenSSH, he will be blocked, because OpenSSH uses /etc/hosts.allow which then invokes BlockHosts which finds the failed login attempts to ProFTPd; but if someone tries to log in to ProFTPd only, there's no way to catch him with the current setup). To block non-TCP_WRAPPERS services, we will create a cron job that starts BlockHosts every five minutes (for example).

 

3 Creating A BlockHosts Cron Job For Non-TCP_WRAPPERS Services

To block hosts from non-TCP_WRAPPERS services such as Debian's ProFTPd, you can run

blockhosts.py --ipblock=iptables --verbose

on the command line. Of course, you don't want to do this every few minutes, therefore we create a cron job for this.

First we create a little wrapper script for /usr/bin/blockhosts.py:

vi /usr/local/sbin/blockhosts

#!/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
/usr/bin/blockhosts.py --ipblock=iptables --verbose >> /var/log/blockhosts.log 2>&1

The purpose of this wrapper script is to pass the correct PATH to the /usr/bin/blockhosts.py script; if we use /usr/bin/blockhosts.py directly in the cron job, we will get errors saying that iptables could not be found.

Of course, we must make /usr/local/sbin/blockhosts executable:

chmod 700 /usr/local/sbin/blockhosts

Then, we create a cron job like this:

crontab -e

*/5 * * * *  /usr/local/sbin/blockhosts &> /dev/null

 

4 Testing

Now you can try to log in to your server using SSH and FTP with wrong usernames/passwords. After some time, you shouldn't be able to connect to your server at all which means you got blocked. Change your client's IP address and log in to the server's shell again.

Run

iptables -L

You can see in the output which IP addresses got blocked:

server1:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
blockhosts  all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
blockhosts  all  --  anywhere             anywhere

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain blockhosts (2 references)
target     prot opt source               destination
DROP       all  --  192.168.0.199        anywhere
server1:~#

Take a look at /etc/hosts.allow. The same IP addresses should be listed in the #---- BlockHosts Additions section:

vi /etc/hosts.allow

[...]
#---- BlockHosts Additions
ALL:   192.168.0.199 : deny
#bh: ip:   192.168.0.199 :  24 : 2010-08-18 14:37:53 CEST
#bh: ip:     192.168.0.2 :   1 : 2010-08-18 14:20:20 CEST
#bh: logfile: /var/log/auth.log
#bh: offset: 7619
#bh: first line:Feb 16 13:22:10 server1 login[1992]: pam_unix(login:session): session opened for user root by (uid=0)
#bh: logfile: /var/log/proftpd/proftpd.log
#bh: offset: 8588
#bh: first line:Feb 16 14:59:18 server1.example.com proftpd[13157] server1.example.com: ProFTPD 1.3.1 (stable) (built Fri Feb 6 12:26:25 GMT 2009) standalone mode STARTUP
#---- BlockHosts Additions
[...]

Finally, you can also take a look at /var/log/blockhosts.log:

tail /var/log/blockhosts.log

server1:~# tail /var/log/blockhosts.log
 ... discarding all host entries older than  2010-08-18 02:40:02 CEST
 ... final counts: blocked 1, watched 2
 ... no email to send.
blockhosts 2.5.0 started: 2010-08-18 14:45:01 CEST
 ... loaded /etc/hosts.allow, starting counts: blocked 1, watched 2
 ... loading log file /var/log/auth.log, offset: 7619
 ... loading log file /var/log/proftpd/proftpd.log, offset: 8588
 ... discarding all host entries older than  2010-08-18 02:45:01 CEST
 ... final counts: blocked 1, watched 2
 ... no email to send.
server1:~#

 

5 Links


Please do not use the comment function to ask for help! If you need help, please use our forum.
Comments will be published after administrator approval.