# [HOWTO] Get rid of SSH Brute Force Attempts / Script Kiddies

## BlinkEye

Wiki HowTo

I get twice a day a mail from logsentry telling me how many brute force attacks my SSH server got. Generally, I don't care (but I'll explain in the howto why I decided to go against these attacks). Although I've seen several scripts on the www blocking out such attacks I decided to write my own python script. Many of the scripts were just too bloated (like using a database) or too circumstantial and lacking easy configuration and customisation. I wrote an easy understandable, easy adjustable script AND trying to explain what, how and why I'm doing it. Well, see for yourself.

README (if you question the use of this script):

Please don't start a flame of why not using password or using strong passwords and on moving SSH to a different port or using portknocking. I know all these methods - and I wouldn't even mind if someone could login to my server (of course this wouldn't be an account I use because there's no way you bruteforce my passwords within a lifetime). I do have my reasons. Point.

Last update of the script: 2006-03-03

----------

## vladgrigorescu

That is a *really* great and detailed HOWTO, thanks a lot!

----------

## thoffmeyer

Wow this howto is GREAT!

----------

## zbindere

I am using denyhosts.

```
emerge denyhosts
```

----------

## BlinkEye

 *zbindere wrote:*   

> I am using denyhosts.
> 
> ```
> emerge denyhosts
> ```
> ...

 

i bet it's a good tool. my script does very much the same (main difference: uses iptables instead of /etc/hosts.deny - i'll reconsider which way is the better). i explicitly mentioned in the first post that my goal was to provide an easy understandable script and explaining what it actually does. denyhosts has more than 2100 source code lines (not including README, configuration files and so on). don't tell me you've read and understand all those 20 python scripts. i haven't run it but i discovered it while writing the howto and downloaded the source and had a look at it. i decided my script was still for some use. it's just not overhelming and everyone sees what it does.

the statistic upon failed login and used usernames denyhosts does is kind of irrelevant for my approach: too many login failures -> go take a break. for a statistic see list of past login attempts (IP) and list of past login attempts (usernames) sections - a simple sed command. i don't think one needs more than that.

[EDIT]

I'll stay with iptables. i found a good article discussing the linux firewall mechanism( see linux exposed for the full review):

 *Linux exposed wrote:*   

> Linux Firewall Mechanisms
> 
> Most Linux distributions, including Fedora Core, provide two forms of service access control: TCP wrappers and iptables. I use the term access control and not firewalling because TCP wrappers should really not be referred to as a firewall per se. The older TCP wrappers offers centralized daemon host access control in one nice and easy to edit file. It can be configured to monitor incoming requests for a range of daemons, works in tandem with xinetd, and changes to it are instantly applied. TCP wrappers is somewhat insecure if used on an untrusted network, is not very powerful, and so should only really be used on trusted networks or where very specific requirements demand it. 

 

 *Linux exposed wrote:*   

> Under Linux, there are three ways of controlling service access by hosts or networks: iptables, TCP wrappers, or individual daemon config settings. The first is the only method that can both be considered a truly hardened method of limiting service access on an untrusted network (personal firewall) and be used in either a stand-alone server or a full-blown network firewall configuration. 

 

 *Linux exposed wrote:*   

> Security that you don't both fully understand and fully control is just an illusion

 

----------

## BlinkEye

I did a major update. Following are the recent changes:

* Completely rewrote the core

* Removing thread logic due to limitation and ressource usage

* Changing datastructure of main list from an array to a hash map to speed up searches

* Adding  CHECK_INTERVALL variable

* Adding dynamical BLOCKING_PERIOD (for every hundred login failure an additional second for the is added)

* Thorough testing

* Modified mailing feature so you get one mail per each run (if any IP were blocked) instead of a mail per IP to prevent mail flood/ressource usage upon a DoS

* Adding DoS / Performance / Stress Test

* Modifying Howto

----------

## _dA_CyANIDe

This script doesn't workes for me. Maybe sshd is misconfigured .. 

errors in /var/log/auth.log :

Jan 19 01:00:32 a02-0520a sshd[5051]: error: Could not get shadow information for NOUSER

Jan 19 01:00:32 a02-0520a sshd[5051]: Failed password for invalid user fu from 147.229.216.198 port 2664 ssh2

Jan 19 01:00:33 a02-0520a sshd[5051]: Failed password for invalid user fu from 147.229.216.198 port 2664 ssh2

Jan 19 01:01:15 a02-0520a sshd[5086]: Invalid user du from 147.229.216.198

Jan 19 01:01:15 a02-0520a sshd[5086]: Failed none for invalid user du from 147.229.216.198 port 2665 ssh2

Jan 19 01:01:16 a02-0520a sshd[5086]: error: Could not get shadow information for NOUSER

Jan 19 01:01:16 a02-0520a sshd[5086]: Failed password for invalid user du from 147.229.216.198 port 2665 ssh2

Jan 19 01:01:17 a02-0520a sshd[5086]: Failed password for invalid user du from 147.229.216.198 port 2665 ssh2

 :Sad: 

----------

## BlinkEye

everything seems alright:

```
# ./blacklist.py "Jan 19 01:00:32 a02-0520a sshd[5051]: error: Could not get shadow information for NOUSER

> Jan 19 01:00:32 a02-0520a sshd[5051]: Failed password for invalid user fu from 147.229.216.198 port 2664 ssh2

> Jan 19 01:00:33 a02-0520a sshd[5051]: Failed password for invalid user fu from 147.229.216.198 port 2664 ssh2

> Jan 19 01:01:15 a02-0520a sshd[5086]: Invalid user du from 147.229.216.198

> Jan 19 01:01:15 a02-0520a sshd[5086]: Failed none for invalid user du from 147.229.216.198 port 2665 ssh2

> Jan 19 01:01:16 a02-0520a sshd[5086]: error: Could not get shadow information for NOUSER

> Jan 19 01:01:16 a02-0520a sshd[5086]: Failed password for invalid user du from 147.229.216.198 port 2665 ssh2

> Jan 19 01:01:17 a02-0520a sshd[5086]: Failed password for invalid user du from 147.229.216.198 port 2665 ssh2

> "

* Entering test mode

* SUCCESS: Caught 147.229.216.198

* SUCCESS: Sending mail from blacklist@yourdomain to ssh@yourdomain
```

what's not working?

----------

## _dA_CyANIDe

So, the problem is in logtail version. Logtail from logsentry 1.1.1 doesn't workes for me. 

Solution:

Replace /usr/bin/logtail from Logsentry with this file : ftp://blinkeye.ch/public/logtail .

Now everything workes fine.  :Laughing: 

----------

## shaped.ch

very good howto!

thanks!

----------

## _dA_CyANIDe

Yes. IT IS.   :Very Happy:   THX 2 BlinkEye

----------

## Andersson

Very nice. I have a pretty random user name and a very good password, but I still feel uncomfortable with all those login attempts in the logs.

On line 137, I think you're missing a space after the " -f" (I got some error message about not finding the file "-f/var/log/auth.log"). I added a space after "-f" and got "IOError: File -f cannot be read." instead. I don't know if I have some light version of logtail, but I don't have an -f option. Replaced " -f " with a single space instead, and it works.

I still have one problem. Whenever someone tries to log in as root, it is logged in a different format:

```
Jan 21 13:31:21 clint sshd(pam_unix)[1416]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=irc.******.com  user=root

Jan 21 13:31:23 clint sshd[1411]: error: PAM: Authentication failure for root from irc.******.com
```

----------

## BlinkEye

 *Andersson wrote:*   

> Very nice. I have a pretty random user name and a very good password, but I still feel uncomfortable with all those login attempts in the logs.
> 
> On line 137, I think you're missing a space after the " -f" (I got some error message about not finding the file "-f/var/log/auth.log"). I added a space after "-f" and got "IOError: File -f cannot be read." instead. I don't know if I have some light version of logtail, but I don't have an -f option. Replaced " -f " with a single space instead, and it works.

 

Yes, I do have the same issue on my boxes. The script does not miss the space after "-f", but there are two logtail version around. And I emerged the logsentry-1.1.1 package on both of the boxes - one is a perl script (which I provided a link for) and which needs the "-f" flag and the other is a (compiled) c program ...

 *Andersson wrote:*   

> I still have one problem. Whenever someone tries to log in as root, it is logged in a different format:
> 
> ```
> Jan 21 13:31:21 clint sshd(pam_unix)[1416]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=irc.******.com  user=root
> 
> ...

 

I see. I take it you use

 *sshd_config wrote:*   

> UseDNS yes

 

 *OpenSSH FAQ wrote:*   

> You can disable most of the server-side lookups by setting UseDNS no in sshd_config.

 

is there a strong reason you use dnslookup? if set to no does this solve the domainname entry?

----------

## Andersson

 *BlinkEye wrote:*   

> The script does not miss the space after "-f", but there are two logtail version around. And I emerged the logsentry-1.1.1 package on both of the boxes - one is a perl script (which I provided a link for) and which needs the "-f" flag and the other is a (compiled) c program ...

 

Ok, I understand. How about testing which version is installed? Or having a config variable that can disable the "-f"? Or is the perl script the only supported version?  :Wink: 

 *BlinkEye wrote:*   

> I take it you use
> 
>  *sshd_config wrote:*   UseDNS yes 
> 
> is there a strong reason you use dnslookup? if set to no does this solve the domainname entry?

 

I didn't know about that option. UseDNS was commented, so I set it to no:

```
Jan 21 14:26:33 clint sshd(pam_unix)[2398]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=193.xx.xx.xx  user=root

Jan 21 14:26:35 clint sshd[2389]: error: PAM: Authentication failure for root from 193.xx.xx.xx
```

This gives the ip number, but still does not match the regexp in the script. If I log in as any other user name, I get a line that matches, like this below (in addition to two lines like the ones above).

```
Jan 21 13:43:43 clint sshd[1610]: Failed keyboard-interactive/pam for invalid user not_a_user from 193.xx.xx.xx port 1198 ssh2
```

But no such line for root, so I changed the regexp to match the error: PAM: Authentication failure line instead:

```
SYSLOG_REGEX = r"sshd[[][0-9]+[]]: error: PAM: Authentication failure for (?:illegal user )?(?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
```

So anyway, it seems to work exactly as intended now. Thanks for the help. I didn't know about the (?P<user>.*?) thing either (capturing group), so I guess I've learned something new as well from this script.  :Smile: 

----------

## spengy

I use pure iptables

```

iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH -j ACCEPT

iptables -I INPUT -p tcp --dport 22 -m recent --update --seconds 360 --hitcount 4 -rttl --name SSH -j DROP

```

More than 4 connections in 360 seconds?  then they get dropped.  It works quite well.

In my sshd log, I will get maybe 2 bruteforce attempts from some n00b before they get dropped.

----------

## BlinkEye

 *Andersson wrote:*   

> I didn't know about that option. UseDNS was commented, so I set it to no:
> 
> ```
> Jan 21 14:26:33 clint sshd(pam_unix)[2398]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=193.xx.xx.xx  user=root
> 
> ...

 

the "error: PAM ..." doesn't appear always. grep your log for sshd and have a look. upon "normal" login tries, like the one we try it would work. but the script used by attackers will not generate such "error: PAM ..." lines. I do have dozens of entries not followed by an "error: PAM ..." entry. it's strange you don't have the same line for a root atempt - I sure do. root login attempts are still the most popular   :Wink: 

could you mail/pm me a series of login attempts with different users and root too? not from localhost please.

----------

## BlinkEye

 *spengy wrote:*   

