# HG changeset patch # User Matti Hamalainen # Date 1250174497 -10800 # Node ID 3da95f3082d93af28f37d24714bf79a0ac62a1ff # Parent ff602dc88d9ea9ac788621535f070d9a45d4fe5c Misc. variable name cleanups. diff -r ff602dc88d9e -r 3da95f3082d9 maltfilter --- a/maltfilter Thu Aug 13 16:22:38 2009 +0300 +++ b/maltfilter Thu Aug 13 17:41:37 2009 +0300 @@ -42,16 +42,67 @@ "/var/log/httpd/access.log" ); -my @scanfiles = (); ############################################################################# ### Script code ############################################################################# +my @scanfiles = (); +my %filehandles = (); my %hitcount = (); my %iplist = (); my $pid_file = ""; my $LOGFILE; +### Check given logfile line for matches +sub check_log_line($) +{ + # (1) SSH login scan attempts + if (/^(\S+\s+\d+\s+\d\d:\d\d:\d\d)\s+\S+\s+sshd\S*?: Failed password for invalid user \S+ from (\d+\.\d+\.\d+\.\d+)/) { + check_add_entry($2, $1, "SSHD", $settings{"CHK_SSHD"}); + } + # (2) Common/known exploitable CGI/PHP software scans (like phpMyAdmin) + # NOTICE! This matches ERRORLOG, thus it only works if you DO NOT have + # any or some of these installed. Preferably none, or use uncommon + # paths and prefixes. + elsif (/^\[(.+?)\]\s+\[error\]\s+\[client\s+(\d+\.\d+\.\d+\.\d+)\]\s+(.+)$/) { + my $mdate = $1; + my $mip = $2; + my $merr = $3; + if ($merr =~ /^File does not exist: (.+)$/) { + my $tmp = $1; + if ($tmp =~ /\/mss2|\/pma|admin|sql|\/roundcube|\/webmail|\/bin|\/mail|xampp|zen|mailto:|appserv|cube|round|_vti_bin|wiki/i) { + check_add_entry($mip, $mdate, "CGI: $tmp", $settings{"CHK_KNOWN_CGI"}); + } + } + } + # Match Apache common logging format GET requests here + elsif (/(\d+\.\d+\.\d+\.\d+)\s+-\s+-\s+\[(.+?)\]\s+\"GET (\S*?) HTTP\//) { + my $mdate = $2; + my $mip = $1; + my $merr = $3; + + # (3) Simple match for generic PHP XSS vulnerability scans + # NOTICE! If your site genuinely uses (checked) PHP parameters with + # URIs, you should set CHK_GOOD_HOSTS to match your hostname(s)/IP(s) + # used in the URIs. + if ($merr =~ /\.php\?\S*?=http:\/\/([^\/]+)/) { + if (!check_hosts($settings{"CHK_GOOD_HOSTS"}, $1)) { + check_add_entry($mip, $mdate, "PHP XSS: $merr", $settings{"CHK_PHP_XSS"}); + } + } + # (4) Try to match proxy scanning attempts + elsif ($merr =~ /^http:\/\/([^\/]+)/) { + if (!check_hosts($settings{"CHK_GOOD_HOSTS"}, $1)) { + check_add_entry($mip, $mdate, "Proxy scan: $merr", $settings{"CHK_PROXY_SCAN"}); + } + } + } +} + + +############################################################################# +### Script code +############################################################################# sub mlog { my $level = shift; @@ -86,7 +137,7 @@ { my @args = ($settings{"IPTABLES"}, @_); if ($settings{"DRY_RUN"}) { - log(3, ":: ".join(" ", @args)."\n"); + mlog(3, ":: ".join(" ", @args)."\n"); } else { system(@args) == 0 or print join(" ", @args)." failed: $?\n"; } @@ -175,64 +226,16 @@ } } -### Check given logfile line for matches -sub check_log_line($) -{ - # (1) SSH login scan attempts - if (/^(\S+\s+\d+\s+\d\d:\d\d:\d\d)\s+\S+\s+sshd\S*?: Failed password for invalid user \S+ from (\d+\.\d+\.\d+\.\d+)/) { - check_add_entry($2, $1, "SSHD", $settings{"CHK_SSHD"}); - } - # (2) Common/known exploitable CGI/PHP software scans (like phpMyAdmin) - # NOTICE! This matches ERRORLOG, thus it only works if you DO NOT have - # any or some of these installed. Preferably none, or use uncommon - # paths and prefixes. - elsif (/^\[(.+?)\]\s+\[error\]\s+\[client\s+(\d+\.\d+\.\d+\.\d+)\]\s+(.+)$/) { - my $mdate = $1; - my $mip = $2; - my $merr = $3; - if ($merr =~ /^File does not exist: (.+)$/) { - my $tmp = $1; - if ($tmp =~ /\/mss2|\/pma|admin|sql|\/roundcube|\/webmail|\/bin|\/mail|xampp|zen|mailto:|appserv|cube|round|_vti_bin|wiki/i) { - check_add_entry($mip, $mdate, "CGI: $tmp", $settings{"CHK_KNOWN_CGI"}); - } - } - } - # Match Apache common logging format GET requests here - elsif (/(\d+\.\d+\.\d+\.\d+)\s+-\s+-\s+\[(.+?)\]\s+\"GET (\S*?) HTTP\//) { - my $mdate = $2; - my $mip = $1; - my $merr = $3; - - # (3) Simple match for generic PHP XSS vulnerability scans - # NOTICE! If your site genuinely uses (checked) PHP parameters with - # URIs, you should set CHK_GOOD_HOSTS to match your hostname(s)/IP(s) - # used in the URIs. - if ($merr =~ /\.php\?\S*?=http:\/\/([^\/]+)/) { - if (!check_hosts($settings{"CHK_GOOD_HOSTS"}, $1)) { - check_add_entry($mip, $mdate, "PHP XSS: $merr", $settings{"CHK_PHP_XSS"}); - } - } - # (4) Try to match proxy scanning attempts - elsif ($merr =~ /^http:\/\/([^\/]+)/) { - if (!check_hosts($settings{"CHK_GOOD_HOSTS"}, $1)) { - check_add_entry($mip, $mdate, "Proxy scan: $merr", $settings{"CHK_PROXY_SCAN"}); - } - } - } -} - - ### ### Utility functions ### -my %logfile = (); sub malt_init { foreach my $logname (@scanfiles) { local *INFILE; mlog(0, "- Parsing ".$logname." ...\n"); open(INFILE, "<", $logname) or die("Could not open '".$logname."'!\n"); - $logfile{$logname} = *INFILE; + $filehandles{$logname} = *INFILE; while () { chomp; check_log_line($_); @@ -245,8 +248,8 @@ sub malt_cleanup { mlog(0, "- Closing open filehandles.\n"); - foreach my $logname (keys %logfile) { - close($logfile{$logname}); + foreach my $logname (keys %filehandles) { + close($filehandles{$logname}); } } @@ -255,9 +258,9 @@ mlog(1, "- Entering main scanning loop.\n"); my $counter = 0; while (1) { - my %logpos = (); - foreach my $logname (keys %logfile) { - for ($logpos{$logname} = tell($logfile{$logname}); $_ = <$logfile{$logname}>; $logpos{$logname} = tell($logfile{$logname})) { + my %filepos = (); + foreach my $logname (keys %filehandles) { + for ($filepos{$logname} = tell($filehandles{$logname}); $_ = <$filehandles{$logname}>; $filepos{$logname} = tell($filehandles{$logname})) { chomp; check_log_line($_); } @@ -271,8 +274,8 @@ update_iplist(time()); weed_entries(); } - foreach my $logname (keys %logfile) { - seek($logfile{$logname}, $logpos{$logname}, 0); + foreach my $logname (keys %filehandles) { + seek($filehandles{$logname}, $filepos{$logname}, 0); } } }