diff --git a/CHANGELOG b/CHANGELOG index 14a9a5f..885b1fb 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,11 @@ +BruteForceBlocker v1.2.6 - Nov 3 2018 + - add new regexps to match more failure log entries of recent OpenSSH versions + - resolve reverse DNS of blocked IPs + +BruteForceBlocker v1.2.5 - Feb 11 2018 + - add a new regexp to match another failure log entry + - contributed by Yasuhiro KIMURA + BruteForceBlocker v1.2.4 - Sep 2 2017 - add a new regexp to match failure log entries of recent OpenSSH versions - contributed by Max Khon diff --git a/CREDITS b/CREDITS index f77ea12..683ee9a 100644 --- a/CREDITS +++ b/CREDITS @@ -1,4 +1,3 @@ - Thanks to: - Jonas Davidsson and Matt Pearce for good ideas @@ -6,5 +5,8 @@ Thanks to: BruteForceBlocker v1.2 was released - Branislav Gerzo for some perl coding hints and help with code cleanup - Kan Sasaki who sent me a patch for BruteForceBlocker which allows to - resolve reverze DNS to an IP address + resolve reverse DNS to an IP address - Max Khon for regexp to match recent OpenSSH failure log entries +- Yasuhiro KIMURA for regexp to match another failure log entry +- Balazs Mateffy for new regexps to match more failure log entr and + an idea to resolve reverse DNS records for blocked IPs diff --git a/README b/README index 52d9ede..adb2dc3 100644 --- a/README +++ b/README @@ -1,4 +1,4 @@ -BruteForceBlocker v1.2.4 +BruteForceBlocker v1.2.6 BruteForceBlocker is a perl script, that works along with pf - OpenBSD's firewall (which is also available on FreeBSD and NetBSD) and its main diff --git a/bruteforceblocker.pl b/bruteforceblocker.pl index ddcb4db..71c4f23 100755 --- a/bruteforceblocker.pl +++ b/bruteforceblocker.pl @@ -17,7 +17,7 @@ require '/usr/local/etc/bruteforceblocker.conf'; my $work = { - version => '1.2.4', + version => '1.2.6', ipv4 => '(?:\d{1,3}\.){3}\d{1,3}', # regexp to match ipv4 address ipv6 => '[\da-fA-F:]+', # regexp to match ipv6 address fqdn => '[\da-z\-.]+\.[a-z]{2,4}', # regexp to match fqdn @@ -35,7 +35,7 @@ } close(TABLE) or syslog("notice", "Couldn't close $cfg->{tablefile}"); - syslog('notice', 'downloading blacklist from project site') if $cfg->{debug}; + syslog('notice', 'downloading blacklist from the project site') if $cfg->{debug}; # download the list from project site and load IPs to @remoteIPs array if ( my $content = download("$work->{projectsite}/blist.php?mindays=$cfg->{mindays}&mincount=$cfg->{mincount}") ) { @@ -43,7 +43,7 @@ push(@{$work->{remoteIPs}}, $1); } } else { - syslog('notice', "Can't download IP blacklist from project site") if $cfg->{debug}; + syslog('notice', "Unable to download IP blacklist from the project site") if $cfg->{debug}; } # get IPs that we don't have in local pf table @@ -67,7 +67,7 @@ syslog('notice', "Couldn't add $work->{pool} to firewall"); } } - syslog('notice', 'blacklist synchronized with project site') if $cfg->{debug}; + syslog('notice', 'blacklist synchronized with the project site') if $cfg->{debug}; } my %count = (); # hash used to store total number of failed tries @@ -78,11 +78,13 @@ while (<>) { if (/.*Failed password.*from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn}) port.*/i || + /.*Failed keyboard.*from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn}) port.*/i || /.*Invalid user.*from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn})$/i || /.*Did not receive identification string from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn})$/i || /.*Bad protocol version identification .* from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn})$/i || /.*User.*from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn}) not allowed because.*/i || /.*error: maximum authentication attempts exceeded for.*from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn}).*/i || + /.*error: PAM: authentication error for.*from ($work->{ipv4}|$work->{ipv6}|$work->{fqdn}).*/i || /.*fatal: Unable to negotiate with ($work->{ipv4}|$work->{ipv6}|$work->{fqdn}).*/i) { my $IP = $1; @@ -124,8 +126,11 @@ sub download { sub block { my ($IP) = shift or die "Need IP!\n"; + my $query = $res->search($IP, "PTR"); + my $RDNS = $query ? ($query->answer)[0]->ptrdname : "not resolved"; + if ($timea{$IP} && ($timea{$IP} < time - $cfg->{timeout})) { - syslog('notice', "resetting $IP count, since it wasn't active for more than $cfg->{timeout} seconds") if $cfg->{debug}; + syslog('notice', "resetting $IP ($RDNS) count, since it wasn't active for more than $cfg->{timeout} seconds") if $cfg->{debug}; delete $count{$IP}; } $timea{$IP} = time; @@ -134,11 +139,11 @@ sub block { $count{$IP}++; if ($cfg->{debug} && ($count{$IP} < $cfg->{max_attempts}+1)) { - syslog('notice', "$IP was logged with total count of $count{$IP} failed attempts"); + syslog('notice', "$IP ($RDNS) was logged with total count of $count{$IP} failed attempts"); } if ($count{$IP} == $cfg->{max_attempts}+1) { - syslog('notice', "IP $IP reached maximum number of failed attempts!") if $cfg->{debug}; + syslog('notice', "IP $IP ($RDNS) reached maximum number of failed attempts!") if $cfg->{debug}; if (!grep { /$IP/ } @{$cfg->{whitelist}}) { $work->{pool} = $IP . '/32' if ($IP =~ /\./); # block whole ipv4 pool $work->{pool} = $IP . '/128' if ($IP =~ /\:/); # block while ipv6 pool @@ -149,14 +154,14 @@ sub block { syslog('notice', "Couldn't add $cfg->{pool} to firewall"); system("$cfg->{pfctl} -k $IP") == 0 || syslog('notice', "Couldn't kill all states for $IP"); - system("echo '$work->{pool}\t\t# $work->{timea}' >> $cfg->{tablefile}") == 0 || + system("echo '$work->{pool}\t\t# $work->{timea} ($RDNS)' >> $cfg->{tablefile}") == 0 || syslog('notice', "Could't write $work->{pool} to $cfg->{table}'s table file"); # send mail if it is configured if ($cfg->{email} && $cfg->{email} ne '') { syslog('notice', "sending email to $cfg->{email}") if $cfg->{debug}; - open(MAIL, "| $cfg->{mail} -s '$work->{hostname}: BruteForceBlocker blocking $work->{pool}' $cfg->{email}"); - print (MAIL "BruteForceBlocker blocking $work->{pool} in pf table $cfg->{table}\n"); + open(MAIL, "| $cfg->{mail} -s '$work->{hostname}: BruteForceBlocker blocking $work->{pool} ($RDNS)' $cfg->{email}"); + print (MAIL "BruteForceBlocker blocking $work->{pool} ($RDNS) in pf table $cfg->{table}\n"); close(MAIL); } ;