SSH Brute Force Attacks

Recently, a script-kiddie tool for brute forcing SSH passwords made its way into the wild. Early versions generally would make a few random attempts at logging into the root, admin, test, and guest accounts, choosing randomly from a list of 2000-odd "obvious" passwords.

I've seen an extended version of this hit a Linode that I manage for my employer, trying a variety of other accounts as well (such as people's names, webmaster, etc.), and failing.

This raises two issues:

1. Make sure that you do not use an "obvious" password for lish. It's almost inevitable that someone will try this tool against a Linode host box, if it hasn't already happened.

2. If you run sshd on your own Linode, use RSA or DSA key authentication only. You will need to generate a key pair, and place a copy of the public key into the user's $HOME/.ssh/authorized_keys file, and then set:

RSAAuthentication yes

PubkeyAuthentication yes

PasswordAuthentication no

in your sshdconfig file (usually /etc/ssh/sshdconfig). Keep your existing SSH session open, restart sshd, and test a new login using the RSA key.

Keep your private key PRIVATE! It is a good idea to protect it with a passphrase. You might want to keep a copy of it on a USB flash drive.

You can generate key pairs using ssh-keygen on a Linux/FreeBSD/other nix/Cygwin installation, or with PuTTYgen* if you use PuTTY on Windows.

edit: add PubkeyAuthentication

36 Replies

so.. on this note i found i had the same thing, so i wrote a perl script to handle this.. it needs a bit of work because it takes a while sometimes, but the shortversion is after 3 failed logins u get added to iptables drop.. the details for it are all here

http://www.our-lan.com/blog

Another band-aid fix you can do if you have a very small number of users is to run sshd on a non-standard port. This of course is not a fix, but for 99.99% of the attacks out there, it'll be effective, since these brute-force approaches look for the low-hanging fruit, and they're not going to take the time to do a complete port scan on every IP. Not yet, anyway…

@NecroBones:

Another band-aid fix you can do if you have a very small number of users is to run sshd on a non-standard port. This of course is not a fix, but for 99.99% of the attacks out there, it'll be effective, since these brute-force approaches look for the low-hanging fruit, and they're not going to take the time to do a complete port scan on every IP. Not yet, anyway…

That's exactly what I do :)

> Another band-aid fix you can do if you have a very small number of users is to run sshd on a non-standard port. This of course is not a fix, but for 99.99% of the attacks out there, it'll be effective, since these brute-force approaches look for the low-hanging fruit, and they're not going to take the time to do a complete port scan on every IP. Not yet, anyway…

n00b question, but how can I set that up? Recommend any good tutorials, or any recommendations?

Thanks,

-Kevin

@chapterthree:

n00b question, but how can I set that up? Recommend any good tutorials, or any recommendations?
Edit /etc/ssh/sshd_config - change the value of the Port parameter, uncomment the Port statment if necessary and then restart the ssh daemon.

To connect, specify the -p portnum option for ssh, in addition to your normal options.

Choose a port number less than 1023, so that if sshd ever goes down, a user cannot start his own compromised version of ssh listening on your chosen port.

Peter,

Thanks! I'll go set that up in a few.

One question, I assume this would affect how scp would work as well, correct? I noticed there is a -P (port) option for scp, so I would just need to do -P and the port number, is that correct?

[Follow up] So it appears to have worked, except when I connect to Lish. If I type 'ssh [username]@host34.linode.com' it connects fine. Any idea why that is?

Thanks

-Kevin

@chapterthree:

So it appears to have worked, except when I connect to Lish. If I type 'ssh [username]@host34.linode.com' it connects fine. Any idea why that is?
Lish is accessed via a separate instance of sshd running under the host kernel - so that Lish is still available to access your Linode's console even if you hose your ssh daemon. You have no control over which port the daemon running under the host kernel listens on (it's common to all Linodes on that host and controlled by caker).

Ahh, OK yeah that makes sense now :)

Thanks for your help!

-Kevin

