SSH Brute Force Attacks
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
@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.
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).
Thanks for your help!
-Kevin
Thanks,
-Kevin
cheers
Nathan
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
#! /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
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
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'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.
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
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
-Brian
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:
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
Michael
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.
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:
Cheers,
Raman