]>
git.ipfire.org Git - people/stevee/guardian.git/blob - modules/Parser.pm
1 package Guardian
::Parser
;
5 use Exporter
qw(import);
7 our @EXPORT_OK = qw(IsSupportedParser Parser);
9 # This hash contains all supported parsers and which function
10 # has to be called to parse messages in the right way.
11 my %logfile_parsers = (
12 "httpd" => \
&message_parser_httpd
,
13 "owncloud" => \
&message_parser_owncloud
,
14 "snort" => \
&message_parser_snort
,
15 "ssh" => \
&message_parser_ssh
,
19 ## The main parsing function.
21 ## It is used to determine which sub-parser has to be used to
22 ## parse the given message in the right way and to return if
23 ## any action should be performed.
26 my ($parser, @message) = @_;
28 # If no responsible message parser could be found, just return nothing.
29 unless (exists($logfile_parsers{$parser})) {
33 # Call responsible message parser.
34 my @actions = $logfile_parsers{$parser}->(@message);
36 # In case an action has been returned, return it too.
38 # Return which actions should be performed.
42 # Return undef, if no actions are required.
47 ## IsSupportedParser function.
49 ## This very tiny function checks if a given parser name is available and
50 ## therefore a supported parser.
52 ## To perform these check, the function is going to lookup if a key in the
53 ## hash of supported parsers is available
55 sub IsSupportedParser
($) {
58 # Check if a key for the given parser exists in the hash of logfile_parsers.
59 if(exists($logfile_parsers{$parser})) {
60 # Found a valid parser, so return nothing.
64 # Return "False" if we got here, and therefore no parser
70 ## The Snort message parser.
72 ## This subfunction is responsible for parsing sort alerts and determine if
73 ## an action should be performed.
75 ## XXX Currently the parser only supports IPv4. Add support for IPv6 at a
78 sub message_parser_snort
(@
) {
82 # Temporary array to store single alerts.
85 # The name of the parser module.
88 # Default returned message in case no one could be grabbed
89 # from the snort alert.
90 my $message = "An active snort rule has matched and gained an alert.";
92 # Snort uses a log buffer and a result of this, when detecting multiple
93 # events at once, multiple alerts will be written at one time to the alert
94 # file. They have to be seperated from each, to be able to parse them
96 foreach my $line (@message) {
97 # Remove any newlines.
100 # A single alert contains multiple lines, push all of them
104 # Each alert ends with an empty line, if one is found,
105 # all lines of the current processed alert have been found
106 # and pushed to the temporary array.
107 if($line =~ /^\s*$/) {
108 # Variable to store the grabbed IP-address.
111 # Loop through all lines of the current alert.
112 foreach my $line (@alert) {
113 # Check Priority Level and skip the alert if it is to low.
114 #if ($line =~ /.*\[Priority: (\d+)\].*/) {
115 # return unless($1 < $priority);
118 # Search for a line like xxx.xxx.xxx.xxx -> xxx.xxx.xxx.xxx
119 if ($line =~ /(\d+\.\d+\.\d+\.\d+)+ -\> (\d+\.\d+\.\d+\.\d+)+/) {
120 # Store the grabbed IP-address.
124 # Search for a line like xxx.xxx.xxx.xxx:xxx -> xxx.xxx.xxx.xxx:xxx
125 elsif ($line =~ /(\d+\.\d+\.\d+\.\d+):\d+ -\> (\d+\.\d+\.\d+\.\d+):\d+/) {
126 # Store the obtained IP-address.
130 # Obtain the reported reason from the headline of the alert.
131 if ($line =~ /.*\] (.*) \[\*\*\]/) {
132 # Store the extracted message.
136 # If the reason could not be determined, try to obtain it from a msg field.
137 elsif ($line =~ /.*msg:\"(.*)\".*/) {
138 # Store the extracted message.
143 # Check if at least the IP-address information has been extracted.
144 if (defined ($address)) {
145 # Add the extracted values and event message for the computed
146 # event to the actions array.
147 push(@actions, "count $address $name $message");
150 # The alert has been processed, clear the temporary array for storing
156 # If any actions are required, return the array.
161 # If we got here, the alert could not be parsed correctly, or did not match any filter.
162 # Therefore it can be skipped - return nothing.
167 ## The SSH message parser.
169 ## This subfunction is used for parsing and detecting different attacks
170 ## against the SSH service.
172 sub message_parser_ssh
(@
) {
176 # The name of the parser module.
179 # Variable to store the grabbed IP-address.
182 # Variable to store the parsed event.
185 # Loop through all lines, in case multiple one have
187 foreach my $line (@message) {
188 # Check for failed password attempts.
189 if ($line =~/.*sshd.*Failed password for (.*) from (.*) port.*/) {
190 # Store the grabbed IP-address.
194 $message = "Possible SSH-Bruteforce Attack for user: $1.";
197 # This should catch Bruteforce Attacks with enabled preauth
198 elsif ($line =~ /.*sshd.*Received disconnect from (.*):.*\[preauth\]/) {
199 # Store obtained IP-address.
203 $message = "Possible SSH-Bruteforce Attack - failed preauth.";
206 # Check if at least the IP-address information has been extracted.
207 if (defined ($address)) {
208 # Add the extracted values and event message for the computed
209 # event to the actions array.
210 push(@actions, "count $address $name $message");
214 # If any actions are required, return the array.
219 # If we got here, the provided message is not affected by any filter and
220 # therefore can be skipped. Return nothing (False) in this case.
225 ## The HTTPD message parser.
227 ## This subfunction is used for parsing and detecting different attacks
228 ## against a running HTTPD service.
230 sub message_parser_httpd
(@
) {
234 # The name of the parser module.
237 # Variable to store the grabbed IP-address.
240 # Variable to store the parsed event.
243 # Loop through all lines, in case multiple one have
245 foreach my $line (@message) {
246 # This will catch brute-force attacks against htaccess logins (username).
247 if ($line =~ /.*\[error\] \[client (.*)\] user(.*) not found:.*/) {
248 # Store the grabbed IP-address.
252 $message = "Possible WUI brute-force attack, wrong user: $2.";
255 # Detect htaccess password brute-forcing against a username.
256 elsif ($line =~ /.*\[error\] \[client (.*)\] user(.*): authentication failure for.*/) {
257 # Store the extracted IP-address.
261 $message = "Possible WUI brute-force attack, wrong password for user: $2.";
264 # Check if at least the IP-address information has been extracted.
265 if (defined ($address)) {
266 # Add the extracted values and event message to the actions array.
267 push(@actions, "count $address $name $message");
271 # If any actions are required, return the array.
276 # If we got here, the provided message is not affected by any filter and
277 # therefore can be skipped. Return nothing (False) in this case.
282 ## The Owncloud message parser.
284 ## This subfunction is used for parsing and detecting brute-force login
285 ## attempts against a local running owncloud instance.
287 sub message_parser_owncloud
(@
) {
291 # The name of the parser module.
292 my $name = "Owncloud";
294 # Variable to store the grabbed IP-address.
297 # Variable to store the parsed event.
300 # Loop through all lines, in case multiple one have
302 foreach my $line (@message) {
303 # This will catch brute-force attacks against the login (username).
304 if ($line =~/.*\"Login failed: \'(.*)\' \(Remote IP: \'(.*)\'\,.*/) {
305 # Store the grabbed user name.
308 # Store the grabbed IP-address.
312 $message = "Possible brute-force attack, wrong password for user: $user.";
315 # Check if at least the IP-address information has been extracted.
316 if (defined ($address)) {
317 # Add the extracted values and event message to the actions array.
318 push(@actions, "count $address $name $message");
322 # If any actions are required, return the array.
327 # If we got here, the provided message is not affected by any filter and
328 # therefore can be skipped. Return nothing (False) in this case.