> I use pure iptables
> 
> ```
> 
> iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH -j ACCEPT
> ...

 

yes, these are the iptables lines you found all over the net.

1. I tried that and I wrote that and the link of how to do it in my howto (where it is actually explained what is done). it doesn't work reliable for me and I get unkown delays for anyone trying to log-in.

2. I've read a lot of posts telling it doesn't work as it should for them

3. what does "it works quite well" mean? I wouldn't be satisfied with "quite well".

4. it limits the possibility/use for user actually working on the server. 4 connection within 360 seconds is way to low for me. if I start work on my server I open instantly 5 connections and other users do that too. sure I could increase that limit but then those failed login attempts of attackers start to increase too.

5. it limits me further because i would try to keep that limit down - but what if I started work, disconnected and just decided to keep on working?

6. using these rules increases the load unnecessary because it must take into account every SSH connection and keep a counter for every IP trying to connect (I don't think this is actually a problem for iptables and I not have hundreds of users working concurrently of course - but then again: why do I get those delays? it must be iptables and the counters)

----------

## Andersson

 *BlinkEye wrote:*   

> the "error: PAM ..." doesn't appear always. grep your log for sshd and have a look. upon "normal" login tries, like the one we try it would work. but the script used by attackers will not generate such "error: PAM ..." lines. I do have dozens of entries not followed by an "error: PAM ..." entry. it's strange you don't have the same line for a root atempt - I sure do. root login attempts are still the most popular   

 

Well, in that case I might as well leave the regexp as it was. I do have permitRootLogin no so where's the harm?

 *BlinkEye wrote:*   

> could you mail/pm me a series of login attempts with different users and root too? not from localhost please.

 

Sorry, I don't keep my logs more than a couple of weeks, and I've had my ssh port blocked from the outside for a while. It's open again now though, so we'll see how long it takes the port scanners to find me.  :Smile: 

----------

## Andersson

 *Andersson wrote:*   

>  *BlinkEye wrote:*   could you mail/pm me a series of login attempts with different users and root too? not from localhost please. 
> 
> Sorry, I don't keep my logs more than a couple of weeks, and I've had my ssh port blocked from the outside for a while. It's open again now though, so we'll see how long it takes the port scanners to find me. 

 

Ok, now they've found me again.  :Laughing: 

I see what you mean, these login attempts don't look like my own. But there is no Failed keyboard-interactive/pam... line either, just a line saying Invalid user.... Also, no root attempts show up in the logs (although I'm sure they try). Now, I don't know much about how security works under the hood. Please correct me, but this is what I believe; sshd uses pam to check the user/password, right? And in case it fails, pam logs the attempt. But a root attempt is never even handed off to pam, since sshd itself denies root to login (permitRootLogin no), which is why it is logged differently.

Anyway, to make this script work for me, I'll have to either change the sshd log verbosity somehow, or change the regexp again to something like this: (And also accept that a root attempt will simply fail, but not result in a blacklisting.)

```
SYSLOG_REGEX = r"sshd[[][0-9]+[]]: Invalid user (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
```

Here's a sample of my log:

```
Jan 22 17:57:55 clint sshd[17822]: Did not receive identification string from 217.58.169.124

Jan 22 18:10:21 clint sshd[18459]: Invalid user admin from 217.58.169.124

Jan 22 18:10:22 clint sshd[18464]: Invalid user admin from 217.58.169.124

Jan 22 18:10:22 clint sshd[18469]: Invalid user admin from 217.58.169.124

Jan 22 18:10:23 clint sshd[18474]: Invalid user admin from 217.58.169.124

Jan 22 18:10:24 clint sshd[18479]: Invalid user administrator from 217.58.169.124

Jan 22 18:10:30 clint sshd[18484]: Invalid user administrator from 217.58.169.124

Jan 22 18:10:30 clint sshd[18489]: Invalid user administrator from 217.58.169.124

Jan 22 18:10:31 clint sshd[18494]: Invalid user tads from 217.58.169.124

Jan 22 18:10:32 clint sshd[18499]: Invalid user tads from 217.58.169.124

Jan 22 18:10:32 clint sshd[18504]: Invalid user tads from 217.58.169.124

Jan 22 18:10:33 clint sshd[18509]: Invalid user tip from 217.58.169.124

Jan 22 18:10:34 clint sshd[18516]: Invalid user tip from 217.58.169.124

Jan 22 18:10:34 clint sshd[18521]: Invalid user tip from 217.58.169.124

Jan 22 18:10:35 clint sshd[18526]: Invalid user myra from 217.58.169.124

Jan 22 18:10:36 clint sshd[18531]: Invalid user myra from 217.58.169.124

Jan 22 18:10:37 clint sshd[18536]: Invalid user myra from 217.58.169.124

[...]

Jan 24 11:34:27 clint sshd[9518]: Did not receive identification string from 61.211.225.12

Jan 24 11:45:25 clint sshd[9590]: Invalid user admin from 61.211.225.12

Jan 24 11:45:28 clint sshd[9595]: Invalid user test from 61.211.225.12

Jan 24 11:45:30 clint sshd[9600]: User guest not allowed because shell /dev/null is not executable

Jan 24 11:45:32 clint sshd[9605]: Invalid user webmaster from 61.211.225.12

Jan 24 11:45:37 clint sshd[9615]: Invalid user oracle from 61.211.225.12

Jan 24 11:45:39 clint sshd[9620]: Invalid user library from 61.211.225.12

Jan 24 11:45:45 clint sshd[9625]: Invalid user info from 61.211.225.12

Jan 24 11:45:47 clint sshd[9630]: Invalid user shell from 61.211.225.12

Jan 24 11:45:50 clint sshd[9635]: Invalid user linux from 61.211.225.12
```

----------

## Kernel_Klink

 *Andersson wrote:*   

>  *Andersson wrote:*    *BlinkEye wrote:*   could you mail/pm me a series of login attempts with different users and root too? not from localhost please. 
> 
> Sorry, I don't keep my logs more than a couple of weeks, and I've had my ssh port blocked from the outside for a while. It's open again now though, so we'll see how long it takes the port scanners to find me.  
> 
> Ok, now they've found me again. 
> ...

 

I am at work and they don't let me ssh home anymore so I will have to look at the script when I get there but I had the same issue.  I assumed it was due to the fact that I use AllowUsers in my sshd config.  While this works quite well at preventing access from any account other than the one listed in the AllowUsers line in sshd.conf, I liked the idea so I am using this script.  Since sshd checks the username against this value first, it never hands off to PAM to authenticate if the username does not match. In order to make it work in my situation I had to modify the script so that it also looked for 'Invalid user'.  It was a simple fix but I can't remember where I put it right now.  Once the script included Invalid user it would write the IP to iptable list.

Thanks again BlinkEye.

I will update this post in a few hours when I get home.

**EDIT**

Boy am I embarassed.  I am not using BlinkEye's script.  It is another one that uses iptables but instead of python it uses perl.  Anyway this is what I check for 

```

my($REASONS) = '(Invalid user|Failed password|Failed none)';

```

Even so, it should just be a matter of updating how BlinkEye's script filters.

----------

## El Tazar

Thanks alot BlinkEye, for the fine nifty little script and for the detailed howto. Been searching for something simple the last few months.

----------

## MatrixM

 *Andersson wrote:*   

> Anyway, to make this script work for me, I'll have to either change the sshd log verbosity somehow, or change the regexp again to something like this: (And also accept that a root attempt will simply fail, but not result in a blacklisting.)
> 
> ```
> SYSLOG_REGEX = r"sshd[[][0-9]+[]]: Invalid user (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
> ```
> ...

 

Hey Anderson, have you this modification to the REGEX, and did it work for you? I'm in about the same boat you are with regards to getting a large number of invalid attempts from people not in the allowed_user list.

Also, does anyone know if it'd be possible to have the REGEX work on two seperate criteria? My log files have two distinct types of users hitting the box. One is in the form listed above from Anderson, the other is in the form of

```
shd[[][0-9]+[]]: User (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3}) not allowed because not listed in AllowedUsers
```

So does anyone know if it'd be possible to cause the script to look for both expressions for blocking at the same time? I'll admit, my coding skills are somewhat lacking (read, none) so the line I cobbled together could be wrong in it's form even.

----------

## Vlad

Just wanted to pipe in that I have the same problem as MatrixM.   All the attacks appear as

```

Feb  6 06:16:02 alpha sshd[7613]: User rpc from 200.47.112.149 not allowed becau

se not listed in AllowUsers

```

in my log files, and they are not 'caught' by blacklist.py.  Having support for both those regexs would be awesome. :)

----------

## Andersson

Hi. Yes, I changed the regexp to what I wrote above. It works. I also get a mix of different lines in the logs:

```
Feb  1 16:30:51 clint sshd[21640]: User guest not allowed because shell /dev/null is not executable

Feb  3 23:18:53 clint sshd[30378]: Invalid user recruit from 69.72.212.18

Feb  3 23:18:54 clint sshd[30383]: User alias from 69.72.212.18 not allowed because not listed in AllowUsers
```

The first doesn't contain an ip address, so it's useless for blocking. Since all the attacks I've seen have the line "Invalid user..." appear the most, it's enough to block them.

In addition to these two, there are the lines that the script originally looks for.

```
Jan  2 21:48:05 blinkeye sshd[4529]: Failed password for invalid user sato from 61.172.192.3 port 54177 ssh2

Sep 18 05:08:06 blinkeye sshd[3971]: Failed keyboard-interactive/pam for root from 152.149.148.115 port 44896 ssh2
```

It seems this script could use a way to specify more than one regexp. One way to do that is (pattern1|pattern2), but then we have to change the way it catches the ip number and user name. Perhaps a better way is changing 

```
SYSLOG_REGEX = r"sshd[[][0-9]+[]]: Invalid user (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
```

to

```
SYSLOG_REGEX1 = r"sshd[[][0-9]+[]]: Invalid user (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"

SYSLOG_REGEX2 = r"sshd[[][0-9]+[]]: User (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3}) not allowed because not listed in AllowUsers"
```

and change the function def.scan from:

```
def scan():

    global countdown

    # compile regex for the logger

    re_invalid = re.compile( SYSLOG_REGEX )

    regex_matches = re_invalid.findall( system_command( LOGTAIL + " -f" + LOG_INPUT ) )

    create_stat( regex_matches )

    ...
```

to something like this: 

```
def scan():

    global countdown

    # compile regex for the logger

    re_invalid1 = re.compile( SYSLOG_REGEX1 )

    re_invalid2 = re.compile( SYSLOG_REGEX2 )

    log = system_command( LOGTAIL + " -f" + LOG_INPUT )

    regex_matches = re_invalid1.findall( log )

    regex_matches += re_invalid2.findall( log )

    create_stat( regex_matches )

    ...
```

I have not tested this in any way, I'm not even sure what I'm adding. Arrays? Does it need some other syntax? But it's a start if you feel like some programming tonight. (I don't)  :Razz: 

----------

## MatrixM

I'll take some stabs at it over the next couple days and report back what I discover from it's results. It will take a few days for me to get my results posted due to school and work.

Also, I did a quick grep on the script for simply REGEX (using case insensitive search), and there are three total lines in the script. I'll post more data once I've done some testing at home overnight.

So what I'll try doing is imlementing the suggested code changes from Andersson regarding multiple regex lines, and then altering the def.scan function to include the additional regex code searches (at least in theory).

If this works, then I'll toy with adding a third regex to catch the "Failed keyboard-interactive/pam" type messages in the log as well (although in my case, these show up exceedingly seldom that it may not be an issue for me).

[Edit]

Ok, So I tried making the changes as were discussed, and it gives me the following errors:

```

tammy ~ # ./blacklist.py

Traceback (most recent call last):

  File "./blacklist.py", line 219, in ?

    scan()

  File "./blacklist.py", line 140, in scan

    log = system_command( LOGTAIL + " -f" + LOG_INPUT )

  File "./blacklist.py", line 92, in system_command

    raise IOError( return_value[ 1 ] )

IOError: File -f/var/log/auth.log cannot be read.

tammy ~ #

```

I'm not sure if I did something wrong, or if we just haven't got the proper coding for multiple REGEX searches figured out.

The interesting thing is that it appears to be complaining about the LOGTAIL execution ability, yet we know that this works when it's by itself. I even tried replacing

```
log = system_command ( LOGTAIL + " -f" + LOG_INPUT )
```

 from the propsed modification and instead added the system_command part directly to the regex_matches lines. Same errors about the ( LOGTAIL + " -f" + LOGINPUT ) IOError. So I'm thinking that there's more going on here than we seem to realize.

I did also change the original script to merely search for the Invalid user so that it appears to be getting some of the people who are hitting my machine now at least.

I also remember there being multiple re_invlaid = re.compile (SYSLOG_REGEX) lines in the script. Maybe over the weekend I can toy with those and see what comes up as well.

[/edit]

----------

## Andersson

The logtail error, could it be the same I had a few pages up? You may have the wrong version of logtail. Try either replacing the "-f" with a single space, or download the other version. See the first few posts in this thread. And keep us updated.  :Smile: 

----------

## BlinkEye

if you need to get in touch with me - please write me a private message/email. i don't get any topic-reply-notifications lately   :Embarassed: 

----------

## JloR

Woah, that's huge! Thanks a lot - I am getting sick and tired of all those false positives.

Much needed :)

----------

## pteppic

I seem to get variants of

```
Feb 14 02:04:09 holly sshd[27554]: Invalid user for dominique from 209.97.205.xx