As a follow up, I have started using a different port under 1023, but I think it's causing issues, as after a period of time, the connection seems to freeze, then time out. I'm thinking I might be using a port that is being used by something else. Anybody recommend a range of ports that are relatively safe to use without running the risk of being used by something else.

Thanks,

-Kevin

Check out this link for an alternative. It uses the recent netfilter module which is installed in the Linode kernels. This limits ssh connection attempts to 3 per 60 secoond period without having effects on existing connections.

http://blog.andrew.net.au/2005/02/17

Try 203, 205, 207 & 208. The block from 201 to 208 inclusive is assigned to AppleTalk, and the four just listed are currently marked as unused by that protocol.

So im corious to know, if there is a way to makeip tables load certian rules on startup? cause i would like to implement the methods that were described in the link posted by mike

cheers

Nathan

You can put startup scripts in /etc/init.d or /etc/rc*.d.

LSB (linux standard base) is now /etc/init.d but redhat 9 uses /etc/rc*.d

Lish is very useful if your firewall locks you out :oops:

Here is a script based on the debian skeleton init.d script:

#! /bin/sh
#
# ssh-bruteforce
#
# Author:   Michael Greb <michael@thegrebs.com>.
#
# Version:      @(#)ssh-bruteforce  1.0  26-Mar-2005 
#

set -e

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DESC="iptables for ssh brute force mitigation"
NAME=ssh-bruteforce
SCRIPTNAME=/etc/init.d/$NAME

#
#       Function that starts the daemon/service.
#
d_start() {
    iptables -N SSH_WHITELIST
    iptables -A SSH_WHITELIST -s 70.187.46.105 -m recent --remove --name SSH -j ACCEPT
    iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
    iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSH_WHITELIST
    iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update \
        --seconds 60 --hitcount 4 --rttl --name SSH -j ULOG --ulog-prefix SSH_brute_force
    iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update \
        --seconds 60 --hitcount 4 --rttl --name SSH -j DROP
}

case "$1" in
  start)
        echo -n "Starting $DESC: $NAME"
        d_start
        echo "."
        ;;

  *)
        # echo "Usage: $SCRIPTNAME {start}" >&2
        echo "Usage: $SCRIPTNAME {start}" >&2
        exit 1
        ;;
esac

exit 0</michael@thegrebs.com> 

This line:

iptables -A SSH_WHITELIST -s 70.187.46.105 -m recent --remove --name SSH -j ACCEPT

should be repeated as many times as you wish with the IP addresses you want whitelisted, or none if you don't wish to have any whitelisted. This script does not unload the iptables rules when asked

Well, I tried using some of the 200 ports, but those seemed to time out after a while too (not sure why).

So now I'm using the script mikegrb typed up, and it seems to be working, sort of.

Since I have to use PPPoE, I commented out the "iptables -A SSH_WHITELIST…" line. My understanding from the script is that it will allow 3 connections within a 60 second period, but it seems to only be allowing 1. Is there something that needs to be changed in the code above to allow up to 3 connections?

Also, is it safe to edit the 60 second thing? I am the only person who SSH's into my box, so wondering if it is OK if I extend the time frame to something longer, like 120 seconds or 300 seconds for even more added protection.

Thanks,

-Kevin

In addition to the above postings, you can also use /etc/hosts.allow and hosts.deny files for ssh.

What I have done in the past is allow .edu, .net, .org, .com. While this may still be considered "wide-open", it does block a lot of countries. Chances are, your legit users should be coming from one of the above anyways.

Now, I just use an IP address list since I only have a small number of users.

-John

@Internat:

So im corious to know, if there is a way to makeip tables load certian rules on startup? cause i would like to implement the methods that were described in the link posted by mike

cheers

Nathan
Under Fedora the system will save the current table state at shutdown and reload it at bootup if you modify the config values in /etc/sysconfig/iptables-config

You can force a save of the current tables (in case teh server hangs) by /etc/init.d/iptables save

I dunno if the other Linux distro's do similar.

