Mercurial > hg > maltfilter
comparison maltfilter @ 69:b090ddfccdab
Cleanups. Improve IP/host definitions.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 26 Aug 2009 15:19:08 +0300 |
parents | bac5931b8312 |
children | adb4795f451e |
comparison
equal
deleted
inserted
replaced
68:bac5931b8312 | 69:b090ddfccdab |
---|---|
10 use Date::Parse; | 10 use Date::Parse; |
11 use Net::IP; | 11 use Net::IP; |
12 use Net::DNS; | 12 use Net::DNS; |
13 use LWP::UserAgent; | 13 use LWP::UserAgent; |
14 | 14 |
15 my $progversion = "0.16.0"; | 15 my $progversion = "0.16.2"; |
16 my $progbanner = | 16 my $progbanner = |
17 "Malicious Attack Livid Termination Filter daemon (maltfilter) v$progversion\n". | 17 "Malicious Attack Livid Termination Filter daemon (maltfilter) v$progversion\n". |
18 "Programmed by Matti 'ccr' Hamalainen <ccr\@tnsp.org>\n". | 18 "Programmed by Matti 'ccr' Hamalainen <ccr\@tnsp.org>\n". |
19 "(C) Copyright 2009 Tecnic Software productions (TNSP)\n"; | 19 "(C) Copyright 2009 Tecnic Software productions (TNSP)\n"; |
20 | 20 |
28 "LOGFILE" => "", | 28 "LOGFILE" => "", |
29 "STATS_MAX_AGE" => 336, # in hours | 29 "STATS_MAX_AGE" => 336, # in hours |
30 | 30 |
31 "PASSWD" => "/etc/passwd", | 31 "PASSWD" => "/etc/passwd", |
32 "SYSACCT_MIN_UID" => 1, | 32 "SYSACCT_MIN_UID" => 1, |
33 "SYSACCT_MAX_UID" => 100, | 33 "SYSACCT_MAX_UID" => 999, |
34 | 34 |
35 "FILTER" => 0, | 35 "FILTER" => 0, |
36 "FILTER_THRESHOLD" => 3, | 36 "FILTER_THRESHOLD" => 3, |
37 "FILTER_MAX_AGE" => 168, # in hours | 37 "FILTER_MAX_AGE" => 168, # in hours |
38 "FILTER_TARGET" => "DROP", | 38 "FILTER_TARGET" => "DROP", |
60 "DRONEBL_MAX_AGE" => 30, # in minutes | 60 "DRONEBL_MAX_AGE" => 30, # in minutes |
61 "DRONEBL_RPC_URI" => "http://dronebl.org/RPC2", | 61 "DRONEBL_RPC_URI" => "http://dronebl.org/RPC2", |
62 "DRONEBL_RPC_KEY" => "", | 62 "DRONEBL_RPC_KEY" => "", |
63 ); | 63 ); |
64 | 64 |
65 # List loopback and private netblocks by default here | |
65 my @noaction_ips_def = ( | 66 my @noaction_ips_def = ( |
66 "127.0.0.1", | 67 "127.0.0.0/8", |
68 "10.0.0.0/8", | |
69 "172.16.0.0/12", | |
70 "192.168.0.0/16" | |
67 ); | 71 ); |
68 | 72 |
69 my %systemacct = (); | 73 my %systemacct = (); |
70 sub check_add_hit($$$$$$); | 74 sub check_add_hit($$$$$$); |
71 | 75 |
139 ### Global variables | 143 ### Global variables |
140 ############################################################################# | 144 ############################################################################# |
141 my $reportmode = 0; # Full report mode | 145 my $reportmode = 0; # Full report mode |
142 my @scanfiles = (); # Files to scan | 146 my @scanfiles = (); # Files to scan |
143 my @scanfiles_once = (); # Files to scan only once during startup or HUP (e.g. not continuously followed) | 147 my @scanfiles_once = (); # Files to scan only once during startup or HUP (e.g. not continuously followed) |
144 my @noaction_ips = (); # IPs not to block | 148 my @noaction_ips = (); # IPs not to filter |
145 my %filehandles = (); # Global hash holding opened scanned log filehandles | 149 my %filehandles = (); # Global hash holding opened scanned log filehandles |
146 my $pid_file = ""; # Name of Maltfilter daemon pid file | 150 my $pid_file = ""; # Name of Maltfilter daemon pid file |
147 my @configfiles = (); # Array of configuration file names | 151 my @configfiles = (); # Array of configuration file names |
148 my $LOGFILE; # Maltfilter logfile handle | 152 my $LOGFILE; # Maltfilter logfile handle |
149 my %dronebl = (); | 153 my %dronebl = (); |
154 # Gathered information about hosts | 158 # Gathered information about hosts |
155 # $statlist{$ip}-> | 159 # $statlist{$ip}-> |
156 # "date1" = timestamp of first hit | 160 # "date1" = timestamp of first hit |
157 # "date2" = timestamp of latest hit | 161 # "date2" = timestamp of latest hit |
158 # "hits" = number of hits to this IP | 162 # "hits" = number of hits to this IP |
163 # "dronebl" = 1 == queued for submission, 2 == submitted | |
159 # $statlist{$ip}{"reason"}{$class}-> | 164 # $statlist{$ip}{"reason"}{$class}-> |
160 # "msg" = reason message (array if $reportmode) | 165 # "msg" = reason message (array if $reportmode) |
161 # "hits" = hits to this class | 166 # "hits" = hits to this class |
162 # "date1" = timestamp of first hit | 167 # "date1" = timestamp of first hit |
163 # "date2" = timestamp of latest hit | 168 # "date2" = timestamp of latest hit |
524 # Create submission data | 529 # Create submission data |
525 my $xml = "<?xml version=\"1.0\"?>\n<request key=\"".$settings{"DRONEBL_RPC_KEY"}."\">\n"; | 530 my $xml = "<?xml version=\"1.0\"?>\n<request key=\"".$settings{"DRONEBL_RPC_KEY"}."\">\n"; |
526 my $entries = 0; | 531 my $entries = 0; |
527 while (my ($ip, $entry) = each(%dronebl)) { | 532 while (my ($ip, $entry) = each(%dronebl)) { |
528 if ($entry->{"sent"} == 0 && $entry->{"tries"} < 3) { | 533 if ($entry->{"sent"} == 0 && $entry->{"tries"} < 3) { |
529 $xml .= "<add ip=\"".$ip."\" type=\"".$entry->{"type"}."\" />\n"; | 534 # $xml .= "<add ip=\"".$ip."\" type=\"".$entry->{"type"}."\" />\n"; |
535 $xml .= "<add ip=\"".$ip."\" type=\"1\" />\n"; | |
530 $entries++; | 536 $entries++; |
531 } | 537 } |
532 } | 538 } |
533 $xml .= "</request>\n"; | 539 $xml .= "</request>\n"; |
534 | 540 |
539 # return; | 545 # return; |
540 } else { | 546 } else { |
541 mlog(1, "[DroneBL] Trying to submit $entries entries.\n"); | 547 mlog(1, "[DroneBL] Trying to submit $entries entries.\n"); |
542 } | 548 } |
543 | 549 |
550 if (0) { | |
544 # Submit via HTTP XML-RPC | 551 # Submit via HTTP XML-RPC |
545 my $tmp = LWP::UserAgent->new; | 552 my $tmp = LWP::UserAgent->new; |
546 $tmp->agent("Maltfilter/".$progversion); | 553 $tmp->agent("Maltfilter/".$progversion); |
547 $tmp->timeout(10); | 554 $tmp->timeout(10); |
548 my $req = HTTP::Request->new(POST => $settings{"DRONEBL_RPC_URI"}); | 555 my $req = HTTP::Request->new(POST => $settings{"DRONEBL_RPC_URI"}); |
550 $req->content($xml); | 557 $req->content($xml); |
551 $req->user_agent("Maltfilter/".$progversion); | 558 $req->user_agent("Maltfilter/".$progversion); |
552 my $res = $tmp->request($req); | 559 my $res = $tmp->request($req); |
553 | 560 |
554 if ($res->is_success) { | 561 if ($res->is_success) { |
555 mlog(2, "[DroneBL] [".$res->code."] ".$res->message."\n"); | 562 mlog(3, "[DroneBL] [".$res->code."] ".$res->message."\n"); |
556 print $res->content."\n"; | 563 print $res->content."\n"; |
564 | |
565 | |
557 # while (my ($ip, $entry) = each(%dronebl)) { | 566 # while (my ($ip, $entry) = each(%dronebl)) { |
558 # $entry->{"sent"} = 1; | 567 # $entry->{"sent"} = 1; |
568 # $statlist{$ip}{"dronebl"} = 2 if (defined($statlist{$ip})); | |
559 # } | 569 # } |
560 } else { | 570 } else { |
561 mlog(-1, "[DroneBL] Submission failed: [".$res->code."] ".$res->message."\n"); | 571 mlog(-1, "[DroneBL] Submission failed: [".$res->code."] ".$res->message."\n"); |
562 } | 572 } |
563 | 573 } |
564 # Remove submitted expired entries | 574 |
575 # Clean up expired entries, warning about unsubmitted ones. | |
565 while (my ($ip, $entry) = each(%dronebl)) { | 576 while (my ($ip, $entry) = each(%dronebl)) { |
566 if (!check_time3($entry->{"date"})) { | 577 if (!check_time3($entry->{"date"})) { |
567 mlog(1, "[DroneBL] $ip submission expired.\n") unless ($entry->{"sent"} > 0); | 578 mlog(1, "[DroneBL] $ip submission expired.\n") unless ($entry->{"sent"} > 0); |
568 delete($dronebl{$ip}); | 579 delete($dronebl{$ip}); |
569 } | 580 } |
571 } | 582 } |
572 | 583 |
573 sub dronebl_queue($$$) | 584 sub dronebl_queue($$$) |
574 { | 585 { |
575 my ($mip, $mdate, $mtype) = @_; | 586 my ($mip, $mdate, $mtype) = @_; |
587 | |
588 return unless ($settings{"DRONEBL"} > 0); | |
589 return if check_hosts_array(\@noaction_ips, $mip); | |
590 | |
576 if (!defined($dronebl{$mip})) { | 591 if (!defined($dronebl{$mip})) { |
577 mlog(3, "[DroneBL] Queueing $mip \@ $mdate ($mtype)\n"); | 592 mlog(3, "[DroneBL] Queueing $mip \@ $mdate ($mtype)\n"); |
578 $dronebl{$mip}{"type"} = $mtype; | 593 $dronebl{$mip}{"type"} = $mtype; |
579 $dronebl{$mip}{"date"} = $mdate; | 594 $dronebl{$mip}{"date"} = $mdate; |
580 $dronebl{$mip}{"sent"} = 0; | 595 $dronebl{$mip}{"sent"} = 0; |
581 $dronebl{$mip}{"tries"} = 0; | 596 $dronebl{$mip}{"tries"} = 0; |
597 $statlist{$mip}{"dronebl"} = 1 if (defined($statlist{$mip})); | |
582 } | 598 } |
583 } | 599 } |
584 | 600 |
585 ############################################################################# | 601 ############################################################################# |
586 ### Evidence gathering | 602 ### Evidence gathering |
697 sub check_hosts_array($$) | 713 sub check_hosts_array($$) |
698 { | 714 { |
699 my $chk_host = $_[1]; | 715 my $chk_host = $_[1]; |
700 my $chk_ip = new Net::IP($chk_host); | 716 my $chk_ip = new Net::IP($chk_host); |
701 foreach my $host (@{$_[0]}) { | 717 foreach my $host (@{$_[0]}) { |
702 if ($chk_host eq $host) { | |
703 return 1; | |
704 } | |
705 my $ip = new Net::IP($host); | 718 my $ip = new Net::IP($host); |
706 if (defined($chk_ip) && defined($ip)) { | 719 if (defined($chk_ip) && defined($ip)) { |
707 if ($chk_ip->binip() eq $ip->binip()) { | 720 my $res = $chk_ip->overlaps($ip); |
708 return 1; | 721 if (defined($res)) { |
709 } | 722 return 1 if ($res == $IP_IDENTICAL); |
710 } | 723 return 2 if ($res == $IP_B_IN_A_OVERLAP); |
724 return 3 if ($res == $IP_A_IN_B_OVERLAP); | |
725 } | |
726 } | |
727 return 4 if ($chk_host eq $host); | |
711 } | 728 } |
712 return 0; | 729 return 0; |
713 } | 730 } |
714 | 731 |
715 ### Check IP/host against | separated list of IPs/hosts | 732 ### Check IP/host against | separated list of IPs/hosts |
847 | 864 |
848 sub update_entry($$$$$$) | 865 sub update_entry($$$$$$) |
849 { | 866 { |
850 my ($struct, $mip, $mdate, $mclass, $mreason, $addhits) = @_; | 867 my ($struct, $mip, $mdate, $mclass, $mreason, $addhits) = @_; |
851 | 868 |
869 return if check_hosts_array(\@noaction_ips, $mip); | |
870 | |
852 $struct->{$mip} = {} unless defined($struct->{$mip}); | 871 $struct->{$mip} = {} unless defined($struct->{$mip}); |
853 my $entry = $struct->{$mip}; | 872 my $entry = $struct->{$mip}; |
854 $entry->{"reason"}{$mclass} = {} unless defined($entry->{"reason"}{$mclass}); | 873 $entry->{"reason"}{$mclass} = {} unless defined($entry->{"reason"}{$mclass}); |
855 my $reason = $entry->{"reason"}{$mclass}; | 874 my $reason = $entry->{"reason"}{$mclass}; |
856 | 875 |
913 # Update date of last hit | 932 # Update date of last hit |
914 $filterlist{$mip} = $mdate; | 933 $filterlist{$mip} = $mdate; |
915 } | 934 } |
916 | 935 |
917 # Separate check for DroneBL | 936 # Separate check for DroneBL |
918 if ($settings{"DRONEBL"} > 0 && $mtype > 0 && $cnt >= $settings{"DRONEBL_THRESHOLD"} && check_time3($mdate)) { | 937 if ($mtype > 0 && $cnt >= $settings{"DRONEBL_THRESHOLD"} && check_time3($mdate)) { |
919 dronebl_queue($mip, $mdate, $mtype); | 938 dronebl_queue($mip, $mdate, $mtype); |
920 } | 939 } |
921 } | 940 } |
922 | 941 |
923 | 942 |
1090 if ($key eq "SCANFILE") { | 1109 if ($key eq "SCANFILE") { |
1091 push(@scanfiles, $value); | 1110 push(@scanfiles, $value); |
1092 } elsif ($key eq "SCANFILE_ONCE") { | 1111 } elsif ($key eq "SCANFILE_ONCE") { |
1093 push(@scanfiles_once, $value); | 1112 push(@scanfiles_once, $value); |
1094 } elsif ($key eq "NOACTION_IPS") { | 1113 } elsif ($key eq "NOACTION_IPS") { |
1095 push(@noaction_ips_def, $value); | 1114 push(@noaction_ips, $value); |
1096 } elsif (defined($settings{$key})) { | 1115 } elsif (defined($settings{$key})) { |
1097 $settings{$key} = $value; | 1116 $settings{$key} = $value; |
1098 } else { | 1117 } else { |
1099 mlog(-1, "[$filename:$line] Unknown setting '$key' = '$value'\n"); | 1118 mlog(-1, "[$filename:$line] Unknown setting '$key' = '$value'\n"); |
1100 $errors = 1; | 1119 $errors = 1; |
1119 @scanfiles = (); | 1138 @scanfiles = (); |
1120 undef(@scanfiles); | 1139 undef(@scanfiles); |
1121 | 1140 |
1122 @scanfiles_once = (); | 1141 @scanfiles_once = (); |
1123 undef(@scanfiles_once); | 1142 undef(@scanfiles_once); |
1143 | |
1144 @noaction_ips = (); | |
1145 undef(@noaction_ips); | |
1124 | 1146 |
1125 foreach my $filename (@configfiles) { | 1147 foreach my $filename (@configfiles) { |
1126 mdie("Errors in configuration file '$filename', bailing out.\n") | 1148 mdie("Errors in configuration file '$filename', bailing out.\n") |
1127 unless (malt_read_config($filename) == 0); | 1149 unless (malt_read_config($filename) == 0); |
1128 } | 1150 } |
1133 | 1155 |
1134 %saw = (); | 1156 %saw = (); |
1135 @scanfiles_once = grep(!$saw{$_}++, @scanfiles_once); | 1157 @scanfiles_once = grep(!$saw{$_}++, @scanfiles_once); |
1136 | 1158 |
1137 %saw = (); | 1159 %saw = (); |
1138 @noaction_ips = grep(!$saw{$_}++, @noaction_ips_def); | 1160 push(@noaction_ips, @noaction_ips_def); |
1161 @noaction_ips = grep(!$saw{$_}++, @noaction_ips); | |
1139 undef(%saw); | 1162 undef(%saw); |
1140 | 1163 |
1141 mlog(-1, "Not acting on IPs: ".join(", ", @noaction_ips)."\n"); | 1164 mlog(-1, "Not acting on IPs: ".join(", ", @noaction_ips)."\n"); |
1142 | 1165 |
1143 # Check if we have anything to do | 1166 # Check if we have anything to do |