#!/usr/local/bin/perl # =head1 NAME sniff-snort - Convert Snort intrusion detection rules for Zeus Web Server =head1 SYNOPSIS B I =head1 DESCRIPTION B reads in rules taken from the B intrusion detection system (I). B supplies a list of attack signatures for known web server and web application attacks. B converts these into a format that can be used with the Zeus Web Server. To use this program, you must first download a set of snort rules signatures from I. These are supplied as a tar file which should be unpacked. Then, run B, supplying it with the path to the snort rules. The converted rules are outputted to stdout. These can then be added to the Zeus Web Server. To import the rules, visit the Protection pages on the Zeus Administration Server gloal settings. Click on the 'Add Multiple Rules' button, and supply the file that was generated by B. The rules will then be added to the system. It is suggested that before adding rules imported this way, they are run in 'testing' mode first, to ensure that they do not affect the access of ordinary users. This is important because B rules can sometimes be over-broad in their scope. =head1 EXAMPLES $ gzip -c -d snortrules.tar.gz | tar xvf - $ ./sniff-snort snortrules > zeus-rules.txt The commands above will unpack an archive of B signatures that have previously been downloaded, then run B on them. The converted rules are then saved to the file I. =head1 AUTHOR Ben Mansell =head1 COPYRIGHT sniff-snort copyright (c) 2002 Zeus Technology Ltd. All rights reserved. Snort (an intrusion detection tool) was written by Marty Roesch and is available from http://www.snort.org/, under the GPL. =head1 DISCLAIMER This information is furnished on an 'as is' basis. Zeus Technology makes no warranties of any kind, either expressed or implied as to any matter including, but not limited to, warranty of fitness for a particular purpose, exclusivity or results obtained from use of the material. Zeus Technology does not make any warranty of any kind with respect to freedom from patent, trademark, or copyright infringement. For the avoidance of doubt this software is not covered by any Zeus Technology Support and/or maintenance agreement. =cut # Sniff through all the snort rules and make some Zeus-style rules # # $1 should be set to where the snort rules have been unpacked use strict; die( "Usage: sniff-snort \n" ) unless( @ARGV ); # convert a wildcard string into a regex sub unwildcard($) { $_ = shift; s/\./\\\./g; s/\*/.*/g; s/\?/\.\?/g; return $_; } my $rules = shift; $rules .= "/rules" if( -f "$rules/rules/web-misc.rules" ); die( "Can't find rules\n" ) unless( -f "$rules/web-misc.rules" ); # Load in all appropriate rules # # Snort classifies rules as either attacks or 'activities' - which may or # may not be dodgy. We'll just go for the attacks, but you can change that # if you want by adjusting the regex below my $match_type = "web-application-attack"; foreach my $file ( glob( "$rules/web-*.rules" )) { open( RULES, "<$file" ) or die( "Can't read file $file: $!\n" ); while( ) { next if( /^\#/ ); next unless( /$match_type/ ); my( $line ) = /\((.*)\)/; my @content = (); my @uricontent = (); my $reference = ""; my $description = ""; my $wildcarded = 0; foreach my $token ( split( /;\s+/, $line )) { if( $token =~ /uricontent:\s*\"(.*)\"/ ) { push( @uricontent, $1 ); } elsif( $token =~ /content:\s*\"(.*)\"/ ) { push( @content, $1 ); } elsif( $token =~ /reference:\s*(.*)/ ) { $reference .= ", " if( $reference ne "" ); $reference .= "$1"; } elsif( $token =~ /msg:\"(.*)\"/ ) { $description = $1; } elsif( $token =~ /regex/ ) { $wildcarded = 1; } } $description ||= "Unknown"; # If wildcarded, convert to a regex if( $wildcarded ) { @content = map { unwildcard( $_ ) } @content; @uricontent = map { unwildcard( $_ ) } @uricontent; } # Print comment for rule my $comment = "# $description "; $comment .= "(Ref: $reference) " if( $reference ); my $type; my $match; if( $wildcarded || ( @content && @uricontent )) { # Regex, $uricontent .* $content my $content = ""; my $uricontent = ""; foreach $_ ( @content ) { $content .= ".*" unless( $content ); $content .= quotemeta( $_ ); } foreach $_ ( @uricontent ) { $uricontent .= ".*" unless( $uricontent ); $uricontent .= quotemeta( $_ ); } $type = "LimitIfURLMatches"; $match = "$uricontent.*$content"; } else { # Doesn't seem to be any real difference between $content # and $uricontent @content = @uricontent if( @uricontent ); if( @content == 1 ) { # Simple search $type = "LimitIfURLContains"; $match = $content[0]; } else { my $content = ""; foreach $_ ( @content ) { $content .= ".*" unless( $content ); $content .= quotemeta( $_ ); } $type = "LimitIfURLMatches"; $match = $content; } } # Get rid of whitespace at the end $match =~ s/(\s+)$//; # Catch dumb rules next if( $match eq "INDEX" ); print "$comment\n$type $match\n\n"; } close( RULES ); }