# HG changeset patch # User Matti Hamalainen # Date 1250375413 -10800 # Node ID d96229159abcdfe3a1f525734776ff231eac8779 # Parent e1e28a76317f122bd25474df9ecb2a24d74a0e74 v0.11.0: More fixes; Configuration files are now re-read when HUP signal is received; Multiple configuration files feature actually works now. diff -r e1e28a76317f -r d96229159abc README --- a/README Sun Aug 16 00:54:21 2009 +0300 +++ b/README Sun Aug 16 01:30:13 2009 +0300 @@ -1,4 +1,4 @@ -Malicious Attack Livid Termination Filter daemon (maltfilter) v0.10.4 +Malicious Attack Livid Termination Filter daemon (maltfilter) v0.11.0 ===================================================================== Programmed by Matti 'ccr' Hämäläinen (C) Copyright 2009 Tecnic Software productions (TNSP) diff -r e1e28a76317f -r d96229159abc maltfilter --- a/maltfilter Sun Aug 16 00:54:21 2009 +0300 +++ b/maltfilter Sun Aug 16 01:30:13 2009 +0300 @@ -10,7 +10,7 @@ use Date::Parse; use Net::IP; -my $progversion = "0.10.4"; +my $progversion = "0.11.0"; my $progbanner = "Malicious Attack Livid Termination Filter daemon (maltfilter) v$progversion\n". "Programmed by Matti 'ccr' Hamalainen \n". @@ -40,6 +40,7 @@ "CHK_PHP_XSS" => 1, "CHK_PROXY_SCAN" => 1, "CHK_ROOT_SSH_PWD" => 0, + "CHK_SYSACCT_SSH_PWD" => 0, "CHK_GOOD_HOSTS" => "", ); @@ -62,7 +63,9 @@ my @noblock_ips = (); # IPs not to block my %filehandles = (); # Global hash holding opened scanned log filehandles my $pid_file = ""; # Name of Maltfilter daemon pid file +my @configfiles = (); # Array of configuration file names my $LOGFILE; # Maltfilter logfile handle +my %systemacct = (); # IPs currently blocked in Netfilter $blocklist{$ip} = date my %blocklist = (); @@ -93,15 +96,26 @@ my $merr = $2; # (1.1) Generic login scan attempts - if ($merr =~ /^Failed password for invalid user \S+ from (\d+\.\d+\.\d+\.\d+)/) { - check_add_hit($1, $mdate, "SSH login scan", "", $settings{"CHK_SSHD"}); + if ($merr =~ /^Failed password for invalid user (\S+) from (\d+\.\d+\.\d+\.\d+)/) { + check_add_hit($2, $mdate, "SSH login scan", $1, $settings{"CHK_SSHD"}); } - # (1.2) Root SSH login password bruteforcing attempts + # (1.2) Root account SSH login password bruteforcing attempts. # NOTICE! Do not enable this setting, if you allow SSH root logins via - # password authentication! Mistyping password may get you blocked then. :) + # password authentication! Mistyping password may get you blocked unless + # your host IP is defined in NOBLOCK_IPS! elsif (/^Failed password for root from (\d+\.\d+\.\d+\.\d+)/) { check_add_hit($1, $mdate, "Root SSH password bruteforce", "", $settings{"CHK_ROOT_SSH_PWD"}); } + # (1.3) System account SSH login password bruteforcing attempts. + # NOTICE! If you enable this setting, make sure have defined safe + # host IPs in NOBLOCK_IPS, and that your system DOES NOT have passwords + # for system accounts (UID < 100) .. which would be stupid anyway. + if ($merr =~ /^Failed password for (\S+) from (\d+\.\d+\.\d+\.\d+)/) { + my $mip = $2; my $macct = $1; + if (defined($systemacct{$macct})) { + check_add_hit($mip, $mdate, "SSH system account bruteforce", $macct, $settings{"CHK_SYSACCT_SSH_PWD"}); + } + } } # (2) Common/known exploitable CGI/PHP software scans (like phpMyAdmin) # NOTICE! This matches ERRORLOG, thus it only works if you DO NOT have @@ -380,7 +394,7 @@ return unless ($filename ne ""); - open(STATUS, ">", $filename) or die("Could not open '".$filename."'!\n"); + open(STATUS, ">", $filename) or mdie("Could not open '".$filename."'!\n"); my $f = \*STATUS; printElem($m, $f, " @@ -473,7 +487,7 @@ { $ENV{"PATH"} = ""; open(STATUS, $settings{"IPTABLES"}." -v -n -L INPUT |") or - die("Could not execute ".$settings{"IPTABLES"}."\n"); + mdie("Could not execute ".$settings{"IPTABLES"}."\n"); my %newlist = (); undef(%newlist); while () { @@ -578,12 +592,16 @@ if (!defined($struct->{$mip}{"date1"}) || ($mdate > 0 && $struct->{$mip}{"date1"} < 0)) { $struct->{$mip}{"date1"} = $mdate; } - $struct->{$mip}{"date2"} = $mdate; + if (!defined($struct->{$mip}{"date2"}) || $mdate > $struct->{$mip}{"date2"}) { + $struct->{$mip}{"date2"} = $mdate; + } if (!defined($struct->{$mip}{"reason"}{$mclass}{"date2"}) || ($mdate > 0 && $struct->{$mip}{"reason"}{$mclass}{"date2"} < 0)) { $struct->{$mip}{"reason"}{$mclass}{"date2"} = $mdate; } - $struct->{$mip}{"reason"}{$mclass}{"date2"} = $mdate; + if (!defined($struct->{$mip}{"reason"}{$mclass}{"date2"}) || $mdate > $struct->{$mip}{"reason"}{$mclass}{"date2"}) { + $struct->{$mip}{"reason"}{$mclass}{"date2"} = $mdate; + } return $cnt; } @@ -630,7 +648,7 @@ ### Main helper functions ############################################################################# ### Print log entry -sub mlog +sub mlog($$) { my $level = shift; my $msg = shift; @@ -641,16 +659,23 @@ } } +sub mdie($) +{ + mlog(-1, $_[0]); + die($_[0]); +} + ### Initialize sub malt_init { mlog(0, "Updating initial blocklist from netfilter.\n"); + malt_configure(); update_blocklist(); foreach my $filename (@scanfiles) { local *INFILE; mlog(0, "Parsing ".$filename." ...\n"); - open(INFILE, "<", $filename) or die("Could not open '".$filename."'!\n"); + open(INFILE, "<", $filename) or mdie("Could not open '".$filename."'!\n"); $filehandles{$filename} = *INFILE; while () { chomp; @@ -702,6 +727,7 @@ { mlog(-1, "Received HUP, reinitializing.\n"); malt_cleanup(); + malt_configure(); malt_init(); mlog(-1, "Reinitialization finished, resuming scanning.\n"); } @@ -714,7 +740,9 @@ while (1) { my %filepos = (); foreach my $filename (keys %filehandles) { - for ($filepos{$filename} = tell($filehandles{$filename}); $_ = <$filehandles{$filename}>; $filepos{$filename} = tell($filehandles{$filename})) { + for ($filepos{$filename} = tell($filehandles{$filename}); + $_ = <$filehandles{$filename}>; + $filepos{$filename} = tell($filehandles{$filename})) { chomp; check_log_line($_); } @@ -743,7 +771,7 @@ my $errors = 0; my $line = 0; - open(CONFFILE, "<", $filename) or die("Could not open configuration '".$filename."'!\n"); + open(CONFFILE, "<", $filename) or mdie("Could not open configuration '".$filename."'!\n"); while () { $line++; chomp; @@ -755,7 +783,7 @@ if (defined($settings{$key})) { $settings{$key} = $value; } else { - print STDERR "[$filename:$line] Unknown setting '$key' = $value\n"; + mlog(-1, "[$filename:$line] Unknown setting '$key' = $value\n"); $errors = 1; } } elsif (/^\s*\"?([a-zA-Z0-9_]+)\"?\s*=>?\s*\"(.*?)\",?\s*$/) { @@ -768,11 +796,11 @@ } elsif (defined($settings{$key})) { $settings{$key} = $value; } else { - print STDERR "[$filename:$line] Unknown setting '$key' = '$value'\n"; + mlog(-1, "[$filename:$line] Unknown setting '$key' = '$value'\n"); $errors = 1; } } else { - print STDERR "[$filename:$line] Syntax error: $_\n"; + mlog(-1, "[$filename:$line] Syntax error: $_\n"); $errors = 1; } } @@ -780,6 +808,18 @@ return $errors; } +### Read all configuration files +sub malt_configure +{ + # Let user define his/her own logfiles to scan + mlog(0, "(Re)reading configuration files.\n"); + @scanfiles_def = (); + undef(@scanfiles_def); + foreach my $filename (@configfiles) { + mdie("Errors in configuration file '$filename', bailing out.\n") + unless (malt_read_config($filename) == 0); + } +} ############################################################################# ### @@ -807,20 +847,18 @@ if ($pid_file eq "-f") { $reportmode = 1; } else { - die("'$pid_file' already exists, not starting.\n". + mdie("'$pid_file' already exists, not starting.\n". "If the daemon is NOT running, remove the pid-file and re-start.\n") if (-e $pid_file); } # Read configuration files -if (defined(my $filename = shift)) { - # Let user define his/her own logfiles to scan - @scanfiles_def = (); - undef(@scanfiles_def); - die("Errors in configuration file '$filename', bailing out.\n") - unless (malt_read_config($filename) == 0); +while (defined(my $filename = shift)) { + push(@configfiles, $filename); } +malt_configure(); + # Force dry run mode if we are reporting only if ($reportmode) { $settings{"DRY_RUN"} = 1; @@ -849,9 +887,7 @@ # Test existence of iptables if (! -e $settings{"IPTABLES"} || ! -x $settings{"IPTABLES"}) { - my $msg = "iptables binary does not exist or is not executable: ".$settings{"IPTABLES"}."\n"; - mlog(-1, $msg); - die($msg); + mdie("iptables binary does not exist or is not executable: ".$settings{"IPTABLES"}."\n"); } mlog(-1, "Not blocking following IPs: ".join(", ", @noblock_ips)."\n"); @@ -872,7 +908,7 @@ } } else { if (my $pid = fork) { - open(PIDFILE, ">", $pid_file) or die("Could not open pid file '".$pid_file."' for writing!\n"); + open(PIDFILE, ">", $pid_file) or mdie("Could not open pid file '".$pid_file."' for writing!\n"); print PIDFILE "$pid\n"; close(PIDFILE); } else {