Mercurial > hg > maltfilter
comparison maltfilter @ 70:adb4795f451e maltfilter-0.17.0
Finished DroneBL support (hopefully).
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Thu, 27 Aug 2009 00:17:42 +0300 |
parents | b090ddfccdab |
children | e8fbe7cd65a7 |
comparison
equal
deleted
inserted
replaced
69:b090ddfccdab | 70:adb4795f451e |
---|---|
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.2"; | 15 my $progversion = "0.17.0"; |
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 |
158 # Gathered information about hosts | 158 # Gathered information about hosts |
159 # $statlist{$ip}-> | 159 # $statlist{$ip}-> |
160 # "date1" = timestamp of first hit | 160 # "date1" = timestamp of first hit |
161 # "date2" = timestamp of latest hit | 161 # "date2" = timestamp of latest hit |
162 # "hits" = number of hits to this IP | 162 # "hits" = number of hits to this IP |
163 # "dronebl" = 1 == queued for submission, 2 == submitted | 163 # "dronebl" = 0 == n/a, 1 == queued for submission, 2 == submitted |
164 # $statlist{$ip}{"reason"}{$class}-> | 164 # $statlist{$ip}{"reason"}{$class}-> |
165 # "msg" = reason message (array if $reportmode) | 165 # "msg" = reason message (array if $reportmode) |
166 # "hits" = hits to this class | 166 # "hits" = hits to this class |
167 # "date1" = timestamp of first hit | 167 # "date1" = timestamp of first hit |
168 # "date2" = timestamp of latest hit | 168 # "date2" = timestamp of latest hit |
380 | 380 |
381 sub print_table2($$$$$$) | 381 sub print_table2($$$$$$) |
382 { | 382 { |
383 my ($m, $f, $table, $keys, $func, $class) = @_; | 383 my ($m, $f, $table, $keys, $func, $class) = @_; |
384 my $nhits = 0; | 384 my $nhits = 0; |
385 my $str = "<th>IP-address</th><th>Hits</th><th>First hit</th><th>Latest hit</th><th>Class</th>"; | 385 my $str = "<th>IP-address</th><th>Hits</th><th>DroneBL?</th><th>First hit</th><th>Latest hit</th><th>Class</th>"; |
386 my $str2 = "IP-address | Hits | First hit | Latest hit | Class "; | 386 my $str2 = "IP-address | Hits | DroneBL | First hit | Latest hit | Class "; |
387 | 387 |
388 printElem($m, $f, | 388 printElem($m, $f, |
389 "<table class=\"".$class."\">\n<tr>". $str."<th> </th>".$str ."</tr>\n", | 389 "<table class=\"".$class."\">\n<tr>". $str."<th> </th>".$str ."</tr>\n", |
390 $str2." || ".$str2."\n"); | 390 $str2." || ".$str2."\n"); |
391 | 391 |
401 my $str = "style=\"background: ".$ipcolors[$ncolor[$_[1]] % scalar @ipcolors].";\""; | 401 my $str = "style=\"background: ".$ipcolors[$ncolor[$_[1]] % scalar @ipcolors].";\""; |
402 | 402 |
403 printTD($m, $f, sprintf("%-15s", get_link($m, $_[0])), $str); | 403 printTD($m, $f, sprintf("%-15s", get_link($m, $_[0])), $str); |
404 printElem(!$m, $f, " | "); | 404 printElem(!$m, $f, " | "); |
405 printTD($m, $f, sprintf("%-8d ", $table->{$_[0]}{"hits"}), $blocked); | 405 printTD($m, $f, sprintf("%-8d ", $table->{$_[0]}{"hits"}), $blocked); |
406 printElem(!$m, $f, " | "); | |
407 printTD($m, $f, sprintf("%-6s ", $table->{$_[0]}{"dronebl"}), $blocked); | |
406 printElem(!$m, $f, " | "); | 408 printElem(!$m, $f, " | "); |
407 printTD($m, $f, get_ago_str($table->{$_[0]}{"date1"}), $blocked); | 409 printTD($m, $f, get_ago_str($table->{$_[0]}{"date1"}), $blocked); |
408 printElem(!$m, $f, " | "); | 410 printElem(!$m, $f, " | "); |
409 printTD($m, $f, get_ago_str($table->{$_[0]}{"date2"}), $blocked); | 411 printTD($m, $f, get_ago_str($table->{$_[0]}{"date2"}), $blocked); |
410 printElem(!$m, $f, " | "); | 412 printElem(!$m, $f, " | "); |
529 # Create submission data | 531 # Create submission data |
530 my $xml = "<?xml version=\"1.0\"?>\n<request key=\"".$settings{"DRONEBL_RPC_KEY"}."\">\n"; | 532 my $xml = "<?xml version=\"1.0\"?>\n<request key=\"".$settings{"DRONEBL_RPC_KEY"}."\">\n"; |
531 my $entries = 0; | 533 my $entries = 0; |
532 while (my ($ip, $entry) = each(%dronebl)) { | 534 while (my ($ip, $entry) = each(%dronebl)) { |
533 if ($entry->{"sent"} == 0 && $entry->{"tries"} < 3) { | 535 if ($entry->{"sent"} == 0 && $entry->{"tries"} < 3) { |
534 # $xml .= "<add ip=\"".$ip."\" type=\"".$entry->{"type"}."\" />\n"; | 536 $xml .= "<add ip=\"".$ip."\" type=\"".$entry->{"type"}."\" />\n"; |
535 $xml .= "<add ip=\"".$ip."\" type=\"1\" />\n"; | 537 # $xml .= "<add ip=\"".$ip."\" type=\"1\" />\n"; |
536 $entries++; | 538 $entries++; |
537 } | 539 } |
538 } | 540 } |
539 $xml .= "</request>\n"; | 541 $xml .= "</request>\n"; |
540 | 542 |
541 # Bait out if no entries to submit | 543 # Bait out if no entries to submit |
542 return unless ($entries > 0); | 544 return unless ($entries > 0); |
543 if ($settings{"DRY_RUN"}) { | 545 if ($settings{"DRY_RUN"}) { |
544 mlog(1, "[DroneBL] Would submit $entries entries.\n"); | 546 mlog(2, "[DroneBL] Would submit $entries entries.\n"); |
545 # return; | 547 return; |
546 } else { | 548 } else { |
547 mlog(1, "[DroneBL] Trying to submit $entries entries.\n"); | 549 mlog(2, "[DroneBL] Trying to submit $entries entries.\n"); |
548 } | 550 } |
549 | 551 |
550 if (0) { | |
551 # Submit via HTTP XML-RPC | 552 # Submit via HTTP XML-RPC |
552 my $tmp = LWP::UserAgent->new; | 553 my $tmp = LWP::UserAgent->new; |
553 $tmp->agent("Maltfilter/".$progversion); | 554 $tmp->agent("Maltfilter/".$progversion); |
554 $tmp->timeout(10); | 555 $tmp->timeout(10); |
555 my $req = HTTP::Request->new(POST => $settings{"DRONEBL_RPC_URI"}); | 556 my $req = HTTP::Request->new(POST => $settings{"DRONEBL_RPC_URI"}); |
557 $req->content($xml); | 558 $req->content($xml); |
558 $req->user_agent("Maltfilter/".$progversion); | 559 $req->user_agent("Maltfilter/".$progversion); |
559 my $res = $tmp->request($req); | 560 my $res = $tmp->request($req); |
560 | 561 |
561 if ($res->is_success) { | 562 if ($res->is_success) { |
562 mlog(3, "[DroneBL] [".$res->code."] ".$res->message."\n"); | 563 mlog(3, "[DroneBL] HTTP response [".$res->code."] ".$res->message."\n"); |
563 print $res->content."\n"; | 564 my $str = $res->content; |
565 my ($type, $msg); | |
566 $str =~ tr/\n/ /; | |
567 | |
568 if ($str =~ /<response\s*type=.(success|error).>(.*?)<\/response>/gm) { | |
569 $type = $1; $msg = $2; | |
570 } elsif ($str =~ /<response\s*type=.(success|error). *\/>/gm) { | |
571 $type = $1; $msg = ""; | |
572 } | |
564 | 573 |
565 | 574 if ($type eq "success") { |
566 # while (my ($ip, $entry) = each(%dronebl)) { | 575 mlog(1, "[DroneBL] Succesfully submitted $entries entries.\n$msg\n"); |
567 # $entry->{"sent"} = 1; | 576 while (my ($ip, $entry) = each(%dronebl)) { |
568 # $statlist{$ip}{"dronebl"} = 2 if (defined($statlist{$ip})); | 577 $entry->{"sent"} = 1; |
569 # } | 578 $statlist{$ip}{"dronebl"} = 2 if defined($statlist{$ip}); |
570 } else { | 579 } |
571 mlog(-1, "[DroneBL] Submission failed: [".$res->code."] ".$res->message."\n"); | 580 } elsif ($type eq "error") { |
572 } | 581 # If we don't have a valid key, disable further submissions. |
573 } | 582 if ($msg =~ /<code>403<\/code>/) { |
574 | 583 mlog(-1, "Disabling DroneBL submission due to invalid key.\n"); |
575 # Clean up expired entries, warning about unsubmitted ones. | 584 $settings{"DRONEBL"} = 0; |
585 } | |
586 # Log error message mangled | |
587 $msg =~ s{\s*</?[^>]+>}{ }g; | |
588 mlog(-1, "[DroneBL] Error in submission: $msg\n"); | |
589 } else { | |
590 mlog(-1, "[DroneBL] Unsupported response message ".$str."\n"); | |
591 } | |
592 } else { | |
593 mlog(-1, "[DroneBL] HTTP request failed: [".$res->code."] ".$res->message."\n"); | |
594 } | |
595 | |
596 # Clean up expired entries, warn/note about unsubmitted ones. | |
576 while (my ($ip, $entry) = each(%dronebl)) { | 597 while (my ($ip, $entry) = each(%dronebl)) { |
577 if (!check_time3($entry->{"date"})) { | 598 if (!check_time3($entry->{"date"})) { |
578 mlog(1, "[DroneBL] $ip submission expired.\n") unless ($entry->{"sent"} > 0); | 599 mlog(1, "[DroneBL] $ip submission expired.\n") unless ($entry->{"sent"} > 0); |
579 delete($dronebl{$ip}); | 600 delete($dronebl{$ip}); |
580 } | 601 } |
592 mlog(3, "[DroneBL] Queueing $mip \@ $mdate ($mtype)\n"); | 613 mlog(3, "[DroneBL] Queueing $mip \@ $mdate ($mtype)\n"); |
593 $dronebl{$mip}{"type"} = $mtype; | 614 $dronebl{$mip}{"type"} = $mtype; |
594 $dronebl{$mip}{"date"} = $mdate; | 615 $dronebl{$mip}{"date"} = $mdate; |
595 $dronebl{$mip}{"sent"} = 0; | 616 $dronebl{$mip}{"sent"} = 0; |
596 $dronebl{$mip}{"tries"} = 0; | 617 $dronebl{$mip}{"tries"} = 0; |
597 $statlist{$mip}{"dronebl"} = 1 if (defined($statlist{$mip})); | 618 $statlist{$mip}{"dronebl"} = 1 if defined($statlist{$mip}); |
598 } | 619 } |
599 } | 620 } |
600 | 621 |
601 ############################################################################# | 622 ############################################################################# |
602 ### Evidence gathering | 623 ### Evidence gathering |
627 $tmp->default_headers->referer($_[1]); | 648 $tmp->default_headers->referer($_[1]); |
628 my $req = HTTP::Request->new(GET => $_[0]); | 649 my $req = HTTP::Request->new(GET => $_[0]); |
629 return $tmp->request($req); | 650 return $tmp->request($req); |
630 } | 651 } |
631 | 652 |
653 my $evidence_dir = 0; | |
632 sub evidence_gather | 654 sub evidence_gather |
633 { | 655 { |
634 my $dns = Net::DNS::Resolver->new; | 656 my $dns = Net::DNS::Resolver->new; |
635 my $base = $settings{"EVIDENCE_DIR"}; | 657 my $base = $settings{"EVIDENCE_DIR"}; |
636 | 658 |
637 return unless ($settings{"EVIDENCE"} > 0); | 659 return unless ($settings{"EVIDENCE"} > 0); |
638 | 660 |
639 mdie("Evidence directory '$base' has disappeared.\n") unless (-e $base); | 661 if (! -e $base) { |
640 | 662 mlog(-1, "Evidence directory '$base' has disappeared.\n") unless ($evidence_dir > 0); |
663 mdie("Evidence directory '$base' has been absent for $evidence_dir cycles, dying.\n") if ($evidence_dir++ > 10); | |
664 return; | |
665 } else { | |
666 $evidence_dir = 0; | |
667 } | |
668 | |
669 my $fetched = 0; | |
641 foreach my $url (keys %evidence) { | 670 foreach my $url (keys %evidence) { |
642 my $did_fetch = 0; | |
643 my $filename = $base."/".$evidence{$url}{"coll"}.".data"; | 671 my $filename = $base."/".$evidence{$url}{"coll"}.".data"; |
644 my $filename2 = $base."/".$evidence{$url}{"coll"}.".hosts"; | 672 my $filename2 = $base."/".$evidence{$url}{"coll"}.".hosts"; |
645 my $filename3 = $base."/".$evidence{$url}{"coll"}.".info"; | 673 my $filename3 = $base."/".$evidence{$url}{"coll"}.".info"; |
646 | 674 |
647 # Get data contents only once | 675 # Get data contents only once |
648 if (! -e $filename) { | 676 if (! -e $filename) { |
649 $did_fetch = 1; | 677 $fetched++; |
650 mlog(1, "Fetching evidence for $url\n"); | 678 mlog(1, "Fetching evidence for $url\n"); |
651 my $res = evidence_fetch($url, ""); | 679 my $res = evidence_fetch($url, ""); |
652 open(FILE, ">:raw", $filename) or mdie("Could not open '$filename' for writing.\n"); | 680 open(FILE, ">:raw", $filename) or mdie("Could not open '$filename' for writing.\n"); |
653 binmode(FILE, ":raw"); | 681 binmode(FILE, ":raw"); |
654 if ($res->is_success && $res->code >= 200 && $res->code <= 201) { | 682 if ($res->is_success && $res->code >= 200 && $res->code <= 201) { |
698 close(FILE); | 726 close(FILE); |
699 | 727 |
700 # This entry has been handled, delete it | 728 # This entry has been handled, delete it |
701 delete($evidence{$url}); | 729 delete($evidence{$url}); |
702 | 730 |
703 # If not in report mode, handle only one fetched entry | 731 # If not in report mode, handle only 5 fetched entries at time |
704 return unless ($reportmode || !$did_fetch); | 732 return unless ($reportmode || $fetched < 5); |
705 } | 733 } |
706 } | 734 } |
707 | 735 |
708 | 736 |
709 ############################################################################# | 737 ############################################################################# |
871 $struct->{$mip} = {} unless defined($struct->{$mip}); | 899 $struct->{$mip} = {} unless defined($struct->{$mip}); |
872 my $entry = $struct->{$mip}; | 900 my $entry = $struct->{$mip}; |
873 $entry->{"reason"}{$mclass} = {} unless defined($entry->{"reason"}{$mclass}); | 901 $entry->{"reason"}{$mclass} = {} unless defined($entry->{"reason"}{$mclass}); |
874 my $reason = $entry->{"reason"}{$mclass}; | 902 my $reason = $entry->{"reason"}{$mclass}; |
875 | 903 |
904 $entry->{"dronebl"} = 0 unless defined($entry->{"dronebl"}); | |
905 | |
876 # Add hits only when requested | 906 # Add hits only when requested |
877 if ($addhits) { | 907 if ($addhits) { |
878 $entry->{"hits"}++; | 908 $entry->{"hits"}++; |
879 $reason->{"hits"}++; | 909 $reason->{"hits"}++; |
880 } else { | 910 } else { |
1072 if ($counter < 0 || $counter++ >= 30) { | 1102 if ($counter < 0 || $counter++ >= 30) { |
1073 # Every once in a while, execute maintenance functions | 1103 # Every once in a while, execute maintenance functions |
1074 $counter = 0; | 1104 $counter = 0; |
1075 malt_maintenance(); | 1105 malt_maintenance(); |
1076 } | 1106 } |
1077 sleep(2); | 1107 sleep(1); |
1078 foreach my $filename (keys %filehandles) { | 1108 foreach my $filename (keys %filehandles) { |
1079 seek($filehandles{$filename}, $filepos{$filename}, 0); | 1109 seek($filehandles{$filename}, $filepos{$filename}, 0); |
1080 } | 1110 } |
1081 } | 1111 } |
1082 } | 1112 } |