Improve snort parser.
authorStefan Schantl <stefan.schantl@ipfire.org>
Thu, 14 Jul 2016 12:21:44 +0000 (14:21 +0200)
committerStefan Schantl <stefan.schantl@ipfire.org>
Thu, 14 Jul 2016 12:21:44 +0000 (14:21 +0200)
The snort parser now can handle multiple alerts at once.

Snort uses a log buffer to write to it's alert file. As a result of this,
when detecting multiple events at a very short time, multiple alerts will
be written at once to the alert file.

The single alerts now will be seperated and parsed individually. The attackers
IP-address (currently only IPv4) and the reported reason will be obtained from
the alert and stored to be returned to the main parser function.

Signed-off-by: Stefan Schantl <stefan.schantl@ipfire.org>
modules/Parser.pm

index 255e6cc..e50cef7 100644 (file)
@@ -76,52 +76,89 @@ sub IsSupportedParser ($) {
 #
 sub message_parser_snort(@) {
        my @message = @_;
+       my @actions;
+
+       # Temporary array to store single alerts.
+       my @alert;
 
        # The name of the parser module.
        my $name = "SNORT";
 
-       # Variable to store the grabbed IP-address.
-       my $address;
-
        # Default returned message in case no one could be grabbed
        # from the snort alert.
        my $message = "An active snort rule has matched and gained an alert.";
 
-       # A snort alert contains multiple lines, loop through all lines
-       # to parse the complete alert.
+       # Snort uses a log buffer and a result of this, when detecting multiple
+       # events at once, multiple alerts will be written at one time to the alert
+       # file. They have to be seperated from each, to be able to parse them
+       # individually.
        foreach my $line (@message) {
-               # Check Priority Level and skip the alert if it is to low.
-               #if ($line =~ /.*\[Priority: (\d+)\].*/) {
-               #       return unless($1 < $priority);
-               #}
-
-               # Search for a line like xxx.xxx.xxx.xxx -> xxx.xxx.xxx.xxx
-               if ($line =~ /(\d+\.\d+\.\d+\.\d+)+ -\> (\d+\.\d+\.\d+\.\d+)+/) {
-                       # Store the grabbed IP-address.
-                       $address = $1;
-               }
-
-               # Search for a line like xxx.xxx.xxx.xxx:xxx -> xxx.xxx.xxx.xxx:xxx
-               elsif ($line =~ /(\d+\.\d+\.\d+\.\d+):\d+ -\> (\d+\.\d+\.\d+\.\d+):\d+/) {
-                       # Store the obtained IP-address.
-                       $address = $1;
-               }
-
-               # Obtain the reported reason.
-               if ($line =~ /.*msg:\"(.*)\".*/) {
-                       # Store the extracted message.
-                       $message = $1;
+               # Remove any newlines.
+               chomp($line);
+
+               # A single alert contains multiple lines, push all of them
+               # a temporary array.
+               push(@alert, $line);
+
+               # Each alert ends with an empty line, if one is found,
+               # all lines of the current processed alert have been found
+               # and pushed to the temporary array.
+               if($line =~ /^\s*$/) {
+                       # Variable to store the grabbed IP-address.
+                       my $address;
+
+                       # Loop through all lines of the current alert.
+                       foreach my $line (@alert) {
+                               # Check Priority Level and skip the alert if it is to low.
+                               #if ($line =~ /.*\[Priority: (\d+)\].*/) {
+                               #       return unless($1 < $priority);
+                               #}
+
+                               # Search for a line like xxx.xxx.xxx.xxx -> xxx.xxx.xxx.xxx
+                               if ($line =~ /(\d+\.\d+\.\d+\.\d+)+ -\> (\d+\.\d+\.\d+\.\d+)+/) {
+                                       # Store the grabbed IP-address.
+                                       $address = $1;
+                               }
+
+                               # Search for a line like xxx.xxx.xxx.xxx:xxx -> xxx.xxx.xxx.xxx:xxx
+                               elsif ($line =~ /(\d+\.\d+\.\d+\.\d+):\d+ -\> (\d+\.\d+\.\d+\.\d+):\d+/) {
+                                       # Store the obtained IP-address.
+                                       $address = $1;
+                               }
+
+                               # Obtain the reported reason from the headline of the alert.
+                               if ($line =~ /.*\] (.*) \[\*\*\]/) {
+                                       # Store the extracted message.
+                                       $message = $1;
+                               }
+
+                               # If the reason could not be determined, try to obtain it from a msg field.
+                               elsif ($line =~ /.*msg:\"(.*)\".*/) {
+                                       # Store the extracted message.
+                                       $message = $1;
+                               }
+                       }
+
+                       # Check if at least the IP-address information has been extracted.
+                       if (defined ($address)) {
+                               # Add the extracted values and event message for the computed
+                               # event to the actions array.
+                               push(@actions, "count $address $name $message");
+                       }
+
+                       # The alert has been processed, clear the temporary array for storing
+                       # the next alert.
+                       @alert = ();
                }
        }
 
-       # Check if at least the IP-address information are obtained from the
-       # provided alert.
-       if ($address) {
-               # Return the extracted values.
-               return "$address $name $message";
+       # If any actions are required, return the array.
+       if (@actions) {
+               return (@actions);
        }
 
-       # If we got here, the alert could not be parsed correctly, return nothing.
+       # If we got here, the alert could not be parsed correctly, or did not match any filter.
+       # Therefore it can be skipped - return nothing.
        return;
 }