I changed ULOG to LOG, but I would like to limit the logging to a few per minute. I tried using -m limit –limit 2/minute, but it failed. Does the Linode kernel not have support for the limit module?

I just noticed something when I rebuilt my system using Debian Sarge instead of Woody: if you want to disable password authentication (for public-key-only authentication), you need both PasswordAuthentication no and ChallengeResponseAuthentication no.

After continuing to see this thread dominate the top 10 threads, I decided to study up on implementing SSH keys for authentication. I generated keys for my freebsd box to access my linode, had success, and saw how simple it was to set up. I now have every single unix machine (5 machines) using SSH keys to authenticate to my linode. I'll eventually do the same thing with my linode, so it can run jobs and communicate with my unix boxes at home.

I'll eventually use iptables also, but this is solid enough (using a 512bit key) and SSH is the ONLY port open.

@NecroBones:

Another band-aid fix you can do if you have a very small number of users is to run sshd on a non-standard port. This of course is not a fix, but for 99.99% of the attacks out there, it'll be effective, since these brute-force approaches look for the low-hanging fruit, and they're not going to take the time to do a complete port scan on every IP. Not yet, anyway…

security through obscurity

Another precaution you can take is deny root login itself. This can be done by setting

PermitRootLogin to no in /etc/ssh/sshd_conf

And then using some arbitrary user to login to your linode for which you give very limited rights or none. The username for this user can be (ic87pz19fd for example) as cryptic as one of your password. Then su using this login.

I use skey only logins (so anyone connecting without a valid skey is just disconnected, without the chance to enter anything).

The iptables 'recent' module doesn't seem to be on my system.

iptables v1.2.11: Couldn't load match `recent':/lib/iptables/libipt_recent.so: cannot open shared object file: No such file or directory

@strikesam:

@NecroBones:

Another band-aid fix you can do if you have a very small number of users is to run sshd on a non-standard port. This of course is not a fix, but for 99.99% of the attacks out there, it'll be effective, since these brute-force approaches look for the low-hanging fruit, and they're not going to take the time to do a complete port scan on every IP. Not yet, anyway…

security through obscurity

Yes, I know, I thought I was clear about that. :)

@astro:

The iptables 'recent' module doesn't seem to be on my system.

iptables v1.2.11: Couldn't load match `recent':/lib/iptables/libipt_recent.so: cannot open shared object file: No such file or directory


CONFIGIPNFMATCHRECENT is set in both the Latest 2.4 and 2.6 kernels, and has been for quite some time now. This is a user-space problem.

-Chris

My bad. I re-emerged the iptables package and it worked. Not sure what the deal was, though, because it was supposedly the same ebuild.

I added the "recent" feature to my iptables on my home computer the other day, and the number of these attempts dropped dramatically. Basically, only the first attempt will show in my logs, because the rest are dropped.

> So im corious to know, if there is a way to makeip tables load certian rules on startup? cause i would like to implement the methods that were described in the link posted by mike

I use the iptables-save command to save the config to a file and then use iptables-restore to load it on startup.

so…

iptables-save > /etc/myiptables.conf

then…

iptables-restore /etc/myiptables.conf

@astro:

The iptables 'recent' module doesn't seem to be on my system.

iptables v1.2.11: Couldn't load match `recent':/lib/iptables/libipt_recent.so: cannot open shared object file: No such file or directory

When compiling iptables, I had to add "recent" to the list of extensions to be made near the top of extensions/Makefile in the source tree.

hth

Nice script Mike. Unfortunately it only works if your INPUT chain policy is set to ACCEPT (mine is on DROP).

So I guess this kinda thing is pretty common eh? Took a look in my logs the other day and saw a bunch of these attacks. However I'm gonna be redoing my linode in the next month or two, so I don't care :D I have only a couple user accounts with good passwords so I'm good. It's just annoying having my logs full of this garbage but I'll worry bout it later.

-Brian

I just found this Python app that will scan log files and then update iptables rules to block after to many failures.

http://fail2ban.sourceforge.net/

