11d65
![]() |
|
|
|
|
|||||||
| Howtos & FAQs (Moderated) Would you like to share some of your solutions for certain problems? Tips or tricks? Post here. All new topics are automatically moderated. |
![]() |
|
|
Thread Tools | Display Modes |
|
#1
|
||||
|
||||
|
I've had a few people from around the world try to break into my mail server using a brute-force approach, trying various username-password combinations, sometimes several thousand of them, slowing my system down, filling my mail log with crap, and generally just annoying me. I figure there must be a kiddie-script out there to do this that these people are running, as some attacks send the exact same usernames in the exact same order... I've tried to ignore them, but they have been annoying.
After the latest one yesterday originating somewhere in China, I decided to do something about it. I wrote a script to detect when this happens and generate a report, including it in the daily periodic cron job so that when I go through the system report each morning, I can blacklist any IPs at my firewall. I figured I'd share it here in case somebody else finds it useful. /usr/local/sbin/updateotherblocks:Code:
/usr/bin/vi /usr/local/etc/IPBlocks/Others /sbin/pfctl -t other-blocked -Tr -f /usr/local/etc/IPBlocks/Others Code:
# Block IPs that have tried to hack me table <other-blocked> persist file "/usr/local/etc/IPBlocks/Others" block in log quick on $ext_if from <other-blocked> to any This effectively blocks any IP in the /usr/local/etc/IPBlocks/Others file at the firewall. While strictly a reactive measure, it does easily detect them and make it plain when they occur, allowing me to block the originating IP in a matter of a few seconds and ensuring that they won't be able to do so again from the same IP. Log analysis script in the next post... |
| The Following User Says Thank You to Ruler2112 For This Useful Post: | ||
gkontos (January 8th, 2010) | ||
|
#2
|
||||
|
||||
/usr/local/sbin/detectmailhack.pl:Code:
#!/usr/bin/perl
###############################################################################
# #
# Perl script to analyze a mail log file for failed logins in order to detect #
# brute-force attempts to guess passwords. Outputs a table with the IP of #
# the failed login, username attempted, and a count of each. #
# #
# Parameter 1: #
# Pass in the full path and name of a log file to use. #
# If none is provided, the default is /var/log/maillog #
# #
###############################################################################
# #
# Ruler's Common-Sense License: #
# #
# You may use this script however you want to, but I don't warrant it to #
# be good for anything in particular, though it happens to work well for #
# me. (I hate putting BS like this in, but I hate more being sued.) If #
# you use this script, you must keep this license and credit to me in it #
# in the form of this block, even if you modify it for your own use. If #
# you want to send me money for it, fantastic! Send me a private message #
# on the freebsd.org forums and I'll give you my PayPal address. :-) Even #
# just a simple 'thank you' would be nice. If not, that's fine too. All #
# hate mail/spam is sent directly to /dev/null #
# - Jim, AKA Ruler2112 #
# #
###############################################################################
# #
# History: #
# #
# 2010-01-06 by Ruler2112 Wrote initial version. #
# 2010-01-07 by Ruler2112 Made it work better and support compressed #
# mail logs. #
# 2010-01-07 by Ruler2112 Released on freebsd.org forums. #
# #
###############################################################################
use strict;
my $LogFile = shift;
###############################################################################
# Variable Declaration Section #
# Set these variables to customize this script's behavior. #
###############################################################################
# UserDelim
# This specifies what the user in the mail log is immediately preceeded by.
# I don't know if this differs for MTAs other than Postfix, but figured I'd
# add it for ease of customizability.
my $UserDelim = "user=";
# IpDelim
# What the IP address of the connecting client is immediately preceeded by
# in the mail log. Again, I'm not certain that this would ever be useful to
# change, but it's easy enough to make customizable.
my $IpDelim = "ip=[";
# UserEnd
# What designates the end of the user field in the mail log. Some attempts
# may have spaces in the name of the user, so a plain space probably isn't
# a good choice.
my $UserEnd = ", ";
# IpEnd
# What comes immediately after the IP address in the mail log.
my $IpEnd = "]";
# CompressedCat
# This is a cat utility that reads compressed files. Typically known as
# 'zcat' under FreeBSD. This should be the full absolute path to and name
# of the file.
my $CompressedCat = "/usr/bin/zcat";
# RegularCat
# This is the normal cat utiliy and again should contain the full absolute
# path to and name of the file.
my $RegularCat = "/bin/cat";
###############################################################################
# !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! #
###############################################################################
# This is the Beginning of the Script #
# #
# Do not change anything below this line unless you know what you're doing! #
###############################################################################
my ($CatUtil);
my (@FailedIps, @FailedCount, $NumberIps, $TotalCount);
my (@FailedUsers, @FailedUserCount, $NumberUsers);
if($LogFile eq "")
{
$LogFile = "/var/log/maillog";
}
if((-e $LogFile) and (-r $LogFile))
{
SetCatUtility();
AnalyzeIps();
AnalyzeUsers();
}
else
{
print "$LogFile either does not exist or cannot be read! Aborting...\n";
}
exit;
sub SetCatUtility()
{
if((substr($LogFile, length($LogFile) - 3, 3) eq "bz2") or
(substr($LogFile, length($LogFile) - 2, 2) eq "gz"))
{
$CatUtil = $CompressedCat;
}
else
{
$CatUtil = $RegularCat;
}
return;
}
sub AnalyzeIps()
{
my ($counter, $workstr);
BuildIpTable();
$workstr = $NumberIps + 1;
print "$TotalCount failed mail login attempts from $workstr sources\n\n";
return;
}
sub BuildIpTable()
{
my (@workary, @sortedarray);
@FailedIps = ();
@FailedCount = ();
$NumberIps = -1;
$TotalCount = 0;
open(INFILE, "$CatUtil $LogFile |");
while(<INFILE>)
{
if(index($_, "LOGIN FAILED") > 0)
{
ProcessIpRecord($_);
}
}
close(INFILE);
push @workary, [$FailedIps[$_], $FailedCount[$_]] foreach (0..$NumberIps);
@sortedarray = sort { $a->[1] <=> $b->[1] } @workary;
@sortedarray = reverse @sortedarray;
@FailedIps = map { $$_[0] } @sortedarray;
@FailedCount = map { $$_[1] } @sortedarray;
return;
}
sub ProcessIpRecord()
{
my $logline = shift;
my ($workstr, $cutpos, $endpos, $leftover);
my ($attempteduser, $attemptedip);
chomp($logline);
$cutpos = index($logline, $UserDelim);
$endpos = index($logline, $UserEnd, $cutpos + 1);
$attempteduser = substr($logline, $cutpos + length($UserDelim), $endpos - $cutpos - length($UserDelim));
$cutpos = index($logline, $IpDelim);
$endpos = index($logline, $IpEnd, $cutpos + 1);
$attemptedip = substr($logline, $cutpos + length($IpDelim), $endpos - $cutpos - length($IpDelim));
$workstr = FindIp($attemptedip);
@FailedCount[$workstr]++;
$TotalCount++;
return;
}
sub FindIp()
{
my $address = shift;
my ($counter, $retval);
$retval = -1;
$counter = 0;
while($counter <= $NumberIps)
{
if($address eq @FailedIps[$counter])
{
$retval = $counter;
$counter = $NumberIps;
}
$counter++;
}
if($retval < 0)
{
$NumberIps++;
$retval = $NumberIps;
@FailedIps[$retval] = $address;
@FailedCount[$retval] = 0;
}
return $retval;
}
sub AnalyzeUsers()
{
my ($counter, $counter2, $spacing, $workstr);
for($counter = 0; $counter <= $NumberIps; $counter++)
{
BuildUserTable(@FailedIps[$counter]);
$workstr = @FailedIps[$counter] . ":";
$spacing = 40 - length($workstr) - length(@FailedCount[$counter]);
$workstr = $workstr . (" " x $spacing);
$workstr = $workstr . @FailedCount[$counter];
print "$workstr\n";
for($counter2 = 0; $counter2 <= $NumberUsers; $counter2++)
{
$workstr = (" " x 10) . @FailedUsers[$counter2];
$spacing = 30 - length($workstr) - length(@FailedUserCount[$counter2]);
$workstr = $workstr . (" " x $spacing) . @FailedUserCount[$counter2];
print "$workstr\n";
}
}
return;
}
sub BuildUserTable()
{
my $address = shift;
my (@workary, @sortedarray);
$NumberUsers = -1;
@FailedUsers = ();
@FailedUserCount = ();
open(INFILE, "$CatUtil $LogFile |");
while(<INFILE>)
{
if((index($_, "LOGIN FAILED") > 0) and (index($_, "$address") > 0))
{
ProcessUserRecord($_);
}
}
close(INFILE);
push @workary, [$FailedUsers[$_], $FailedUserCount[$_]] foreach (0..$NumberUsers);
@sortedarray = sort { $a->[0] cmp $b->[0] } @workary;
@sortedarray = sort { $a->[1] <=> $b->[1] } @workary;
@sortedarray = reverse @sortedarray;
@FailedUsers = map { $$_[0] } @sortedarray;
@FailedUserCount = map { $$_[1] } @sortedarray;
return;
}
sub ProcessUserRecord()
{
my $logline = shift;
my ($workstr, $cutpos, $endpos, $leftover);
my ($attempteduser, $attemptedip);
chomp($logline);
$cutpos = index($logline, $UserDelim);
$endpos = index($logline, $UserEnd, $cutpos + 1);
$attempteduser = substr($logline, $cutpos + length($UserDelim), $endpos - $cutpos - length($UserDelim));
$cutpos = index($logline, $IpDelim);
$endpos = index($logline, $IpEnd, $cutpos + 1);
$attemptedip = substr($logline, $cutpos + length($IpDelim), $endpos - $cutpos - length($IpDelim));
$workstr = FindUser($attempteduser);
@FailedUserCount[$workstr]++;
return;
}
sub FindUser()
{
my $user = shift;
my ($counter, $retval);
$retval = -1;
$counter = 0;
while($counter <= $NumberUsers)
{
if($user eq @FailedUsers[$counter])
{
$retval = $counter;
$counter = $NumberUsers;
}
$counter++;
}
if($retval < 0)
{
$NumberUsers++;
$retval = $NumberUsers;
@FailedUsers[$retval] = $user;
@FailedUserCount[$retval] = 0;
}
return $retval;
}
|
|
#3
|
||||
|
||||
|
You might want to take a look at security/bruteforceblocker.
__________________
Looking for administrator? http://www.syscare.sk |
|
#4
|
||||
|
||||
|
LOL - I saw your post and thought there was already something built to do this, which bummed me out. Then saw it only worked with SSH and was happy again. Doesn't look like bruteforceblocker works with mail, only SSH, which I don't have on the box. (I only administer the server from a PC in my office hard-wired to a dedicated NIC.) I also don't want to automatically block an IP based on the number of failed attempts (I thought of it early on, but then discarded the idea) for the simple reason that the vast majority of my co-workers are rather stupid and routinely forget their passwords, so it'd block everybody in the store from connecting to retrieve/send e-mail.
|
|
#5
|
||||
|
||||
|
bruteforceblocker works with anything, you only need to slightly modify its source (i.e. add the corresponding regexps to the appropriate place) and configure syslog correctly...
__________________
Looking for administrator? http://www.syscare.sk |
|
#6
|
|||
|
|||
|
In another thread SirDice posted that he uses sshguard and it looks pretty slick.
http://www.sshguard.net http://www.freebsd.org/cgi/ports.cgi...uard&stype=all
__________________
Advise on initial contact you have information Bravo Sierra Delta. |
|
#7
|
||||
|
||||
|
Quote:
I'm still going to use it for what I wrote it for, namely to run from the periodic daily cron job to add the report to the daily system e-mail. It's done and does exactly what I want, plus I really don't want the IPs blocked automatically. It's here if anybody wants to use it. |
![]() |
| Thread Tools | |
| Display Modes | |
|
|
Similar Threads
|
||||
| Thread | Thread Starter | Forum | Replies | Last Post |
| Change password via bash script | tmw | Userland Programming & Scripting | 6 | December 29th, 2009 02:21 |
| [Solved] Detect crashing application in a script | octix | Userland Programming & Scripting | 2 | December 18th, 2009 05:06 |
| Which mail server, POP server, IMAP server are good for install on FreeBSD 7.2 | Detective | Web & Network Services | 7 | December 4th, 2009 10:05 |
| Check connection with timeout, script telnet web server in script | bsddaemon | Web & Network Services | 5 | December 7th, 2008 14:03 |
| ss.h.i.t alternatives || other brute force blockers? | dave | Installation and Maintenance of FreeBSD Ports or Packages | 12 | November 30th, 2008 00:28 |