SBFD - SSH Brute-Force Detector

SBFD runs from cron, scanning the latest changes in your syslog file for failed login attempts. After a threshold has been reached (defaults to 5 failures or connections immediately dropped), the IP is added to "/etc/hosts.deny.sshd". This file can then be used by tcpwrappers to block further connections from that IP

It is recommended a list of fall-back IPs be configured in /etc/hosts.allow, so they never get blocked.

To get emailed when a new IP is added, edit /usr/local/bfd/notify

Install instructions

You also might want to block all IPs without reverse DNS or valid reverse DNS with the line:

sshd : PARANOID UNKNOWN : deny

change history

similar programs / other solutions

Speed

Running under FreeBSD 4.11 on a dual p3 1ghz:

9712 runs completed over 161 hours
wallclock time spent per run:
min 0.030s
avg 0.058s
max 0.840s
stddev 0.029s

security

2007-06-07: The recent focus on spoofed ssh log messages made me re-examine the log message regular expressions. I wrote a simple shellscript to test these log messages. Here is how sbfd handled them:

1.3
$ ./test-iplist ./sbfd-1.3/iplist
TEST:username with spaces in it
192.168.50.65
192.168.50.65
TEST:invalid protocol with failed password in it
192.168.50.65
192.168.50.65
TEST:invalid user
192.168.50.65
TEST:bad protocol: username with "from all"
192.168.50.65
192.168.50.65
TEST:ssh printing "from all"
all
TEST:ssh printing "from all", with port
all port 34786 ssh2
1.4
$ ./test-iplist ./sbfd-1.4/iplist
TEST:username with spaces in it
192.168.50.65
TEST:invalid protocol with failed password in it
TEST:invalid user
192.168.50.65
TEST:bad protocol: username with "from all"
TEST:ssh printing "from all"
error with host all
TEST:ssh printing "from all", with port
error with host all

The "ssh printing from all" test case is just a defensive coding test, as I don't see how ssh could ever print such a thing. The other cases are safe from the attacks even in 1.3, because ssh always prints "from [hostname]" at the end of the line, and the .* in the regex is greedy (will match everything it can).

That said, 1.4 fixes the case where 1.3 was mistaken by invalid protocol, as well as 1.3's double matching of lines.

I welcome any examples where sbfd does the wrong thing

contact

sbfd AT drown .org

- et tu, brute for ce?