| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package ThreatDetector::Dispatcher;
|
|
2
|
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
85802
|
use strict;
|
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
55
|
|
|
4
|
2
|
|
|
2
|
|
6
|
use warnings;
|
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
89
|
|
|
5
|
2
|
|
|
2
|
|
1449
|
use JSON;
|
|
|
2
|
|
|
|
|
25333
|
|
|
|
2
|
|
|
|
|
15
|
|
|
6
|
2
|
|
|
2
|
|
636
|
use File::Basename;
|
|
|
2
|
|
|
|
|
4
|
|
|
|
2
|
|
|
|
|
283
|
|
|
7
|
2
|
|
|
2
|
|
21
|
use Time::HiRes qw(gettimeofday);
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
23
|
|
|
8
|
|
|
|
|
|
|
|
|
9
|
2
|
|
|
2
|
|
1122
|
use ThreatDetector::Handlers::SQLInjection qw(handle_sql_injection);
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
127
|
|
|
10
|
2
|
|
|
2
|
|
1167
|
use ThreatDetector::Handlers::ClientError qw(handle_client_error);
|
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
200
|
|
|
11
|
2
|
|
|
2
|
|
1002
|
use ThreatDetector::Handlers::CommandInjection qw(handle_command_injection);
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
132
|
|
|
12
|
2
|
|
|
2
|
|
956
|
use ThreatDetector::Handlers::DirectoryTraversal qw(handle_directory_traversal);
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
125
|
|
|
13
|
2
|
|
|
2
|
|
1047
|
use ThreatDetector::Handlers::XSS qw(handle_xss);
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
128
|
|
|
14
|
2
|
|
|
2
|
|
1025
|
use ThreatDetector::Handlers::EncodedPayload qw(handle_encoded);
|
|
|
2
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
115
|
|
|
15
|
2
|
|
|
2
|
|
1099
|
use ThreatDetector::Handlers::BotFingerprint qw(handle_scanner);
|
|
|
2
|
|
|
|
|
7
|
|
|
|
2
|
|
|
|
|
247
|
|
|
16
|
2
|
|
|
2
|
|
869
|
use ThreatDetector::Handlers::MethodAbuse qw(handle_http_method);
|
|
|
2
|
|
|
|
|
6
|
|
|
|
2
|
|
|
|
|
742
|
|
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
our $VERSION = '0.04';
|
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
my %handlers = (
|
|
21
|
|
|
|
|
|
|
sql_injection => \&handle_sql_injection,
|
|
22
|
|
|
|
|
|
|
client_error => \&handle_client_error,
|
|
23
|
|
|
|
|
|
|
command_injection => \&handle_command_injection,
|
|
24
|
|
|
|
|
|
|
directory_traversal => \&handle_directory_traversal,
|
|
25
|
|
|
|
|
|
|
xss_attempt => \&handle_xss,
|
|
26
|
|
|
|
|
|
|
encoded_payload => \&handle_encoded,
|
|
27
|
|
|
|
|
|
|
scanner_fingerprint => \&handle_scanner,
|
|
28
|
|
|
|
|
|
|
http_method_abuse => \&handle_http_method,
|
|
29
|
|
|
|
|
|
|
# Will add a few more later
|
|
30
|
|
|
|
|
|
|
);
|
|
31
|
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
sub dispatch {
|
|
33
|
2
|
|
|
2
|
1
|
261913
|
my ($entry, @threats) =@_;
|
|
34
|
2
|
50
|
33
|
|
|
15
|
return unless $entry && @threats;
|
|
35
|
|
|
|
|
|
|
|
|
36
|
2
|
|
|
|
|
6
|
for my $threat (@threats) {
|
|
37
|
2
|
50
|
|
|
|
8
|
if (exists $handlers{$threat}) {
|
|
38
|
2
|
|
|
|
|
11
|
$handlers{$threat}->($entry);
|
|
39
|
|
|
|
|
|
|
} else {
|
|
40
|
0
|
|
|
|
|
|
warn "[Dispatcher] No handler for threat type: $threat\n";
|
|
41
|
|
|
|
|
|
|
}
|
|
42
|
|
|
|
|
|
|
}
|
|
43
|
|
|
|
|
|
|
}
|
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
1;
|
|
46
|
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
=head1 NAME
|
|
48
|
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
ThreatDetector::Dispatcher - Routes classified threats to their appropriate handler modules
|
|
50
|
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
=head1 SYNOPSIS
|
|
52
|
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
use ThreatDetector::Dispatcher;
|
|
54
|
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
ThreatDetector::Dispatcher::dispatch($entry, @threats);
|
|
56
|
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
This module dispatches structured Apache log entries (parsed and classified) to the appropriate threat handler based on their threat types. Each handler is responsible for processing or logging the alert in its own way (typically as JSON output).
|
|
60
|
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
The dispatch system uses a mapping of known threat types to handler subroutine references. If a threat type has no matching handler, a warning is printed.
|
|
62
|
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
=head1 FUNCTIONS
|
|
64
|
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=head2 dispatch($entry, @threats)
|
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
Given a parsed log entry (as a hashref) and a list of threat types (as strings), this function invokes the appropriate handler subroutine for each threat.
|
|
68
|
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Parameters:
|
|
70
|
|
|
|
|
|
|
$entry - A hashref representing the parsed log line.
|
|
71
|
|
|
|
|
|
|
@threats - A list of strings representing classified threat types.
|
|
72
|
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Example:
|
|
74
|
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
my $entry = ThreatDetector::Parser::parse_log_line($line);
|
|
76
|
|
|
|
|
|
|
my @threats = ThreatDetector::Classifier::classify($entry);
|
|
77
|
|
|
|
|
|
|
ThreatDetector::Dispatcher::dispatch($entry, @threats);
|
|
78
|
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=head1 SUPPORTED THREAT TYPES
|
|
80
|
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
=over 4
|
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
=item * sql_injection
|
|
84
|
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
=item * client_error
|
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item * command_injection
|
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
=item * directory_traversal
|
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
=item * xss_attempt
|
|
92
|
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
=item * encoded_payload
|
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
=item * scanner_fingerprint
|
|
96
|
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
=item * http_method_abuse
|
|
98
|
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=back
|
|
100
|
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=head1 AUTHOR
|
|
102
|
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
Jason Hall
|
|
104
|
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
=head1 LICENSE
|
|
106
|
|
|
|
|
|
|
|
|
107
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or modify
|
|
108
|
|
|
|
|
|
|
it under the same terms as Perl itself.
|
|
109
|
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
=cut
|