Monday, April 11, 2011

Better SSH Brute force prevention with iptables

There are countless of 'Howto prevent SSH Brute force attacks with iptables' scattered around the Net. What, in my opinion, is missing from those howtos is a 'holistic' approach to the problem. Only adding a 'recent' rule to iptables is just a part of the solution.

IPTables rule set

Let's take for example the ubiquitous:
# iptables -N IN_SSH
# iptables -A IN_SSH -m recent --name sshbf --rcheck \
              --hitcounts 3 --seconds 10 -j DROP
# iptables -A IN_SSH -m recent --name sshbf --set -j ACCEPT

# iptables -A INPUT -p tcp --dport ssh -m conntrack \
              --ctstate NEW -j IN_SSH

The first rule in IN_SSH will drop any new packet from a host that tried to connect to our server more than 3 times in the last 10 seconds.
It is a pretty good rule but there is something to consider.

Here is how an hypothetical attack goes:
  • attempt: fail (auth.log says Failed login attempt...)
  • attempt: fail (auth.log says Failed login attempt...)
  • attempt: fail (auth.log says Failed login attempt...)
  • subsequent attempts dropped (for 10 seconds, nothing is logged)
  • attempt: fail (auth.log says Failed login attempt...)
  • attempt: fail (auth.log says Failed login attempt...)
  • attempt: fail (auth.log says Failed login attempt...)
  • subsequent attempts dropped (for 10 seconds, nothing is logged)
  • and so on...

    This might or might not be desirable. I have seen slowish bruteforce attacks from zombie machine going on for hours and filling up my logs. Ideally, you want to block them cold after the first attempts.

    How?

    Replace --rcheck with --update. This will continously update the source 'last seen' timestamp.

    Now the attack will go something like:
    • attempt: fail (auth.log says Failed login attempt...)
    • attempt: fail (auth.log says Failed login attempt...)
    • attempt: fail (auth.log says Failed login attempt...)
    • subsequent attempts dropped for has long as the new attempt is within 10 seconds from the last one and nothing is logged.

    The zombie can keep on trying forever for what I care. As long as it tries to connect within 10 seconds it is going to get dropped. and my logs are less cluttered with script kiddies crap.

    But, there is a huge problem with this approach: it opens a big DoS vector.

    If the bad guy's intention is to DoS a legitimate user then what he needs to do is to spoof the legitimate user address and try to connect every few seconds... for ever... and ever... and the legitimate user is cut-off... for ever... and ever...

    A better IPTables ruleset

    A way to mitigate the DoS problem mentioned above is to add --rttl to the blocking rule. This will further narrow the check to the TTL of the packets in the hope that the legitimate user packets' TTL value is different from the bad guy spoofed address one. Quite a mouthful.

    # iptables -A IN_SSH -m recent --name sshbf --rttl \
                  --rcheck --hitcounts 3 --seconds 10 -j DROP
    # iptables -A IN_SSH -m recent --name sshbf --set -j ACCEPT

    There's another rule we can add to our rule set in order to catch login attempts spread out over time. Consider this:

    # iptables -A IN_SSH -m recent --name sshbf --rttl \
                  --rcheck --hitcounts 3 --seconds 10 -j DROP
    # iptables -A IN_SSH -m recent --name sshbf --rttl \
                  --rcheck --hitcounts 4 --seconds 1800 -j DROP 
    # iptables -A IN_SSH -m recent --name sshbf --set -j ACCEPT

    We added a second check which will kick-in after 4 hits in 30 minutes.
    The first rule we had says "Hold on, you're too fast. Go away!", the newly added one says "Hey, didn't I see you too many times already?"
    This is pretty neat since it tries to catch some clever attempts where the attacks come in waves.
    I saw attacks that tried a couple of times then stopped for a little while than tried again some more, then stopped then started again for more tries and so on and so forth.

    SSH configuration

    Another thing overlooked by a lot of 'bruteforce howto' is the tweaking of sshd_config, and I am not talking about the usual PermitRootLogin.

    The default number of password tries (MaxAuthTries) allowed per login attempt defaults to 6. This means that for every login attempt it is possible to try 6 passwords before getting rejected.

    No good. The value should be significantly lowered.

    This goes hand in hand with the setting of the login grace time value (LoginGraceTime, defaults to 120 seconds) and the maximum number of concurrent unauthenticated connections (MaxStartups, defaults to 10.)

    It is my opinion that if someone is logging through SSH knows what it is doing, therefore, I would lower the password attempts to 2 (or even 1) and the login grace time to 15-20 seconds (how long does it take to input a password?)

    The settings for the maximum number of unauthenticated connections is tricky. If a bad guy wants to DoS us, it will attempt to swamp the SSH server with fake connections. This will keep the MaxStartups value well above 10 creating problems to legitimate users. By lowering the grace time (and the password tries) we can mitigate this. Also, we could set MaxStartups to something fancy like this:

    MaxStartups 10:30:60

    What this does is (taken form ssh_config man page):
    [...] random early drop can be enabled by specifying the three colon separated values "start:rate:full" (e.g. "10:30:60").  sshd(8) will refuse connection attempts with a probability of "rate/100" (30%) if there are currently "start" (10) unauthenticated connections.  The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches "full" (60).
    In addition to all of this, hopefully, our iptables rules will kick-in and start dropping unwanted connections too.

    All in all, it boils down to 'too tight settings vs. usability.' You want to make bruteforcers' life difficult but you do not want to force your users to log-in only once per day at noon and only if the date is odd.

    The choice is yours, don't be late.

    ---

    Useful links:

    sshd_config man page
    OpenSSH

    ---

    No comments:

    Post a Comment