Feb 15 21:54:58 admin sshd[14191]: error: PAM: Authentication failure for root from 192.168.1.1

Sep 18 05:08:06 blinkeye sshd[3971]: Failed keyboard-interactive/pam for root from 152.149.148.115 port 44896 ssh2
```

in my logfile, the regex I managed to cobble together to catch all this is

```
r"(?:error:|Failed|Invalid) (?:user|PAM: Authentication failure|password|keyboard-interactive/pam) (?:none|invalid user)*(?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
```

Can someone tell me if there is anything wrong with this, it appears to work fine, but...

Also if no one shoots it down in flames, it's sshd with PAM or RSA key auth only.

----------

## castrik

Does anyone know how to make this into a daemon, so it can be run from startup and without an open terminal?

----------

## pteppic

 *castrik wrote:*   

> Does anyone know how to make this into a daemon, so it can be run from startup and without an open terminal?

 

Add 

```
/usr/bin/blacklist.py &
```

or whatever the path you saved it to, to /etc/conf.d/local.start and make sure you have local in the runlevels you want it to run in.

----------

## Andersson

 *castrik wrote:*   

> Does anyone know how to make this into a daemon, so it can be run from startup and without an open terminal?

 

If you use a firewall, you could run into trouble. Since the script adds new iptables rules (the chain "BLACKLIST"), starting a firewall after the script might erase this chain. However, I think local.start is run after all the initscripts run. You might still need to start the script again if you restart the firewall. If you happen to use shorewall as your firewall, you can add the following lines to /etc/shorewall/start to make it work nice together with the firewall. Replace the path on the last line with your own.

```
killall blacklist.py

/root/scripts/blacklist.py &
```

(The first line is to make sure only one copy is running, if you restart the firewall for a new configuration.)

----------

## guero61

 *spengy wrote:*   

> I use pure iptables
> 
> ```
> 
> iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set --name SSH -j ACCEPT
> ...

 

Even better than DROP, use the TARPIT target - from the iptables manual:

```

   TARPIT

       Captures and holds incoming TCP connections using no local per-connection resources. Connections are accepted,

       but immediately switched to the persist state (0 byte window), in which the remote side stops sending data and

       asks  to continue every 60-240 seconds.  Attempts to close the connection are ignored, forcing the remote side

       to time out the connection in 12-24 minutes.

       This offers similar functionality to LaBrea <http://www.hackbusters.net/LaBrea/> but doesn't require dedicated

       hardware or IPs. Any TCP port that you would normally DROP or REJECT can instead become a tarpit.

       To tarpit connections to TCP port 80 destined for the current machine:

              iptables -A INPUT -p tcp -m tcp --dport 80 -j TARPIT

       To  significantly slow down Code Red/Nimda-style scans of unused address space, forward unused ip addresses to

       a Linux box not acting as a router (e.g. "ip route 10.0.0.0 255.0.0.0 ip.of.linux.box" on a Cisco), enable  IP

       forwarding on the Linux box, and add:

              iptables -A FORWARD -p tcp -j TARPIT

              iptables -A FORWARD -j DROP

       NOTE:  If  you use the conntrack module while you are using TARPIT, you should also use the NOTRACK target, or

              the kernel will unnecessarily allocate resources for each TARPITted connection. To TARPIT incoming con-

              nections to the standard IRC port while using conntrack, you could:

              iptables -t raw -A PREROUTING -p tcp --dport 6667 -j NOTRACK

              iptables -A INPUT -p tcp --dport 6667 -j TARPIT

```

Also, just to be safe I typically do something of the following after my "X in N minutes" rule, both to limit my memory usage and let myself back in (after 45 minutes) if I goof:

```

iptables -A ANTI_L337 -p tcp --dport 22 -m recent --rcheck ! --seconds 2700 --name SSH

iptables -A ANTI_L337 -p tcp --dport 22 -m recent --remove --name SSH -j ACCEPT

```

Of course, I never tested it, so I'm not even certain it works properly...

----------

## MatrixM

 *Andersson wrote:*   

> The logtail error, could it be the same I had a few pages up? You may have the wrong version of logtail. Try either replacing the "-f" with a single space, or download the other version. See the first few posts in this thread. And keep us updated. 

 

It seems to work fine when it's only trying to catch one regex, it's when I try to modify the script to catch multiple regex's that I get the logtail errors (I'll look into the logtail version I have on the system though to see if that may be the issue at stake or not).

----------

## carneboy

thank you so much, this was very easy to setup

i suggest adding to /etc/init.d/iptables (this works good for me)

under start():

```
blacklist.py > /dev/null &
```

under stop():

```
kill `pgrep blacklist.py`
```

----------

## nahpets

Really cool looking program/script.  Is this compatible with Shorewall?  I have Shorewall blocking all ports except for 22 and I've disabled password and root login.  My box is pretty secure but I'd like to use your program with my current setup cause I guess I'm paranoid   :Shocked: 

----------

## Andersson

 *nahpets wrote:*   

> Is this compatible with Shorewall?

 

Yes, it is. Although you may have to restart the script if you restart shorewall. See my previous post.

----------

## Vlad

I've been trying to follow this thread, but it seems that regex matching drops from AllowUsers still hasn't been implemented.  Is there any chance this can be added to the script?  If so, could someone explain how (or better yet, simply paste/link the script)?

This script would be great if it'd just catch those AllowUsers lines.

Vlad

----------

## spindle

 *carneboy wrote:*   

> thank you so much, this was very easy to setup
> 
> i suggest adding to /etc/init.d/iptables (this works good for me)
> 
> under start():
> ...

 

This inspired me to take this a step further and I wrote up a separate init script for this,

/etc/init.d/blacklist

```
#!/sbin/runscript

# Distributed under the terms of the GNU General Public License v2

# Copyright 2006 spindle aka Mike Nelson

#

# Refer to forum post: http://forums.gentoo.org/viewtopic-p-3141510.html#3141510

# Thanks to: blinkeye aka Reto Glauser for the blacklist.py script and

# carneboy for the inspiration for this script

#

# Date: 2006-02-26

# Version 0.1

# you may want to uncomment the below if using iptables in rc-update, but

# it is probably not necessary

#depend() {

#       use iptables

#}

start() {

        ebegin "Starting blacklist.py"

        start-stop-daemon --start --quiet --background --exec /usr/sbin/blacklist.py

        eend $?

}

stop() {

        ebegin "Stopping blacklist.py"

        start-stop-daemon --stop --quiet --name blacklist.py

        eend $?

}
```

Copy the above into a file named /etc/init.d/blacklist. Then,

```
# chmod 755 /etc/init.d/blacklist
```

This script assumes that you put the blacklist.py script in /usr/sbin and that it's executable. You can do that by,

```
cp blacklist.py /usr/sbin

chmod a+x /usr/sbin/blacklist.py
```

To add to default runlevel, so it starts up automatically,

```
# rc-update add blacklist default
```

Then you can start stop like other init scripts,

```
# /etc/init.d/blacklist start

# /etc/init.d/blacklist stop

# /etc/init.d/blacklist restart
```

blacklist.py is a sweet little script, thanks Blinkeye!

----------

## BlinkEye

 :Embarassed: 

I don't get any more topic replies. I just pure luck I stumbled on my own thread  :Shocked: . thanks for all the input. I'll release a new version soon where I'll take into account your suggestions and features. Some (like the issue where the script is already running and you rerun it or where you're iptable rules for the script have been flushed) have been already implemented and now thoroughly tested. What I want (which on the other hand joines other issues you brought up) is to have several regex lines (in particular for me a ftp regex). I get a lot of login tries there too. When this logic is implemented it will be very easy for anyone to add a new regex.

----------

## Andersson

I'm looking forward to the update. I get more and more login attempts every day. If you want me to email you every time I see a new reply here, just ask!  :Wink: 

I saw a link to this script in another thread: http://www.csc.liv.ac.uk/~greg/sshdfilter/

It is similar to yours, but it has one neat feature. I quote from the web page: "sshdfilter starts sshd itself, having started sshd with the -e and -D options. This means it can see events as they happen."

Very cool. You think we can have that as well? However, that would make my other suggestion a lot harder: support for multiple log files. That way you could block people abusing your web server as well.  :Smile: 

----------

## BlinkEye

New version 0.4.2 is out (other changes will follow):

2006-03-03

    * Added new function handlepid() to check if an instance is already running (thanks to Erik J.)

    * Added try/except block to handle the issue if iptables get flushed while the script is running

    * Added try/except block to handle the different logtail versions

    * Fixed an issue where wrong entries would be written to the LOG_OUTPUT (modifying the hash table while iterating through it without making a copy)

    * Minor speed improvement

