randombio.com | commentary
Saturday, May 13, 2017

Blocking sshd brute force attacks in Linux

How to configure two commonly used methods


If you run sshd, you already know why you need to block brute force attacks. They're not a security threat as long as you have a good password policy, but they clog up your logs with junk, which makes it hard to track down a real threat. With these techniques, the attacker only gets a fixed number of tries before they're permanently locked out.

There are two commonly used ways to do this.

Method 1: TCP Wrappers scripts (Denyhost, etc)

Advantages: It's transparent, easy to extend to other services, and it's easy to block or unblock sites manually. Until recently, this was the most widely used method.

Disadvantages: It requires the libwrap library and a specially compiled version of sshd. Openssh dropped libwrap support after version 6.6. Versions later than this cannot use hosts.deny files. TCP wrappers can be found here.

Compiling TCP Wrappers

Linux distros used to come with sshd pre-compiled with TCP Wrappers. Nowadays you have to build it yourself. This section covers TCP Wrappers 7.6 and OpenSSH 6.0.

To compile TCP Wrappers, make all the files in the tcp wrappers directory writable (chmod a+rw *) and edit the top of percent_m.c to get rid of the redefined sys_errlist declaration:

#ifndef SYS_ERRLIST_DEFINED
/*
extern char *sys_errlist[];
extern int sys_nerr;
*/
#endif

then comment out the malloc line at the top of scaffold.c like so:

/*extern char *malloc();*/

This fixes the duplicate definition error.

Next edit the Makefile and uncomment the line

#REAL_DAEMON_DIR=/usr/sbin

to this

REAL_DAEMON_DIR=/usr/sbin

On my system it was also necessary to add -DHOSTS_ACCESS to the EXTRA_CFLAGS line, making the linux section look like this:

linux:
@make REAL_DAEMON_DIR=$(REAL_DAEMON_DIR) STYLE=$(STYLE) \
LIBS= RANLIB=ranlib ARFLAGS=rv AUX_OBJ=setenv.o \
NETGROUP= TLI= EXTRA_CFLAGS="-DBROKEN_SO_LINGER -DHOSTS_ACCESS" all

If this was skipped, it would compile but ssh still couldn't use the hosts.deny files.

Then type make linux and manually install libwrap.a and tcpd.h somewhere (e.g. in /lib64 and /usr/include).

Compiling SSHD with TCP Wrapper support

To test if your version of sshd has tcpwrappers (libwrap) installed, type

strings /usr/sbin/sshd | grep wrap

If libwrap support is enabled, it will say:

Connection refused by tcp wrapper

Notice you can't use ldd /usr/sbin/sshd, because it could have been compiled with the static libwrap.a library, so nothing would be dynamically linked in for ldd to see.

The latest version of openssh that supports tcpwrappers is 6.6, so compile openssh-6.6p1 using the command

configure --with-tcp-wrappers

Note that the tcpwrappers part (above) has to be done first, or you will get the message configure: error: *** libwrap missing

Then type make. It will say something like:

/usr/lib64/gcc/x86_64-suse-linux/4.7/../../../../x86_64-suse-linux/bin/ld:
/lib/../lib64/libwrap.a(hosts_access.o): relocation R_X86_64_32 against
`.data' can not be used when making a shared object; recompile with -fPIC

/lib/../lib64/libwrap.a: could not read symbols: Bad value
collect2: error: ld returned 1 exit status

fPIC, of course, means either Florida Professionals in Infection Control or Position Independent Code. If you get this error, do what it says: add -fPIC to the Makefile for tcpwrappers and recompile and reinstall tcpwrappers.

If it compiles, type make install.


Method 2: Iptables

A more modern way to block sshd brute force attacks is to use a little brute force of your own. That means iptables. This site tells you why. It's a bit trickier to get it right, and so far I haven't found the perfect set of rules. Here's a quick refresher of some iptables commands.

 Command        Function      
iptables -L list rules
iptables -F flush, erases all the rules
iptables -X delete an empty iptables chain
iptables -I insert rules

Here's one ruleset that seems to be widely used. It simply drops any new connections that come in faster than four times a minute:

/usr/sbin/iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --set

/usr/sbin/iptables -I INPUT -p tcp --dport 22 -i eth0 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP

If add these rules, you may get the error message:

WARNING: The state match is obsolete. Use conntrack instead.

This is your computer trying to hint that constantly reinventing the wheel by changing the interface of software is a bad thing. It still accepts the ‘obsolete’ rule, though, and if you list the rules it shows you what the computer thinks you meant:

Chain INPUT (policy ACCEPT)
target prot opt source destination

DROP tcp -- anywhere anywhere tcp dpt:ssh ctstate NEW recent: UPDATE seconds: 60 hit_count: 4 name: DEFAULT side: source mask: 255.255.255.255

tcp -- anywhere anywhere tcp dpt:ssh ctstate NEW recent: SET name: DEFAULT side: source mask: 255.255.255.255

Chain FORWARD (policy ACCEPT)
target prot opt source destination

Chain OUTPUT (policy ACCEPT)
target prot opt source destination

Here' a set of rules that uses conntrack (from here). (Note: I haven't tested them).

iptables -I INPUT 1 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT

iptables -A INPUT -p tcp --dport 22 ! -s 192.168.1.0/24 -m conntrack --ctstate NEW -m recent --set --name SSH

iptables -A INPUT -p tcp --dport 22 ! -s 192.168.1.0/24 -m conntrack --ctstate NEW -m recent --update --seconds 60 --hitcount 2 --rttl --name SSH -j DROP

iptables -A INPUT -p tcp --dport 22 ! -s 192.168.1.0/24 -m conntrack --ctstate NEW -m recent --update --seconds 600 --hitcount 4 --rttl --name SSH -j DROP

This rule is supposed to drop the connection if the are more than 2 new login attempts in 60 seconds drop the connection, or if there are 5 attempts in 600 seconds. The 192.168.1.0/24 is the IP range of your local network.


On the Internet, no one can tell whether you're a dolphin or a porpoise
may 13 2017

back

to top