Quote from the website: "Fail2Ban scans log files like /var/log/pwdfail or /var/log/apache/error_log and bans IP that makes too many password failures. It updates firewall rules to reject the IP address."

See also:

http://devmike.com/blog/archives/2005-0 … 58_57.html">http://devmike.com/blog/archives/2005-07-19T215857.html

Installed smoothly on my Debian Linode. The program banned two script kiddies in the first 5 minutes.

@drware:

I just found this Python app that will scan log files and then update iptables rules to block after to many failures.

Did you miss http://www.linode.com/forums/viewtopic.php?p=6935#6935? It has the advantage of not running a script from cron every X minutes and thus eating unnecessary resources. In addition, it will block someone after the third attempt rather then an IP not being blocked until the first time the cron script runs. A problem with cron scripts is, these bruteforce attempts rarely last longer then a minute so by the time the cron script blocks them, they are already done.

Michael

Yep, guess I missed that post…

This Fail2ban app is not using cron. It runs as a service. You set the polling interval via the config file. Default is 1 second.

I am sure this app is using plenty of resources, and would not want to imply that it is a better solution then those already proposed. I just thought it was interesting.

I have found Shorewall to work wonderfully for configuring my iptables firewall both at home and on my linode. It also supports the iptables rate limiting feature, as is being discussed.

The following line in /etc/shorewall/rules works great for me, and sets up iptables on each restart:

ACCEPT   net            fw              tcp     22      -          -            3/min:3

Description of the last parameter (rate limit):

#       RATE LIMIT      You may rate-limit the rule by placing a value in
#                       this column:
#
#                               <rate>/<interval>[:<burst>]
#
#                       where <rate> is the number of connections per
#                       <interval> ("sec" or "min") and <burst> is the
#                       largest burst permitted. If no <burst> is given,
#                       a value of 5 is assumed. There may be no
#                       no whitespace embedded in the specification.</burst></burst></interval></rate></burst></interval></rate>

A lot of the other scripts go to the trouble of blocking the source IP when the rate limit is exceeded, but I've found it to be totally unnecessary – when the scanner program gets blocked, it moves on.

Cheers,

Raman

@mastabog:

Nice script Mike. Unfortunately it only works if your INPUT chain policy is set to ACCEPT (mine is on DROP).
If your policy is set to DROP (as it probably should be) add one more line:

# Create the Whitelist chain
iptables -N SSH_WHITELIST

# Block after three failed attempts per minute
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j SSH_WHITELIST
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --rttl --name SSH -j ULOG --ulog-prefix SSH_brute_force
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 --rttl --name SSH -j DROP

# Less than four? Accept
iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j ACCEPT

# Whitelist (repeat for each host...or create a mask)
iptables -A SSH_WHITELIST -p tcp --dport 22 -s ${WHITELISTED_HOST} -m recent --remove --name SSH -j ACCEPT

@raman:

The following line in /etc/shorewall/rules works great for me, and sets up iptables on each restart:

ACCEPT   net            fw              tcp     22      -          -            3/min:3

This thread is long dead, but for future searchers and posterity, do not do the above. It generally does work fine, but brute force attacks will cause a DOS on your ssh server because the rule is not specific to the source IP – it blocks all conections, including valid ones.

However, if you are using Shorewall 3.0.4 or later, you can just put something like this in rules instead:

Limit:info:SSH,3,60   net    fw    tcp   ssh

The Limit action is builtin (> 3.0.4), and the params define how it is logged, and how many connections are allowed over what interval for individual IPs. See this page: http://www.shorewall.net/PortKnocking.html#id2460417 (the URL is a bit misleading, it talks about port knocking as well).

Cheers,

Raman

Reply

Please enter an answer
Tips:

You can mention users to notify them: @username

You can use Markdown to format your question. For more examples see the Markdown Cheatsheet.

> I’m a blockquote.

I’m a blockquote.

[I'm a link] (https://www.google.com)

I'm a link

**I am bold** I am bold

*I am italicized* I am italicized

Community Code of Conduct