So, you may safely try to start the script several times (it won't work   :Wink:  ) and safely flush/restart your iptable rules. The script will (re)add the necessary iptable rules the next time it will block an IP.

----------

## MrBlc

Great script, but.. i was wondering about the same as Andersson mentioned...

in my auth.log file i get attempts like these:

```

Mar  6 03:47:53 freya proftpd[3773]: freya (219-81-19-30.static.tfn.net.tw[219.81.19.30]) - USER Administrator: no such user found from 219-81-19-30.static.tfn.net.tw [219.81.19.30] to 69.60.118.95:21

Mar  6 03:47:53 freya proftpd[3773]: freya (219-81-19-30.static.tfn.net.tw[219.81.19.30]) - USER Administrator: no such user found from 219-81-19-30.static.tfn.net.tw [219.81.19.30] to 69.60.118.95:21

Mar  6 03:47:54 freya proftpd[3773]: freya (219-81-19-30.static.tfn.net.tw[219.81.19.30]) - USER Administrator: no such user found from 219-81-19-30.static.tfn.net.tw [219.81.19.30] to 69.60.118.95:21

Mar  6 03:47:54 freya proftpd[3773]: freya (219-81-19-30.static.tfn.net.tw[219.81.19.30]) - Maximum login attempts (3) exceeded

```

and that is repeated over and over again.. 

I would like it if there was a option to add a regex to cover that as well.. 

fortunately, my proftpd is using sql backend so no real users exist on it, but still.. i have A LOT of attempts on that, and blocking them would save me the headache of wondering wether or not they sooner or later manage to break into an account..

-blc

----------

## pteppic

 *Andersson wrote:*   

> [snip] support for multiple log files. That way you could block people abusing your web server as well. 

 

I can see two ways of doing this; Setup syslog-ng to filter all transactions you want scrutinised to a specific log file, and write the regex to cope.

OR setup the logfile variable(s) as a list object, with the index pointing to the relavent regex stored in a dictionary object, then run the check function as a 'for in' loop.

I would tinker with this myself, but as the project is still very much under development I have no wish to be stepping on toes, if this however is not the case, let me know and I'll tinker away....

----------

## MatrixM

(crossposted to actual forum thread as well as PM to BlinkEye with log dumps trimmed for forum post)

Sorry for such a long forum post, just trying to help get in the development of the script as best I can since my coding skills are lacking.

I'm still getting people that are trying to use the root account to login not being blocked. A prime example of this is seen in the below output of my auth.log file. A single site was hammering my system for over 15 minutes trying to get in on the root account. Not only did it not block the account for trying to use root to login, but it also didn't block the account out after 30 seconds of hammering the system.

The only change I had to make to the script for my system was to point it to the proper location of logtail (/usr/bin/logtail instead of /usr/sbin/logtail). I don't believe that change would have disabled the blocking of the root login attempts.

I did a quick grep of my logs just to check to see if any of the non-AllowUsers attempts are being blocked, and it doesn't appear that they are. So I'm including two different grep outputs here, the first is from a grep of just root attempts on the server. The second will be a grep of AllowUsers with the root attempts stripped out via sed (at the very bottom of the root listing).

Hope that the log outputs can help in further development of the script.

 *grep root /var/log/auth.log wrote:*   

> 
> 
> Mar  7 02:37:29 tammy sshd[8559]: User root from 61.178.20.170 not allowed because not listed in AllowUsers
> 
> Mar  7 02:37:31 tammy sshd[8561]: User root from 61.178.20.170 not allowed because not listed in AllowUsers
> ...

 

 *grep AllowUsers /var/log/messages | sed -e '/root/d' wrote:*   

> 
> 
> Mar  7 14:26:43 tammy sshd[15175]: User alias from 218.71.137.69 not allowed because not listed in AllowUsers
> 
> Mar  7 14:26:57 tammy sshd[15189]: User cyrus from 218.71.137.69 not allowed because not listed in AllowUsers
> ...

 

----------

## Andersson

MatrixM: Are all the log lines on the form "user X from Y not allowed because not listed in AllowUsers"? If so, is it the syntax of a regexp for cathing these you are having problems with? Something else? Which regexp are you using in the script right now?

Also, some of the hosts in your log are ip numbers, other are domain names. They should all be ip numbers for the script to work. Do you have that "useDNS" setting enabled or disbled in your sshd config?

----------

## MatrixM

I just checked the sshd_config file, and the useDNS option was commented out, not sure what it's default setting is, but I'll assume that it's on since I'm getting DNS lookups in the auth.log file. I went ahead and changed it to useDNS no and uncommented the section (and remembered to /etc/init.d/sshd restart as well).

The "User X from Y not allowed because not listed in AllowUsers" is the one string that's not being blocked by the script right now. I'm guessing that is why the root attempts aren't being blocked either since they don't match the original REGEX search pattern as well.

I really like this script, I just wish there was some way to get it to ban people who were trying invalid accounts as well (since there are only two accounts not blocked, I wonder why some are even being blocked at all).

I just don't know enough about regex to alter the existing line so that it will search for it's existing stuff as well as the AllowUsers listings (and thus still catch the root attempts like the script seems to supposed to be doing).

----------

## Andersson

Well, blinkeye promised to support multiple regular expressions in the next version, so I guess you have to wait. Or, you could always run two instances of the script.  :Very Happy: 

I love a quick and dirty fix, so I had to try this immediately. It seems to be working, and I don't see why it shouldn't. So, copy the script to a new file, and make some modifications: You need to change the name of the iptables chain, and it needs a separate offset file for logtail. And, just in case, I gave it a separate log file. It's a few lines you need to change. Actually, it's probably more lines than needed to add support for multiple regexps. But less testing is needed.  :Wink: 

Add the "2" to these lines:

```
LOG_OUTPUT = "/var/log/blacklist2.log"

PID_FILE = "/var/run/blacklist2.pid"

CUSTOM_CHAIN = "BLACKLIST2"
```

and add this line in the same section (feel free to use another file, but keep the space character inside the quotes):

```
LOG_OFFSET = " /root/scripts/.blacklist2_offset"
```

and change the regexp you're using to this:

```
SYSLOG_REGEX = r"sshd[[][0-9]+[]]: User (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3}) not allowed because not listed in AllowUsers"
```

And add the offset file to these two lines (in the scan function at around line 150, they also appear in the test mode around line 220, but you can skip them):

```
              regex_matches = re_invalid.findall( system_command( LOGTAIL + " -f " + LOG_INPUT + LOG_OFFSET ) )
```

and

```
              regex_matches = re_invalid.findall( system_command( LOGTAIL + " " + LOG_INPUT + LOG_OFFSET) )
```

(Don't cut and past here, mind the indentation. Python is tricky like that.)

Oh, and the syntax for a specific offset file might actually be different if you use the "-f" version of logtail. Try to type "logtail" and see what it says.

----------

## BlinkEye

I'm currently testing a new version (allowing multiple regex for both ssh and ftp). I just wonder why you only get similar lines to

```
Mar 8 01:11:39 tammy sshd[630]: User root from 72.36.231.146 not allowed because not listed in AllowUsers
```

...

If I explicitly specify which users to allow in sshd_config I get those lines too BUT along with the usual entries.

The lines for one attempt:

```
Mar 13 22:08:01 blinkeye sshd[19817]: User test from 10.10.10.3 not allowed because not listed in AllowUsers

Mar 13 22:08:01 blinkeye sshd[19817]: Failed none for invalid user test from 10.10.10.3 port 60991 ssh2

Mar 13 22:08:07 blinkeye sshd(pam_unix)[19822]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=10.10.10.3  user=test

Mar 13 22:08:10 blinkeye sshd[19817]: error: PAM: Authentication failure for illegal user test from 10.10.10.3

Mar 13 22:08:10 blinkeye sshd[19817]: Failed keyboard-interactive/pam for invalid user test from 10.10.10.3 port 60991 ssh2
```

are you really sure you don't get the usual "Failed keyboard-interacitve/pam ..." lines too?

----------

## Andersson

I get more lines if I try to log in manually:

```
Mar 11 03:18:33 clint sshd[8894]: Invalid user test from 193.27.xx.xx

Mar 11 03:18:35 clint sshd(pam_unix)[8900]: check pass; user unknown

Mar 11 03:18:35 clint sshd(pam_unix)[8900]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=193.27.xx.xx

Mar 11 03:18:37 clint sshd[8894]: error: PAM: Authentication failure for illegal user test from 193.27.xx.xx

Mar 11 03:18:37 clint sshd[8894]: Failed keyboard-interactive/pam for invalid user test from 193.27.xx.xx port 2263 ssh2
```

But the attempts coming from the script kiddies look like this:

```
Mar 12 15:55:54 clint sshd[5841]: Did not receive identification string from 83.248.169.252

Mar 12 16:04:01 clint sshd[6122]: User root from 83.248.169.252 not allowed because not listed in AllowUsers

Mar 12 16:04:11 clint sshd[6136]: Invalid user admin from 83.248.169.252

Mar 12 16:04:18 clint sshd[6142]: Invalid user test from 83.248.169.252

Mar 12 16:04:24 clint sshd[6149]: User guest not allowed because shell /dev/null is not executable

Mar 12 16:04:30 clint sshd[6155]: Invalid user webmaster from 83.248.169.252

Mar 12 16:06:25 clint sshd[6155]: fatal: Timeout before authentication for 83.248.169.252
```

----------

## kill[h]er

Just a quick suggestion.

Would be nice if the email subjects contained the hostname of the station that is triggering the email.

very nice work, though.  i just started using this and the backup script you wrote.  love em both!

----------

## kill[h]er

so, i got curious and wanted to add the hostname myself...

i'm not python programmer, so if i'm screwing this up or if there's an easier way let me know...

But to add the hostname to the subject line of the email, this is what I did.

after the comments section, in the "import" section, add the following line:

```
import socket;
```

in the variables area, add the following line:

```
HOST_NAME = socket.gethostname()
```

that'll give us the variable with the hostname in it.  down in the section labeled "# mail list of IP blocked during this run", add the variable by changing this:

```
             p.write( "Subject: Too many SSH login failures from multiple IPs\n\n" )

        else:

                p.write( "Subject: Too many SSH login failures from " + ip + "\n\n" )

```

to this:

```
             p.write( "Subject: " + HOST_NAME + ": Too many SSH login failures from multiple IPs\n\n" )

        else:

                p.write( "Subject: " + HOST_NAME + ": Too many SSH login failures from " + ip + "\n\n" )

```

that should do it.  now the emails will have the hostname in the subject line.

----------

## Robert S

Nice little script.  I had to get rid of the following lines in my /etc/ssh/sshd_config:

 *Quote:*   

> AllowGroups ssh_users

 

.. and had to add the following:

 *Quote:*   

> UsePAM no
> 
> UseDNS no

 

It would be nice if the regex was changed to allow "AllowGroups" and "UsePAM" to be set as an additional security measure.   Any regex masters out there?

Also I had to add a space in SYSLOG_REGEX after ?:invalid user thus:

 *Quote:*   

> SYSLOG_REGEX = r"Failed (?:none|password|keyboard-interactive/pam) for (?:invalid user )*(?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3
> 
> }\d{1,3})"

 

----------

## BlinkEye

well guys, you don't give me much time to test  :Wink: 

thanks for the feedback so far.

version 0.4.4 is out. 

recent changes:

'''2006-03-15'''

* Added new functionality to permit several regexs at once

* Added ftp regex

* Changed subject of the status mail to contain the hostname

* The mail now contains the port an IP is blocked on

* Correctly blocking out invalid users/login tries if the ssh daemon is configured with the "AllowUsers" variable

* Enhanced the test modus to support several loglines

----------

## BlinkEye

I'm not updating the wiki for another 16 hours or so. give it some time - the script section and the ftp download are up-to-date. 

Please use the test modus/function to see if the (new) regexs are working for you. You may now easily add another regex to SSH_REGEX or FTP_REGEX (comma seperated list) - although I don't think it's necessary.

----------

## carneboy

Thanks once again, it now works on the "script-kiddie" log lines, that constitute 90% of my auth.log.   :Very Happy: 

----------

## PabOu

Nice script and howto ;)

But what about fail2ban ?

----------

## kill[h]er

Ok, I haven't upgraded to the latest version which was released last night, so if this is fixed in the new version just slap me upside the head.

Woke up this morning and had no emails from the blacklist script.  The blacklist log file reported nobody being banned overnight.  But looking at /var/log/auth.log, this was in there (en masse):

```
Mar 15 07:04:02 setii sshd[31699]: Invalid user teen from 201.6.252.173

Mar 15 07:04:02 setii sshd[31699]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:07 setii sshd[31705]: Invalid user theo from 201.6.252.173

Mar 15 07:04:07 setii sshd[31705]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:12 setii sshd[31707]: Invalid user teo from 201.6.252.173

Mar 15 07:04:12 setii sshd[31707]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:16 setii sshd[31709]: Invalid user theodora from 201.6.252.173

Mar 15 07:04:16 setii sshd[31709]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:21 setii sshd[31711]: Invalid user theodore from 201.6.252.173

Mar 15 07:04:21 setii sshd[31711]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:26 setii sshd[31713]: Invalid user valentin from 201.6.252.173

Mar 15 07:04:26 setii sshd[31713]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:31 setii sshd[31715]: Invalid user vali from 201.6.252.173

Mar 15 07:04:31 setii sshd[31715]: reverse mapping checking getaddrinfo for c906fcad.static.spo.virtua.com.br failed - POSSIBLE BREAKIN ATTEMPT!

Mar 15 07:04:36 setii sshd[31721]: Invalid user anna from 201.6.252.173

```

Why wasn't blacklist banning this guy?

----------

## BlinkEye

 *kill[h]er wrote:*   

> Why wasn't blacklist banning this guy?

 

because the guy uses a script which somehow does not generate ordinary loglines - in my opinion the script doesn't even try to authenticate .... 

it's fixed now anyway.

----------

## kill[h]er

cool, i'll be upgrading to the new version today... figgered it'd be fixed there.  just got worried.

----------

## Robert S

The new version works fine now except I need to use the perl logtail script.  Is there any chance of getting it to work with the binary version of logtail that comes with the logsentry package?  I assume that being a binary it would put less load on my system than a perl script.  I've been able to restore my sshd_config to its original form.

Also - perhaps the .pid file could be cleaned up on exit   :Wink: 

An excellent piece of work!

----------

## evermind

nice howto BlinkEye

----------

## BlinkEye

I made some minor updates to the wiki. You may be interested in the advanced test modus section.

----------

## kill[h]er

Disclaimer - When reading this, keep these things in mind:

1) I'm not a Python expert, I'm a Perl man.  So there might be better ways of doing what I'm doing, python-wise.

2) I'm not an ssh expert, so the string below might actually be used for something useful, so if you use this change, you might block out such use.  Personally, I don't care, because the only people who should be using SSH on my server(s) shouldn't be nmap'ing or scanssh'ing me.

3) I'm not implying that Blinkeye should add this to his script, but if he wants to, he's free to!

------------

Ok, I've been watching this stuff pretty closely (this stuff being the ssh bruteforce attempts), and Blinkeye's Blacklist script is working great... but I've noticed that each bruteforce attempt by these kiddies is preceded by this:

```
Mar 22 02:07:46 setii sshd[6383]: Did not receive identification string from 202.28.25.133
```

