#!/usr/bin/perl -w #dupl.pl GPL license # v0.4 13-08-2000 release2 #Snort rules beautifier # Last version can be found at http://www.norz.org # It remove duplicate rules from snort alert files from # www.snort.org or www.whitehats.com # # For any comments mail zas at norz.org #Usage: cat rules_file | ./dupl.pl 2>log | sort > newrules #or cat rules_file | ./dupl.pl 2>log | sort | uniq > newrules #Then edit newrules for snort config use Digest::MD5 qw(md5_hex); $DEFAULT_SRCNET="HOME_NET"; $DEFAULT_DSTNET="HOME_NET"; sub traiter_ligne() { chomp; s/^\s*//; s/\s*$//; if ($_) { print STDERR "-"x132, "\n"; if (/alert\s+(\S+)\s+([^ \t]+)\s+(\S+)\s+([<-]>)\s+([^ \t]+)\s+(\S+)\s+\((.+)\)$/) { # print STDERR "%% $_\n"; my %rule; $rule{"_pro"}=uc($1); my $src=$2; $rule{"_sp"}=$3; $rule{"_dir"}=$4; my $dst=$5; $rule{"_dp"}=$6; my $text=$7; if ($src=~/\$EXTERNAL$/) { $src=~s/\$EXTERNAL$/!\$$DEFAULT_SRCNET/; } elsif ($src=~/\$INTERNAL$/) { $src=~s/\$INTERNAL$/\$$DEFAULT_SRCNET/; } else { $src=~s/\$[^\/]+$/\$$DEFAULT_SRCNET/; } $rule{"_src"}=$src; if ($dst=~/\$EXTERNAL$/) { $dst=~s/\$EXTERNAL$/!\$$DEFAULT_DSTNET/;} elsif ($dst=~/\$INTERNAL$/) { $dst=~s/\$INTERNAL$/\$$DEFAULT_DSTNET/; } else { $dst=~s/\$[^\/]+$/\$$DEFAULT_DSTNET/; } $rule{"_dst"}=$dst; my @param = (); push(@param, $+) while $text =~ m/( [^\"\:\;]+:\s*" (?: \\[^\"][^\";]? | [^\\;\"] | \\[\";]? )+ "?)\s*; |([^;\"]+)\s*; /gx; for (@param) { if ($_) { chomp; s/^\s*(.*?)\s*$/$1/; # print STDERR ">> $_\n"; $text=$_; my @vars = (); if (/([^\"\:\;]+):\s*"( (?: \\[^\"][^\";]? | [^\\;\"] | \\[\";]? )+ )"?\s*; /x) { $vars[0]=$1; $vars[1]=$2; } elsif (/([^:]+):(.+)/) { $vars[0]=$1; $vars[1]=$2; } else { $vars[0]=$text; $vars[1]=""; }; if ($vars[0]) { $vars[0]=~ s/^\s*(.*?)\s*$/$1/; # print STDERR " 0: $vars[0]\n"; } if ($vars[1]) { $vars[1]=~ s/^\s*("?.*?"?)\s*$/$1/; # print STDERR " 1: $vars[1]\n"; } if ($vars[0]) { my $ni=0; if (lc($vars[0]) eq "ttl") { $vars[1]=~s/[^\d]//g; } if (lc($vars[0]) eq "content") { while ($rule{"content".$ni}) { $ni++; }; my $tmp=$vars[1]; while ($tmp=~/(\|(?:\s?[0-9a-fA-F]+\s?)+\|)/) { $tmp=$'; my $txt1=$1; my $txt2=$1; $txt2=~s/[\s\|]//g; $txt2=~y/[a-f]/[A-F]/; $txt2=~s/([0-9A-F]{2})/$1 /g; $txt2=~s/\s+$//; $vars[1]=~s/\Q$txt1/|$txt2|/; } } if (lc($vars[0]) eq "nocase" or lc($vars[0]) eq "offset" or lc($vars[0]) eq "dep th") { my $nbcase=-1; while ($rule{"content".$ni}) { $ni++; $nbcase++; }; $ni=$nbcase; } # print STDERR "ni $ni\n"; if ($ni!= -1) { $vars[0].=$ni; if ($vars[1] ne "") { $rule{lc($vars[0])}=$vars[1]; } else { $rule{lc($v ars[0])}="1"; }; } } } } my @keys = sort keys %rule; my $s=""; for (@keys) { if ($_ eq "flags0" ) { $rule{$_}=~s/\s+//g; my @tmp=split //,uc($rule{$_}); my $t=0; for (@tmp) { # print STDERR "[$_]"; if (/F/) { $t+=1; } elsif (/S/) { $t+=2; } elsif (/R/) { $t+=4; } elsif (/P/) { $t+=8; } elsif (/A/) { $t+=16; } elsif (/U/) { $t+=32; } elsif (/2/) { $t+=64; } elsif (/1/) { $t+=128; } else { $t+=32768; }; } # print STDERR "nn $t\n"; $s.=" $_ = $t"; } elsif (/^nocase/) { } else { #dont check src and dst # if ($_ ne "msg0" and $_ ne "_src" and $_ ne "_dst" ) { $s.=" $_ = $rule{$_}"; }; if ($_ ne "msg0") { $s="$s $_ = $rule{$_}"; } } }; # print STDERR "md5 : $s\n"; my $sum=md5_hex("$s"); if ($frules{$sum}) { my $a=""; my $b=""; print STDERR "!! Duplicate ***********\n"; if (/msg:\s*"([^"]+)"/) { $a=$1;} if ($frules{$sum} =~ /msg:\s*"([^"]+)"/) { $b=$1;} if ( $a and $b ) { if ($a ne $b) { print STDERR '!! [',$a,"]\n!! [",$b,"]\n"; if (($a =~/IDS\d+/ and $b!~/IDS\d+/) or ($a =~/IDS\d+\s*-/ and $b=~/IDS\d+\s*\//) or (length($a)>length($b)) ) { $frules{$sum}=~s/\Q\"$b\"/\"$a\"/; print STDERR "!! Selecting [$a]\n"; } } } print STDERR "%% ",$_,"\n"; print STDERR "** ",$frules{$sum},"\n"; $nbld++; } else { my $ok="1"; my $str=qq/alert /.uc($rule{"_pro"}).qq/ $rule{"_src"} $rule{"_sp"} $rule{"_dir"} $rule{"_dst"} $rule{"_dp"} (/; if ($rule{"msg0"}) { $str.=qq/msg:$rule{"msg0"};/; } if ($rule{"logto0"}) { $str.=qq/ logto:$rule{"logto0"};/; } while (($key, $value) = each %rule) { if ($key=~ /^ttl/) { $str.=qq/ ttl:$value;/; } elsif ($key=~ /^id/) { $str.=qq/ id:$value;/; } elsif ($key=~ /^dsize/) { $str.=qq/ dsize:$value;/; } elsif ($key=~ /^content(\d+)/) { $str.=qq/ content:$value;/; if ($rule{"nocase$1"}) { $str.=qq/ nocase;/; } if ($rule{"offset$1"}) { $str.=qq/ offset:$rule{"offset$1"};/; } if ($rule{"depth$1"}) { $str.=qq/ depth:$rule{"depth$1"};/; } } elsif ($key=~ /^flags/) { $str.=qq/ flags:$value;/; } elsif ($key=~ /^seq/) { $str.=qq/ seq:$value;/; } elsif ($key=~ /^ack/) { $str.=qq/ ack:$value;/; } elsif ($key=~ /^itype/) { $str.=qq/ itype:$value;/; } elsif ($key=~ /^icode/) { $str.=qq/ icode:$value;/; } elsif ($key=~ /^session/) { $str.=qq/ session:$value;/; } elsif ($key=~ /^icmp_id/) { $str.=qq/ icmp_id:$value;/; } elsif ($key=~ /^icmp_seq/) { $str.=qq/ icmp_seq:$value;/; } elsif ($key=~ /^ipoption/) { $str.=qq/ ipoption:$value;/; } elsif ($key=~ /^rpc/) { $str.=qq/ rpc:$value;/; } elsif ($key=~ /^resp/) { $str.=qq/ resp:$value;/; } elsif ($key=~ /^ipopts/) { $str.=qq/ ipopts:$value;/; } else { $key=~ s/([^\d]+)\d*$/$1/ ; if ($key !~ /_pro|_src|_sp|_dir|_dst|_dp|msg|logto|nocase|offset|depth/){ $unknown{$key}=$value; $ok=""; } } } if ($ok) { $str.=')'; $frules{$sum}=qq/$str/; print STDERR "%% $_\n"; print STDERR "** $str\n"; $nblv++; } else { print STDERR "!! $_\n"; print STDERR "!! Unknown elements, ignoring line....\n"; $nbli++; } }; } elsif (/^var/ or /^preprocessor/ or /^output/ or /^log/ or /^pass/ ) { $head{$nh++}=$_; $nbls++; } else { print STDERR '@@ ',$_,"\n"; $nbli++; # print $_,"\n"; } } else { $nbli++; } } $nh=0; $nbl=0; $nblv=0; $nbld=0; $nbli=0; $nbls=0; while (<>) { traiter_ligne; $nbl++; } print STDERR "+"x132, "\n\n"; while (($key, $value) = each %head) { print "# $value\n"; } $key=""; print STDERR "Processed lines:\r\t\t\t$nbl\nAlert rules:\r\t\t\t$nblv\nDuplicated rules:\r\t\t\t$nbld\nIgnored lines:\r\t\t\t$nb li\n"; print STDERR "Other lines:\r\t\t\t$nbls\n"; print STDERR "+"x132, "\n\n"; while (($key, $value) = each %unknown) { print STDERR "UNKNOW: $key $value\n"; } print "\n"; while (($key, $value) = each %frules) { print "$value\n"; }