This is (from all that i'm aware of) from nmap scans or scanssh with the -I option.  After seeing this, the bruteforce attempts start about 3-5 mins later.  So I decided to add an immediate block to anybody doing such a scan.  I haven't found any legitimate use of ssh that causes that string to come across, so I use it to block any bozos trying to scan me.  Here's the changes I made:

Change the SSH_REGEX section:

```

SSH_REGEX =     [                                       

                                                        r"Failed (?:none|password|keyboard-interactive/pam) for (?:invalid user )*(?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",

                                                        r"Invalid user (?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",

                                                        r"Did not receive (?P<user>.*) string from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"

                             ]

```

In the create_stat subroutine, add another if statement to check the user name (in this case we're using the term "identification" as a user name):

```
                # no tolerance for a root login attempt

                if ( match.group( 'user' ) == "root" ):

                        entry[ 1 ] += PERMITTED_LOGIN_FAILURES

                if ( match.group( 'user' ) == "identification" ):

                        entry[ 1 ] += PERMITTED_LOGIN_FAILURES  

```

And that's it.  As soon as some idiot tries to nmap or scanssh -I you, they'll be blocked instantly for 10 minutes.  This will cause their bruteforce scripts to fail, even though they initially get a response from you.

----------

## codejnki

 *kill[h]er wrote:*   

> 
> 
> ```
> Mar 22 02:07:46 setii sshd[6383]: Did not receive identification string from 202.28.25.133
> ```
> ...

 

I was wondering what those lines were.  That makes a lot of sense though.

----------

## BlinkEye

 *codejnki wrote:*   

>  *kill[h]er wrote:*   
> 
> ```
> Mar 22 02:07:46 setii sshd[6383]: Did not receive identification string from 202.28.25.133
> ```
> ...

 

So was I. I'll investigate that. Thanks so far.

----------

## MatrixM

 *Robert S wrote:*   

> The new version works fine now except I need to use the perl logtail script.  Is there any chance of getting it to work with the binary version of logtail that comes with the logsentry package?  I assume that being a binary it would put less load on my system than a perl script.  I've been able to restore my sshd_config to its original form.

 

I've got it working with the logsentry logtail with no problems. The only thing I had to change was the location of logtail line in the script:

```

LOGTAIL = "/usr/bin/logtail"

```

instead of 

```

LOGTAIL = "/usr/sbin/logtail"

```

With that one change, I don't get any problems stemming from logtail on my system. At least, I don't think I've downloaded the logtail perl script and installed it into /usr/bin on my system.

----------

## kill[h]er

Just wanted to update yall... I added the nmap/scanssh blocking to my server's script 2.5 days ago.  Since then I've had at least 10 "scans" that resulted in the immediate blocking of their IPs.  I have had zero "bruteforce" attempts.  This is looking promising.

(Not saying to remove the other checks, just that this one seems to be cutting them off at the knees.)

----------

## urcindalo

Hi!

I got blacklist.py installed and activated (as an init script ran after iptables). The thing is today I've seen this in my /var/log/auth.log

```
Mar 27 23:17:52 machine-name sshd[14107]: Did not receive identification string from 69.70.19.226

Mar 27 23:21:39 machine-name sshd[14157]: Invalid user admin from 69.70.19.226

Mar 27 23:21:41 machine-name sshd[14159]: Invalid user test from 69.70.19.226

Mar 27 23:21:42 machine-name sshd[14161]: User guest not allowed because shell /dev/null is not executable

Mar 27 23:21:44 machine-name sshd[14163]: Invalid user webmaster from 69.70.19.226

Mar 27 23:21:47 machine-name sshd[14167]: Invalid user oracle from 69.70.19.226
```

Since blacklist.py is configured this way

```
PERMITTED_LOGIN_FAILURES = 3
```

I assume blacklist only triggers ip blocking when a valid user tries to log in with an invalid password, but not when a non-existent user tries to log in, right? I say this because I've seen no messages from blacklist blocking the 69.70.19.226 ip

So, are fail2ban and blacklist.py mutually exclusive? Or can I have both activated at the same time? I'd like to get rid also of those scans for valid users...   :Evil or Very Mad: 

----------

## BlinkEye

You're using an old version. New versions (>=0.4.4) support catching of invalid users. To make sure have a look at the TestMode .

----------

## urcindalo

Nope. Version is 0.4.5:

```
# cat blacklist.py | grep Version

# Version 0.4.5

```

There must be something else. This is the output after test mode:

```
* Entering test mode

* SSH_REGEX[ 0 ]: Caught ip "61.172.192.3 and username "sato"

* SSH_REGEX[ 0 ]: Caught ip "61.172.192.3 and username "sato"

* SSH_REGEX[ 0 ]: Caught ip "152.149.148.115 and username "root"

* SSH_REGEX[ 0 ]: Caught ip "152.149.148.115 and username "root"

* SSH_REGEX[ 0 ]: Caught ip "152.149.148.115 and username "root"

* SSH_REGEX[ 0 ]: Caught ip "152.149.148.115 and username "root"

* SSH_REGEX[ 1 ]: No match found

* FTP_REGEX[ 0 ]: Caught ip "206.222.29.194" and username "root"

* FTP_REGEX[ 0 ]: Caught ip "206.222.29.194" and username "root"

* SUCCESS: Sending mail from blacklist@localhost to root@localhost
```

The mail says "A test mail from blacklist.py". To me, it looks like test mode is working.

What else can I check?

----------

## kill[h]er

check and see if your iptables is being updated.

```
iptables -v -L
```

----------

## VelVet

I might be stupid :p but i just started using linux and i found this verry usefull,

However when i try to start the script i get

```
Traceback (most recent call last):

  File "./blacklist.py", line 227, in ?

    raise IOError, LOGTAIL + " is not executable"

IOError: /usr/sbin/logtail is not executable

```

though i emerged logsentry anny suggestions or help would be welcome  :Smile: 

----------

## VelVet

never mind me, i found it

had to change 

```
LOGTAIL = "/usr/sbin/logtail" 

back to 

LOGTAIL = "/usr/bin/logtail"

```

----------

## urcindalo

My iptables:

```
# iptables -v -L

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)

 pkts bytes target     prot opt in     out     source               destination

13436 1508K ACCEPT     all  --  any    any     localhost            anywhere

    0     0 ACCEPT     all  --  any    any     80-103-114-34.mad1.adsl.uni2.es  anywhere

   65  9912 ACCEPT     all  --  any    any     150.214.212.13       anywhere

 3669 3353K ACCEPT     all  --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpt:ssh

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpt:ftp-data

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpt:ftp

    2    96 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpts:netbios-ns:netbios-ssn

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpt:426

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpt:microsoft-ds

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpts:1417:1420

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpts:5900:5902

    0     0 ACCEPT     udp  --  any    any     anywhere             anywhere            state NEW udp dpts:5900:5902

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpts:5800:5802

    0     0 ACCEPT     udp  --  any    any     anywhere             anywhere            state NEW udp dpts:5800:5802

    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            state NEW tcp dpts:5500:5502

    0     0 ACCEPT     udp  --  any    any     anywhere             anywhere            state NEW udp dpts:5500:5502

 5335  616K REJECT     all  --  any    any     anywhere             anywhere            reject-with icmp-port-unreachable

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)

 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 16348 packets, 1744K bytes)

 pkts bytes target     prot opt in     out     source               destination

    0     0 ACCEPT     all  --  any    any     80-103-114-34.mad1.adsl.uni2.es  anywhere

    0     0 ACCEPT     all  --  any    any     150.214.212.13       anywhere
```

By the way, I see nothing when I run the command to get the list of tried login names indicated in the wiki:

```
machine log # grep "Failed" /var/log/auth.log | sed "s/.*for\( invalid user\)*\(.*\)\(from.*\)/\2/" | sort | uniq -c

machine log #
```

However they exist, as I mentioned in my previous post. Maybe there is a problem with the regex expressions and my auth.log output? But it is generated by syslog-ng...   :Confused: 

----------

## VelVet

never mind, got everything to work

great script  :Smile: 

----------

## urcindalo

New observations of my blacklist strange behaviour. Whenever I suffer an ssh attack, blacklist seems to stop running. I've checked this quite a few times. For instance, right now. I've just seen this in my /var/log/auth.log:

```
Apr  3 01:08:54 machine sshd[13008]: Invalid user {\\rtf1\\ansi\\ansicpg1252\\deff0{\\fonttbl{\\f0\\fswiss\\fcharset0 from 24.199.204.163

Apr  3 01:08:55 machine sshd[13010]: Invalid user {\\*\\generator from 24.199.204.163

Apr  3 01:08:56 machine sshd[13012]: Invalid user ak from 24.199.204.163

Apr  3 01:08:58 machine sshd[13014]: Invalid user asvn from 24.199.204.163

Apr  3 01:08:59 machine sshd[13016]: Invalid user atemp from 24.199.204.163

Apr  3 01:09:00 machine sshd[13018]: Invalid user aalyssa from 24.199.204.163

Apr  3 01:09:02 machine sshd[13020]: Invalid user amirion from 24.199.204.163

Apr  3 01:09:03 machine sshd[13022]: Invalid user azimbra from 24.199.204.163

Apr  3 01:09:13 machine sshd[13024]: Did not receive identification string from 24.199.204.163

Apr  3 04:46:44 machine sshd[13665]: Invalid user admin from 201.247.150.165

Apr  3 04:46:44 machine sshd[13665]: Invalid user admin from 201.247.150.165

Apr  3 04:40:04 machine sshd[13656]: Did not receive identification string from 201.247.150.165

Apr  3 04:46:29 machine sshd[13657]: Invalid user webmaster from 201.247.150.165

Apr  3 04:46:37 machine sshd[13661]: Invalid user ftp from 201.247.150.165

Apr  3 04:46:39 machine sshd[13663]: Invalid user sales from 201.247.150.165

Apr  3 04:46:44 machine sshd[13665]: Invalid user admin from 201.247.150.165

Apr  3 04:46:47 machine sshd[13667]: Invalid user andrea from 201.247.150.165

Apr  3 04:46:57 machine sshd[13669]: Did not receive identification string from 201.247.150.165

Apr  3 04:40:04 machine sshd[13656]: Did not receive identification string from 201.247.150.165

Apr  3 04:46:29 machine sshd[13657]: Invalid user webmaster from 201.247.150.165

Apr  3 04:46:37 machine sshd[13661]: Invalid user ftp from 201.247.150.165

Apr  3 04:46:39 machine sshd[13663]: Invalid user sales from 201.247.150.165

Apr  3 04:46:44 machine sshd[13665]: Invalid user admin from 201.247.150.165

Apr  3 04:46:47 machine sshd[13667]: Invalid user andrea from 201.247.150.165

Apr  3 04:46:57 machine sshd[13669]: Did not receive identification string from 201.247.150.165
```

Immediatly, after noticing this morning tonight's attack, I checked out if blacklist was running, but it was not. The following commands have been run in a row:

```
# ps aux | grep black

root     14579  0.0  0.0   4056   816 pts/1    S+   09:05   0:00 grep black

# /usr/local/bin/blacklist.py &

[1] 14583

# Removing stale pidfile /var/run/blacklist.pid with pid 1348

# ps aux | grep black

root     14583  0.3  0.3  18664  3936 pts/1    S    09:05   0:00 /usr/bin/python /usr/local/bin/blacklist.py

root     14595  0.0  0.0   4056   804 pts/1    R+   09:05   0:00 grep black

#
```

However, it was running last night, because I had to re-activate it after if de-activated following the attacks on Sunday morning. Why does blacklist.py quit itself when an attack happens? I understand nothing   :Confused: 

----------

## Andersson

urcindalo: Run in test mode and see if you get any error messages.

I had problems with it quitting also, for me it was that I had changed the regexp, but forgot to change <host> to <ip>.

----------

## urcindalo

Thanks for your help.

Nope, I got no problems with test mode. Just look at these commands in a row:

```
# ps aux | grep black

root     10045  0.0  0.3  18432  3856 ?        Ss   15:59   0:00 /usr/bin/python /usr/local/bin/blacklist.py

root     14398  0.0  0.0   4056   808 pts/5    R+   18:39   0:00 grep black

# /usr/local/bin/blacklist.py "Apr  3 01:08:56 machine sshd[13012]: Invalid user ak from 24.199.204.163"

* Entering test mode

* SSH_REGEX[ 0 ]: No match found

* SSH_REGEX[ 1 ]: Caught ip "24.199.204.163 and username "ak"

* FTP_REGEX[ 0 ]: No match found

* SUCCESS: Sending mail from blacklist@localhost to root@localhost

# /usr/local/bin/blacklist.py "Apr  3 04:46:44 machine sshd[13665]: Invalid user admin from 201.247.150.165"

* Entering test mode

* SSH_REGEX[ 0 ]: No match found

* SSH_REGEX[ 1 ]: Caught ip "201.247.150.165 and username "admin"

* FTP_REGEX[ 0 ]: No match found

* SUCCESS: Sending mail from blacklist@localhost to root@localhost

# ps aux | grep black

root     10045  0.0  0.3  18432  3856 ?        Ss   15:59   0:00 /usr/bin/python /usr/local/bin/blacklist.py

root     14435  0.0  0.0   4056   800 pts/5    R+   18:40   0:00 grep black

#
```

The examples are real attacks from my previous post. I didn't change the regex's in the script. However, I did change this:

```
...

LOGTAIL = "/usr/bin/logtail"

...

PERMITTED_LOGIN_FAILURES = 3

BLOCKING_PERIOD = 604800 #seconds

SUSPECTING_PERIOD = 86400 #seconds

...

DATE_FORMAT = "%Y.%M.%d %X" # e.g.: 02.01.2006 23:49:12 (I changed it from %d.%m.%Y)

...

...

                system_command( IPTABLES + " --insert INPUT 4 --jump " + CUSTOM_CHAIN )

...
```

Notice the "--insert INPUT 4 --jump". The reason is my iptables config is:

```
# iptables -L -n

Chain INPUT (policy ACCEPT)

target     prot opt source               destination

ACCEPT     all  --  127.0.0.1            0.0.0.0/0

ACCEPT     all  --  80.103.114.34        0.0.0.0/0

ACCEPT     all  --  150.214.212.13       0.0.0.0/0

ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED

...
```

and I want blacklist rules to be inserted after the rule for 150.214.212.13

----------

## kill[h]er

if you put it after related/established, that could knock your own existing ssh sessions out for 10 mins too, if you aren't careful.

----------

## urcindalo

 *kill[h]er wrote:*   

> if you put it after related/established, that could knock your own existing ssh sessions out for 10 mins too, if you aren't careful.

 

That's why I put it before that rule (intented as number 4), so that related/established will be number 5, unless I don't understand the syntax. Am I right or did I make a mistake?

----------

## BlinkEye

 *urcindalo wrote:*   

>  *kill[h]er wrote:*   if you put it after related/established, that could knock your own existing ssh sessions out for 10 mins too, if you aren't careful. 
> 
> That's why I put it before that rule (intented as number 4), so that related/established will be number 5, unless I don't understand the syntax. Am I right or did I make a mistake?

 

[EDIT]

Like kill[h]er said, you most likely want it to be at number 5. You can easily verify the behaviour by running blacklist.py and then making a couple of login attempts yourself. I suggest you run it (without moving it into the background) and then do some login failures and tell us what's happening. If it crashes you should see a debug output ...

----------

## kill[h]er

maybe i'm wrong, but i don't think so.

from what I know of iptables, it goes down the list and looks for a match.  if a match occurs, it executes the entry.

so if you get to the blacklist chain before you get to the established/related chain, then if your IP is in the blacklist chain as a drop, it will drop your connections and stop processing the chain (ie, it won't bother looking at established/related rule).

but like blinkeye said, try it out and let us all know...

----------

## urcindalo

 *BlinkEye wrote:*   

> Like kill[h]er said, you most likely want it to be at number 5. You can easily verify the behaviour by running blacklist.py and then making a couple of login attempts yourself. I suggest you run it (without moving it into the background) and then do some login failures and tell us what's happening. If it crashes you should see a debug output ...

 

OK. I changed it to number 5 and ran it in the foreground:

```
# ./blacklist.py

Removing stale pidfile /var/run/blacklist.pid with pid 15174
```

Then I tried to login remotely with a fake username from an OS X box:

```
$ ssh -l fakeuser mymachine.mydomain

Password:

Password:

Password:

Permission denied (publickey.keyboard-interactive).

$
```

And I saw blacklist in my Gentoo box failing this way:

```
# ./blacklist.py

Removing stale pidfile /var/run/blacklist.pid with pid 15174

Traceback (most recent call last):

  File "./blacklist.py", line 298, in ?

    scan()

  File "./blacklist.py", line 166, in scan

    create_stat( regex_matches, ssh_list, ssh_list_blocked, len( re_ssh.findall( new_log_entries ) )/100, SSH_PORT )

  File "./blacklist.py", line 150, in create_stat

    block( ip_list_blocked[ 0 ][ 0 ], BLOCKING_PERIOD + delay, port )

  File "./blacklist.py", line 98, in block

    system_command( IPTABLES + " --insert " + CUSTOM_CHAIN + " --source " + ip + " --protocol tcp --dport " + str( port ) + " --jump TARPIT" )

  File "./blacklist.py", line 87, in system_command

    raise IOError( return_value[ 1 ] )

IOError: iptables: No chain/target/match by that name

# ps aux | grep black

root     15323  0.0  0.0   4056   804 pts/2    R+   15:55   0:00 grep black

#
```

As you can see, it quit after the "IOError: iptables: No chain/target/match by that name" error.

How could I solve it? Again, my iptables are:

```
# iptables -L -n

Chain INPUT (policy ACCEPT)

target     prot opt source               destination

ACCEPT     all  --  127.0.0.1            0.0.0.0/0

ACCEPT     all  --  80.103.114.34        0.0.0.0/0

ACCEPT     all  --  150.214.212.13       0.0.0.0/0

ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:20

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:21

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:137:139

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:426

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:445

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:1417:1420

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:5900:5902

ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           state NEW udp dpts:5900:5902

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:5800:5802

ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           state NEW udp dpts:5800:5802

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:5500:5502

ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           state NEW udp dpts:5500:5502

REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-port-unreachable

Chain FORWARD (policy ACCEPT)

target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)

target     prot opt source               destination

ACCEPT     all  --  80.103.114.34        0.0.0.0/0

ACCEPT     all  --  150.214.212.13       0.0.0.0/0
```

----------

## BlinkEye

Please try replacing TARPIT with REJECT.

----------

## urcindalo

I replaced TARPIT with DROP (I don't wanna give'em a clue  :Wink:  ), and now it didn't quit after trying to login with a fake user.

What's more, now I see this in iptables:

```
# iptables -L -n

Chain INPUT (policy ACCEPT)

target     prot opt source               destination

ACCEPT     all  --  127.0.0.1            0.0.0.0/0

ACCEPT     all  --  80.103.114.34        0.0.0.0/0

ACCEPT     all  --  150.214.212.13       0.0.0.0/0

ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED

BLACKLIST  all  --  0.0.0.0/0            0.0.0.0/0

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:20

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:21

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:137:139

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:426

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:445

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:1417:1420

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:5900:5902

ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           state NEW udp dpts:5900:5902

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:5800:5802

ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           state NEW udp dpts:5800:5802

ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpts:5500:5502

ACCEPT     udp  --  0.0.0.0/0            0.0.0.0/0           state NEW udp dpts:5500:5502

REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-port-unreachable

Chain FORWARD (policy ACCEPT)

target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)

target     prot opt source               destination

ACCEPT     all  --  80.103.114.34        0.0.0.0/0

ACCEPT     all  --  150.214.212.13       0.0.0.0/0

Chain BLACKLIST (1 references)

target     prot opt source               destination

DROP       tcp  --  192.168.12.147       0.0.0.0/0           tcp dpt:22
```

It seems it is working, at last!!

Thanks for your help.

----------

## urcindalo

Blacklist is now working beautifully. This morning I saw a blocked-out ip   :Smile:  So, I would like to thank BlinkEye for his impressive work.

I got a request that maybe is off-topic (since this is an ssh/ftp thread). I receive sometimes vnc attacks, also. Since I don't want to close that port, would it be possible to include in blacklist some kind of vnc regex?

----------

## Freman

I cheated, I simply patched openssh to call a pre-configured executable file (be it script or what not) with the IP address on Invalid User.

The script saves which ip's it's blocked to a file and if it hasn't blocked the ip passed to it it'll add it to iptables.

Works GREAT, I even made an ebuild including patch for ease of installing across my entire network (c:

Only get one log entry per IP, it's blocked as fast as it starts.

----------

## kill[h]er

 *Quote:*   

> I got a request that maybe is off-topic (since this is an ssh/ftp thread). I receive sometimes vnc attacks, also. Since I don't want to close that port, would it be possible to include in blacklist some kind of vnc regex?

 

If they are attacking ssh and vnc at the same time, blacklist will already block them out of your system entirely for the timeout period you defined (10 mins default).  If they are just attacking VNC, and if the VNC attacks log to /var/log/auth.log then you could add a regex to the script, and if done right it should block them out entirely as well.

If they are doing nmap scans before attacking your ssh or vnc or both, then if you add the portions I posted before, they'll be blocked for 10 mins (default) immediately, and won't get the chance to attack ssh or vnc.

----------

## urcindalo

 *kill[h]er wrote:*   

> If they are doing nmap scans before attacking your ssh or vnc or both, then if you add the portions I posted before, they'll be blocked for 10 mins (default) immediately, and won't get the chance to attack ssh or vnc.

 

I modified blacklist.py with your HOST_NAME feature, launched it and I got no error, so I suppose it is working. 

Then I went on and modified it again to detect nmap scans. However, I get this error when launching the re-modified script:

```
 ./blacklist.py

Removing stale pidfile /var/run/blacklist.pid with pid 8300

Traceback (most recent call last):

  File "./blacklist.py", line 303, in ?

    scan()

  File "./blacklist.py", line 169, in scan

    re_ssh = re.compile( SSH_REGEX[ i ] )

  File "/usr/lib/python2.4/sre.py", line 180, in compile

    return _compile(pattern, flags)

  File "/usr/lib/python2.4/sre.py", line 227, in _compile

    raise error, v # invalid expression

sre_constants.error: redefinition of group name 'user' as group 3; was group 1
```

My modified lines look like this:

```
.....

SSH_REGEX =     [

                                                        r"Failed (?:none|password|keyboard-interactive/pam) for (?:invalid user )*(?P<$

                                                        r"Invalid user (?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\$

                                                        r"Did not receive (?P<user>.*) string from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}$

                                ]

.......

                # no tolerance for a root login attempt

                if ( match.group( 'user' ) == "root" ):

                        entry[ 1 ] += PERMITTED_LOGIN_FAILURES

                if ( match.group( 'user' ) == "identification" ):

                        entry[ 1 ] += PERMITTED_LOGIN_FAILURES
```

Since your modifications were intended for a previous script version, I wonder if the "user" definition changed somehow in the new script, causing your modifications to fail...   :Question: 

----------

## urcindalo

Apart from what I just wrote in the previous post, I'd like to add this comment:

 *kill[h]er wrote:*   

> If they are just attacking VNC, and if the VNC attacks log to /var/log/auth.log then you could add a regex to the script, and if done right it should block them out entirely as well.

 

Well, that's the problem. I don't know where the vnc login attempts go. I've just tried to connect to my box using fake vnc passwords and the connection gets refused. However, I see nothing vnc related in /var/log/auth.log nor in /var/log/messages. Even if the connection is successful I see no vnc entries in those files.

Does anybody know where can I look for them?

----------

## urcindalo

One more question (the ones posted immediately before this post are still unanswered   :Crying or Very sad:  ).

In the wiki you can read this: *Quote:*   

> UPDATE: You may safely reset your iptable rules while running blacklist.py. It will (re)add it's needed rules automatically when blocking the next IP.

 

One of my rules relates to my home box, which has a dynamic ip. So, I signed up with no-ip.com and assigned it to my-machine.no-ip.org, which is the actual address I set up in my iptables.conf file. However, everytime I reboot my DSL modem-router I'm assigned a different ip, so my-machine.no-ip.org points to a different ip periodically. To make the iptables rule regarding my home machine to always point to the correct ip address, I added a cron job to "iptables-restore iptables.conf" every day.

My question is: will any pre-existing BLACKLIST rules in iptables be flushed after cron execs iptables-restore? I interpret it will be in fact the case from the quote above, although I'd like very much to be wrong.

Is there any better way of updating my my-machine.no-ip.org rule without loosing pre-existing blacklist rules?

----------

## BlinkEye

 *urcindalo wrote:*   

> One more question (the ones posted immediately before this post are still unanswered   ).
> 
> In the wiki you can read this: *Quote:*   UPDATE: You may safely reset your iptable rules while running blacklist.py. It will (re)add it's needed rules automatically when blocking the next IP. 
> 
> One of my rules relates to my home box, which has a dynamic ip. So, I signed up with no-ip.com and assigned it to my-machine.no-ip.org, which is the actual address I set up in my iptables.conf file. However, everytime I reboot my DSL modem-router I'm assigned a different ip, so my-machine.no-ip.org points to a different ip periodically. To make the iptables rule regarding my home machine to always point to the correct ip address, I added a cron job to "iptables-restore iptables.conf" every day.
> ...

 

Well, if you're IP changes it won't really matter if you continue to block out IPs from previous attacks because it would be quite a coincidence if exactly such a blocked IP would start to attack your new IP. And even if it did, they could try a couple of times and then will (again) be blocked out. This restore would only affect the 10 minutes before your IP change which is 1/144 each day ... Nothing to worry about   :Wink: .

So, no, it won't add any pre-existing iptables. What I tried to say was it will add the needed CHAIN again (this comment was related to an earlier version where the CHAIN was only added once when starting the script).

----------

## urcindalo

 *BlinkEye wrote:*   

> Well, if you're IP changes ...
> 
> So, no, it won't add any pre-existing iptables.

 

Well, it does NOT change at my Gentoo box (at work), the one running blacklist. I got a static ip.

What I said (or tried to) was that I've added an iptables rule for my box at home to never be excluded from accessing my work computer. My home box is the one with a dynamic ip. Why should I add a rule to one of my computers, in the first place? Because I recently replaced my long-time-used passwd with a new pretty long one, and sometimes I just forget it   :Laughing:  Since I set the PERMITTED_LOGIN_FAILURES to 3, that allows me only for one mistake (old passwd), one typo and any other sort of wrong input (maybe CAPS key activated?) before denying me the acces for A WEEK (yeah, I'm drastic here  :Wink:  ) So, I decided that rule would someday be useful.

From your explanation I see I was correctly interpreting your wiki. So, I'm going to reduce the blocking period to match the cron job restoring iptables. It's a nonsense to set it up any longer.

The ideal woul be to create a script to update only that particular rule, not the whole iptables. However, I'm no programmer and don't know how to do that   :Crying or Very sad: 

----------

## urcindalo

It's me again  :Rolling Eyes:  It seems I'm monopolizing the thread   :Laughing: 

Anyway, first of all I must thank again BlinkEye for his work. It's working like a charm. And I also want to thank kill[h]er. His modifications are now working perfectly. I just made a typo in the ssh regex when inserting them. I also completely removed the reference to my dynamic-address home box in my office box's iptables.conf, since it was causing more trouble than good.

My question is: what must I change in the script to deny access in the blacklist rules to ALL ports, not only to the ssh or ftp port? I just want to deny the access to any port to those ssh or ftp brute-force attacking address, but not to anyone (including myself), that might make a mistake typing a password. Thanks.

----------

## brfsa

extremelly nice post...

I read it long ago, but only now that some chinese hackers started to brute force the server at college, i took a tough look at this tutorial.

I backtraced hackers from China and Korea mainly, and some from Malaysia...

----------

## brfsa

I like this script... very nice.

when is a new vesion that will support more types of DoS blocking coming out ?

how to block those attepts of wrong password for an allowed user ???

for example:

 *Quote:*   

> 
> 
> Apr 23 08:40:18 athlon sshd(pam_unix)[21158]: authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=localhost  user=fred
> 
> Apr 23 08:40:19 athlon sshd[21153]: error: PAM: Authentication failure for fred from localhost
> ...

 

 :Cool: 

----------

## scottevil

great script, made a couple regex and it's on doing it's blocking and so forth...

i'm using proftpd , the log was slightly different, 

```

Apr 29 09:01:20 poo sshd[21523]: Invalid user erick from 200.31.27.182

Apr 29 08:32:18 poo proftpd[5617]: localhost (test.com[70.85.121.242]) - USER asdf: no such user found from test.com [70.85.121.242] to 127.0.01:21

```

```

proftpd:

r"proftpd(?:.*)\slocalhost(?:.*)\sUSER\s(?P<user>.*)[:]\sno such user found from(?:.*)\[(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]"

sshd:

r"Invalid user (?P<user>.*)\sfrom\s(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"

```

Anyway, thought I would throw out those regex's for anyone who had the same log format as I do.

Thanks for the script, I think i'll add a section in it for <start> <restart> <stop>  ..

----------

## eyeL

Good idea. I've been working on my own version of this.  I have a bash script to parse my logs for brute attempts, save them into a file, and then a perl script to run a regex through and harvest the IPs, then another script that reads that script line by line and add a rule to my IPTables to ban them, and then emails me the IP and port which they are banned from.  It also includes a DNS lookup, and a whois report, and it gives me the abuse email for their ISP, and then sends out an automated message containing logs of their intrusion attempts.  It all runs in cron at midnight each night.

edit; 

```
SYSLOG_REGEX = r"sshd[[][0-9]+[]]: Invalid user (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
```

you wouldn't even need a regex like that, you could just

```
 import os

os.system("cat /log/file/ | grep \"Invalid user\" > /invalid/users/file)

```

----------

## BlinkEye

 *eyeL wrote:*   

> 
> 
> ```
> SYSLOG_REGEX = r"sshd[[][0-9]+[]]: Invalid user (?P<user>.*?) from (?:::ffff:)*(?P<host>(\d{1,3}\.){3}\d{1,3})"
> ```
> ...

 

Yes I do. This regex catches not only the line but especially the user and host for latter use (iptables).

----------

## xoomix

Has anyone modified this script yet to block port 80 after matching strings from the apache logs? That could be so useful to me - just wish I knew how to go about it.

----------

## magic919

This is not a support forum.

However,  check www.pettingers.org to block port 80 stuff.

----------

## xoomix

 *magic919 wrote:*   

> This is not a support forum.
> 
> However,  check www.pettingers.org to block port 80 stuff.

 

It's pretty apparent that everyone comes here for questions/answers - what is that if it's not support? This entire thread is made up specifically of posts from people asking the author how to get his script working and/or configured to do certain things, so I am not sure where you are coming from.

As far as the link you provided thanks for the thought, but I could not find anything there that addresses my specific question, which was "Has anyone modified this script yet to block port 80 after matching strings from the apache logs?" - meaning blacklist.py . I guess it's posssible that it's there but for some reason I just can't find it.

----------

## xoomix

I see now where you are coming from about this not being a support forum (the sticky post) - I did feel it rather strange that I was the only one in there that got a comment on it not being a support forum - go through the thread and count up how many question marks there are in there (people asking quetions) - go figure.

----------

## xoomix

For anyone interested I started a new topic under the unsupported software forum:

```
http://forums.gentoo.org/viewtopic-t-470094-highlight-.html
```

This is me specifically asking, again, if anyone's configured blacklist.py to do port 80 stuff.

Please feel free to add/reply to that  :Smile: 

----------

## Biker

Great script.

If you have logrotate installed you may consider droppping:

```
/var/log/blacklist.log {

    daily

    missingok

    notifempty

}

```

into a file named /etc/logrotate.d/blacklist

Biker

----------

## skakz

hi all!

this tool is powerful!!!!   :Twisted Evil: 

please check here.. i have modified this script to support http protocol too.

anyway all thanks goes to  BlinkEye!!!

----------

## Robert S

This script doesn't seem to work any longer for me.  I can still log in after repeated failures.  It used to work fine.  I suspect its an iptables problem.  After repeated incorrect passwords from 192.168.2.20:

 *Quote:*   

>  # iptables -nvL
> 
> Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
> 
>  pkts bytes target     prot opt in     out     source               destination
> ...

 

Despite this I am still able to log in from my PC at 192.168.2.20.  I assume that the reason for 202.67.151.139 being repeatedly rejected is that whoever is at that address is not being blocked.

Unfortunately I'm not cluey enough about iptables to solve this.

Also - at certain times my iptables rules get mysteriously dropped ie:

 *Quote:*   

> # iptables -nvL |less                                                
> 
> Chain INPUT (policy ACCEPT 2339 packets, 750K bytes)                            
> 
>  pkts bytes target     prot opt in     out     source               destination 
> ...

 

- with no intervention on my part.

Can anybody help?

----------

## Robert S

This seems to have solved the problem.  I've changed "yes" to "no":

 *Quote:*   

> $ cat /etc/conf.d/iptables 
> 
> SAVE_ON_STOP="no"

 

It looks as if the saved iptables rules have mucked up the script when it restarts.

----------

## mudrii

Nice script jumping into it for trial  :Wink: 

----------

## Ishiki

Magnificent script !

Thank you very much.

----------

## predatorfreak

In my own experience, using iptable's recent match support you can achieve the same results (blocks all bruteforce attempts at SSH) without parsing ANY logs. It also doesn't require any fancy blacklist or somesuch, iptables will keep the blacklist internal until the host in question has stopped ramming ports, if they continue to port ram, they're banned for longer periods of time.

In practice, this works extremely well. I use something to this effect on my server, although it's wrapped in my iptables system that handles pretty much all my firewall rules.

Here's a basic example of iptables recent match being used to defeat bruteforce attacks:

```
iptables -N BRUTEFORCE_DEFEAT

iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j BRUTEFORCE_DEFEAT

iptables -A BRUTEFORCE_DEFEAT -m recent --update --seconds 15 -j DROP

iptables -A BRUTEFORCE_DEFEAT -m recent --set -j RETURN
```

Of course, this might not meet everyone's needs, but it's significantly simpler AND faster, it will defeat bruteforce attempts, DDoS's on ports, etc, without parsing any logs or some such.

Also, changing the --seconds 15 to any other value means that if they hit the port more than once in X amount of time, they're banned. Most bruteforcers just hit as much as possible, so they're banned after the first attempt, generally. If you wanted the ban to last longer though, you could use say 500 seconds or whatever and they'd be banned for 500 seconds.

----------

## funkoolow

hi there,

I think this is a great tool, but, if allowed, i suggest some improvements that would make it greater:

1) the max_attempts variable: number of max attempts over which the ban time for an attacking ip will increase.

2) the increase_time variable: number of seconds to increase the ban time for an attacking ip once the max_attempts has been passed.

actually, if i read correctly, those two parameters are set but in a static way to max_attempts=100 and increase_time=1sec so that at the 101st login attempt the ban time will increase to BLOCKING_PERIOD+1. Otherwise, i'd rather prefer the script to increase the ban time by 30sec every 10 wrong login attempts.

Is it possible to add those values in a dynamic way as actually happens e.g. for the blocking_period parameter?

thanks a lot  :Smile: 

----------

## LJM9000

Ok, I think I must be retarded I cannot get this to work.

I have compiled IPTables into my kernel

Then I ran

```
iptables-restore  /etc/iptables.bak
```

Then 

```
/etc/init.d/iptables save
```

I started IPTables by running

```
/etc/init.d/iptables start
```

I then ran the blacklist init script posted here

```
/etc/init.d/blacklist
```

Then when I fail logging in from a different computer on purpose /var/log/blacklist.log never changes. It just remains empty. My /var/log/auth.log shows failed connections.

I have installed the logsentry package as well, per the instructions.

Help!

----------

## LJM9000

I fixed it. It seems that there was an error in the code.

I changed it to the following since Logtail doesn't need the -f anymore.

```
try:

      new_log_entries = system_command( LOGTAIL + " " + LOG_INPUT )

   except:

      new_log_entries  =  system_command( LOGTAIL + " " + LOG_INPUT )

```

Yes it makes no sense that I should need the except in there. But when I removed it the program errored.

Also I don't know python so that could have been the problem too.

Everythings working correctly now.

----------

## Raposatul

 *LJM9000 wrote:*   

> Ok, I think I must be retarded I cannot get this to work.
> 
> I have compiled IPTables into my kernel
> 
> Then I ran
> ...

 

iptables-save > /etc/iptables.bak

iptables-restore <  /etc/iptables.bak

----------

## dr4cul4

I have a small fix for init.d script (assuming pid file is the same as in original blacklist.py script). It fixes stopping and restarting issues.

```
#!/sbin/runscript

# Distributed under the terms of the GNU General Public License v2

#

# Refer to forum post: http://forums.gentoo.org/viewtopic-p-3141510.html#3141510

#

# Date: 2008-05-14

# Version 0.2 by dr4cul4

# you may want to uncomment the below if using iptables in rc-update, but

# it is probably not necessary

depend() {

       use iptables sshd

}

start() {

        ebegin "Starting blacklist"

        start-stop-daemon --start --quiet --background \

                --exec /usr/bin/python /usr/sbin/blacklist.py

        eend $?

}

stop() {

        ebegin "Stopping blacklist"

        start-stop-daemon --stop --quiet --pidfile /var/run/blacklist.pid

        eend $?

}
```

----------

## sam_i_am

Hi all,

I've pared down this script even further and used the ability of syslog-ng to create filters to match a regexp as well as the ability to send the log to another program. So, no need for a separate thread and polling.

Here's how to use it:

Modify syslog-ng.conf by adding the following filter and destination

```

# destination is the python script which will insert an iptable rule to block the ip

destination sshd_ban    { program("/sbin/block_ip.py");  };

# create a filter that will pick suspicions log statements from sshd daemon

filter f_sshd_attack { program(sshd) and (

                        match('Did not receive identification string from') or

                        match('Invalid user') or

                        match('Failed password for root')

                       );

                     };

# connect the filter to the destination

log { source(src);     filter(f_sshd_attack);    destination(sshd_ban);   };

```

Save the following script as /sbin/block_ip and edit the variables at the top to suit your environment. I've removed the timeout part as it wasn't important in my case.

BEWARE: once an ip is blocked, it stays blocked until the rule is removed (or the system is rebooted)

One nice thing about this is that the hack attempts that I've seen starts with a port scan on port 22 which generates the log message "Did not receive identification from xx.xx.xx.xx". This will immediately trigger the block and the hapless script kiddie is locked out forever without being able to try even a single username  :Twisted Evil: 

```

#!/usr/bin/python

# block_ip.py is free software; you can redistribute it and/or modify

# it under the terms of the GNU General Public License as published by

# the Free Software Foundation; either version 2 of the License, or

# (at your option) any later version.

#

# block_ip.py is distributed in the hope that it will be useful,

# but WITHOUT ANY WARRANTY; without even the implied warranty of

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the

# GNU General Public License for more details.

#

# Original Copyright: Reto Glauser aka blinkeye

# Adapted by academic.sam at gmail.com for invocation by syslog-ng

# Version 0.1

import re;

import commands;

import sys;

from syslog import *;

import os;

from os import access, R_OK, W_OK, X_OK;

DATE_FORMAT = "%b %d %X" # e.g.: May 25 23:49:12

BLOCKED_LIST = "/tmp/blocked_ip"

IPTABLES = "/sbin/iptables"

CUSTOM_CHAIN = "BLACKLIST"

SSH_PORT = 22

SSH_REGEXC = [ 

       re.compile( r"Did not receive identification string from (?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" ),

      re.compile( r"Failed (?:none|password|keyboard-interactive/pam) for (?:invalid user )*(?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" ),

       re.compile( r"Invalid user (?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" )

      ]

# Wrapper function for commands

def system_command( string_command ):

   return_value = []

   return_value = ( commands.getstatusoutput( string_command ) )

   if not return_value[ 0 ] == 0:

      raise IOError( return_value[ 1 ] )

   return return_value[ 1 ]

# block ip for the duration of time

def block( ip, port ):

   try:

      system_command( IPTABLES + " --new-chain " + CUSTOM_CHAIN )

      system_command( IPTABLES + " --insert INPUT --jump " + CUSTOM_CHAIN )

   except:

      None      

   system_command( IPTABLES + " --insert " + CUSTOM_CHAIN + " --source " + ip + " --protocol tcp --dport " + str( port ) + " --jump DROP" )

   syslog( "Blocking " + ip )

# Do we have iptables ?

if not access( IPTABLES, X_OK ):

   raise IOError, IPTABLES + " is not executable"

ip_list = []

try:

   ipf = open( BLOCKED_LIST, "r")

except IOError:

   pass

else:

   for line in ipf:

      ip_list.append( line.strip() )

   ipf.close()

   

openlog( "block_ip" )

syslog("Reading initial list: " + ", ".join( ip_list ) )

while 1:

   line = sys.stdin.readline()

   if not len( line ):

      break

   for i in range( 0, len( SSH_REGEXC ) ):

      if len( SSH_REGEXC[ i ].findall( line ) ):

         regex_matches = SSH_REGEXC[ i ].finditer( line )

         for match in regex_matches:

            ip = match.group( 'ip' )

            if ip not in ip_list:

               ip_list.append( ip )

               block( ip, SSH_PORT)

try:

   ipf = file( BLOCKED_LIST, "w")

except IOError:

   syslog( "Could not write blocked IP list to " + BLOCKED_LIST)

else:

   ipf.write( "\n".join(ip_list))

   ipf.close()

```

----------

## brfsa

sam_i_am, 

Very nice and neat implementation u have in there man... 

Works great!!! 

if I put my password wrong? means Im locked out?

Anyone, better add your public ssh key to the authorized file just in case. 

Thanks for sharing your script.

 :Very Happy: 

----------

## haarp

Hey,

I added/modified a few filters. If anyone's interested, here's the relevant sections of my blacklist.py. Just add what you need to your own blacklist...

```
SSH_REGEX =     [

                        r"Failed (?:none|password|keyboard-interactive/pam) for (?:invalid user )*(?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",

                        r"Invalid user (?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",

                        r"Did not receive (?P<user>.*) from (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})",

                        r"Address (?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) maps to (?:.*), but this does not map back to the address - (?P<user>.*)",

                        r"reverse mapping checking getaddrinfo for (?:.*) \[(?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\] failed - (?P<user>.*)"

                ]

# SSH_REGEX catches following similar entries: 

# Jan  2 21:48:05 blinkeye sshd[4529]: Failed password for invalid user sato from 61.172.192.3 port 54177 ssh2 

# Jan  2 21:48:05 blinkeye sshd[4529]: Failed password for invalid user sato from ::ffff:61.172.192.3 port 54177 ssh2 

# Oct 21 18:52:01 blinkeye sshd[31286]: Failed password for root from 152.149.148.115 port 36667 ssh2 

# Oct 21 18:52:01 blinkeye sshd[31286]: Failed password for root from ::ffff:152.149.148.115 port 36667 ssh2 

# Sep 18 05:08:06 blinkeye sshd[3971]: Failed keyboard-interactive/pam for root from 152.149.148.115 port 44896 ssh2 

# Sep 18 05:08:06 blinkeye sshd[3971]: Failed keyboard-interactive/pam for root from ::ffff:152.149.148.115 port 44896 ssh2                 

# Feb 16 15:07:33 madcat sshd[30582]: Did not receive identification string from 204.191.10.60 

# Mar 30 06:14:36 madcat sshd[13621]: Address 218.28.166.67 maps to pc0.zz.ha.cn, but this does not map back to the address - POSSIBLE BREAK-IN ATTEMPT! 

# Jul  3 12:36:25 madcat sshd[1586]: reverse mapping checking getaddrinfo for 66962125.hostnoc.net [66.96.212.5] failed - POSSIBLE BREAK-IN ATTEMPT! 

FTP_REGEX =    [

             r"ftp(?:.*) authentication failure(?:.*) rhost=(?:::ffff:)*(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) (?: user=)*(?P<user>.*)",

             r"proftpd(?:.*)USER (?P<user>.*): no such user found(?:.*)\[(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\]"

      ]

# FTP_REGEX catches following similar entries:

# Oct  3 19:35:41 blinkeye ftp(pam_unix)[8746]: authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=206.222.29.194

# Oct  3 19:35:43 blinkeye ftp(pam_unix)[8746]: authentication failure; logname= uid=0 euid=0 tty= ruser= rhost=206.222.29.194  user=root

# Feb 14 08:38:47 madcat proftpd[3247]: madcat (202.96.5.29[202.96.5.29]) - USER Administrator: no such user found from 202.96.5.29 [202.96.5.29] to 192.168.0.5:21
```

```

   # no tolerance for root login attempts

   if ( match.group( 'user' ) == "root" ):

       entry[ 1 ] += PERMITTED_LOGIN_FAILURES

        

   # no tolerance for Administrator login attempts 

        if ( match.group( 'user' ) == "Administrator" ):

            entry[ 1 ] += PERMITTED_LOGIN_FAILURES

   # no tolerance for NMAP scans

   if ( match.group( 'user' ) == "identification string" ):

       entry[ 1 ] += PERMITTED_LOGIN_FAILURES

   # no tolerance for possible break-in attempts

   if ( match.group( 'user' ) == "POSSIBLE BREAK-IN ATTEMPT!" ):

       entry[ 1 ] += PERMITTED_LOGIN_FAILURES

```

Last edited by haarp on Tue Oct 14, 2008 9:11 pm; edited 1 time in total

----------

## sam_i_am

 *brfsa wrote:*   

> sam_i_am, 
> 
> if I put my password wrong? means Im locked out?
> 
> 

 

I'm afraid so. Like you suggested, I don't recommend this if you are using password based logins as there are plenty of chances of getting locked out.

In fact it happened to me just today even with a public key  :Embarassed:  I was logging in from a XP machine using cygwin and it had my username with first letter in upper case which triggered the block. Fortunately I got in through another machine. I guess a time out feature would make it a bit more forgiving.

Sam

----------

## crimson

I use fail2ban to block failed attempts, and I get quite a few, but I'm curious is there a way to tell what passwords they are trying to use?  It only tells me the username.  ie:  Invalid user test from 123.45.67.89.  Out of curiosity I'd like to know what passwords they're using.

----------

## haarp

If your apps log the passwords that are tried then somethings inherently broken. No, that's impossible

----------

## crimson

 *haarp wrote:*   

> If your apps log the passwords that are tried then somethings inherently broken. No, that's impossible

 

I guess I would have to write a fake ssh server to log passwords then.  I don't know that I'm that curious, but that probably wouldn't be too hard to do.

----------

## crimson

Actually here is an article that shows some researchers doing just that by patching ssh to record passwords, this nearly satisfies my curiosity.

http://www.securityfocus.com/infocus/1876

----------

## fwmartin

Anyone else getting distributed attempts now?  I'm getting hit by many ips.  Each at a rate below fail2bans threshold.

----------

## urcindalo

I was running this script with great success in the past, but I just noticed that for a long while I haven't received any more local messages on ssh attempts. Since I don't think they have stopped all of a sudden, I checked my init script and I found this out:

```
$ sudo /etc/init.d/blacklist restart

 * Staring blacklist.py ...                                                                                        [ !! ]
```

Is anyone else having problems with this script? I miss it.

----------

## haarp

Works fine for me. Try my initscipt:

```
#!/sbin/runscript

# Distributed under the terms of the GNU General Public License v2

#

# Refer to forum post: http://forums.gentoo.org/viewtopic-p-3141510.html#3141510

#

# Date: 2008-05-14

# Version 0.2 by dr4cul4

# modified by haarp on 2008-07-08

# you may want to comment iptables...

depend() {

   need localmount net

   after bootmisc iptables

   use sshd

}

start() {

   ebegin "Starting blacklist"

   # For some reason, start-stop-daemon fails. Luckily, blacklist itself makes lockfiles

   # start-stop-daemon --start --background --quiet --pidfile /var/run/blacklist.pid --exec /usr/bin/python /usr/local/sbin/blacklist.py

   /usr/bin/python /usr/local/sbin/blacklist.py &

   eend $?

}

stop() {

   ebegin "Stopping blacklist"

   start-stop-daemon --stop --quiet --pidfile /var/run/blacklist.pid

   eend $?

}
```

----------

## urcindalo

 *haarp wrote:*   

> Works fine for me. Try my initscipt:
> 
> ...
> 
> [

 

Thanks very much. Your script made me notice where my error was coming from: my log file had changed from /var/log/auth.log to /var/log/messages but I hadn't updated this info in my blacklist.py script.

I am using now your script   :Smile:   even though my old one could still work.

----------

## Shadus

I've noticed on some newer version of python this seems to crash a lot, have there been any updates to keep this as stable as it was on older versions of python?

----------

## wolfieh

i just disable password authentication and use public key auth only

----------

## qdii

link seems to be down  :Sad: 

----------

