line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Parse::Snort; |
2
|
|
|
|
|
|
|
|
3
|
9
|
|
|
9
|
|
644732
|
use strict; |
|
9
|
|
|
|
|
86
|
|
|
9
|
|
|
|
|
280
|
|
4
|
9
|
|
|
9
|
|
53
|
use warnings; |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
334
|
|
5
|
9
|
|
|
9
|
|
55
|
use base qw(Class::Accessor); |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
4746
|
|
6
|
9
|
|
|
9
|
|
17773
|
use List::Util qw(first); |
|
9
|
|
|
|
|
24
|
|
|
9
|
|
|
|
|
936
|
|
7
|
9
|
|
|
9
|
|
67
|
use Carp qw(carp); |
|
9
|
|
|
|
|
20
|
|
|
9
|
|
|
|
|
14463
|
|
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
our $VERSION = '0.9'; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
=head1 NAME |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
Parse::Snort - Parse and create Snort rules |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
=head1 VERSION |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
Version 0.9 |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
=head1 SYNOPSIS |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
use Parse::Snort; |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
my $rule = Parse::Snort->new( |
24
|
|
|
|
|
|
|
action => 'alert', |
25
|
|
|
|
|
|
|
proto => 'tcp', |
26
|
|
|
|
|
|
|
src => '$HOME_NET', src_port => 'any', |
27
|
|
|
|
|
|
|
direction => '->' |
28
|
|
|
|
|
|
|
dst => '$EXTERNAL_NET', dst_port => 'any' |
29
|
|
|
|
|
|
|
); |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
$rule->action("pass"); |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
$rule->opts( |
34
|
|
|
|
|
|
|
[ 'depth' => 50 ], |
35
|
|
|
|
|
|
|
[ 'offset' => 0 ], |
36
|
|
|
|
|
|
|
[ 'content' => "perl6" ], |
37
|
|
|
|
|
|
|
[ "nocase" ] |
38
|
|
|
|
|
|
|
); |
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
my $rule = Parse::Snort->new(); |
41
|
|
|
|
|
|
|
$rule->parse('pass tcp $HOME_NET any -> $EXTERNAL_NET 6667;'); |
42
|
|
|
|
|
|
|
$rule->msg("IRC server"); |
43
|
|
|
|
|
|
|
my $rule_string = $rule->as_string; |
44
|
|
|
|
|
|
|
); |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=cut |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
our @RULE_ELEMENTS_REQUIRED = qw/ action proto src src_port direction dst dst_port /; |
49
|
|
|
|
|
|
|
our @RULE_ELEMENTS = ( @RULE_ELEMENTS_REQUIRED, 'opts' ); |
50
|
|
|
|
|
|
|
|
51
|
|
|
|
|
|
|
# create the accessors for the standard parts (note; opts comes later) |
52
|
|
|
|
|
|
|
__PACKAGE__->mk_accessors(@RULE_ELEMENTS_REQUIRED); |
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
=head1 METHODS |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
These are the object methods that can be used to read or modify any part of a Snort rule. B |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
If input validation is required, check out the L module. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
=head2 new () |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
Create a new C object, and return it. There are a couple of options when creating the object: |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
=over 4 |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
=item new ( ) |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
Create an unpopulated object, that can be filled in using the individual rule element methods, or can be populated with the L<< parse|Parse::Snort/"PARSE" >> method. |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
=item new ( $rule_string ) |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
Create an object based on a plain text Snort rule, all on one line. This module doesn't understand the UNIX style line continuations (a backslash at the end of the line) that Snort does. |
74
|
|
|
|
|
|
|
|
75
|
|
|
|
|
|
|
$rule_string = 'alert tcp $EXTERNAL_NET any -> $HOME_NET any (msg:"perl 6 download detected\; may the world rejoice!";depth:150; offset:0; content:"perl-6.0.0"; nocase;)' |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
=item new ( $rule_element_hashref ) |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
Create an object baesd on a prepared hash reference similar to the internal strucutre of the L object. |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
$rule_element_hashref = { |
83
|
|
|
|
|
|
|
action => 'alert', |
84
|
|
|
|
|
|
|
proto => 'tcp', |
85
|
|
|
|
|
|
|
src => '$EXTERNAL_NET', |
86
|
|
|
|
|
|
|
src_port => 'any', |
87
|
|
|
|
|
|
|
direction => '->', |
88
|
|
|
|
|
|
|
dst => '$HOME_NET', |
89
|
|
|
|
|
|
|
dst_port => 'any', |
90
|
|
|
|
|
|
|
opts => [ |
91
|
|
|
|
|
|
|
['msg' => '"perl 6 download detected\; may the world rejoice!"'], |
92
|
|
|
|
|
|
|
['depth' => 150], |
93
|
|
|
|
|
|
|
['offset' => 0], |
94
|
|
|
|
|
|
|
['content' => 'perl-6.0.0'], |
95
|
|
|
|
|
|
|
['nocase'], |
96
|
|
|
|
|
|
|
], |
97
|
|
|
|
|
|
|
}; |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=back |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=cut |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
sub new { |
104
|
14
|
|
|
14
|
1
|
2320
|
my ( $class, $data ) = @_; |
105
|
|
|
|
|
|
|
|
106
|
14
|
|
|
|
|
35
|
my $self = { |
107
|
|
|
|
|
|
|
}; |
108
|
|
|
|
|
|
|
|
109
|
14
|
|
|
|
|
36
|
bless $self, $class; |
110
|
14
|
|
|
|
|
53
|
$self->_init($data); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub _init { |
114
|
14
|
|
|
14
|
|
38
|
my ( $self, $data ) = @_; |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# were we passed a hashref? (formatted rule in hashref form) |
117
|
14
|
100
|
|
|
|
77
|
if ( ref($data) eq "HASH" ) { |
|
|
100
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# loop through the bits and set the values |
119
|
2
|
|
|
|
|
13
|
while ( my ( $method, $val ) = each %$data ) { |
120
|
16
|
|
|
|
|
254
|
$self->$method($val); |
121
|
|
|
|
|
|
|
} |
122
|
|
|
|
|
|
|
} elsif ( defined($data) ) { |
123
|
|
|
|
|
|
|
# otherwise, interpret this as a plain text rule. |
124
|
4
|
|
|
|
|
18
|
$self->parse($data); |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
# nothing |
127
|
14
|
|
|
|
|
120
|
return $self; |
128
|
|
|
|
|
|
|
} |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
=head2 parse( $rule_string ) |
131
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
The parse method is what interprets a plain text rule, and populates the rule object. Beacuse this module does not support the UNIX style line-continuations (backslash at the end of a line) the rule must be all on one line, otherwise the parse will fail in unpredictably interesting and confusing ways. The parse method tries to interpret the rule from left to right, calling the individual accessor methods for each rule element. This will overwrite the contents of the object (if any), so if you want to parse multiple rules at once, you will need multiple objects. |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
$rule->parse($rule_string); |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=cut |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub parse { |
139
|
8
|
|
|
8
|
1
|
101
|
my ( $self, $rule ) = @_; |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# nuke extra whitespace pre/post rule |
142
|
8
|
|
|
|
|
35
|
$rule =~ s/^\s+//; |
143
|
8
|
|
|
|
|
83
|
$rule =~ s/\s+$//; |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Rules are distributed without being enabled |
146
|
8
|
100
|
|
|
|
30
|
if ($rule =~ /^#/) { |
147
|
1
|
|
|
|
|
5
|
$rule =~ s/^#+\s*//g; |
148
|
1
|
|
|
|
|
3
|
$self->state(0); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
else { |
151
|
7
|
|
|
|
|
29
|
$self->state(1); |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# 20090823 RGH: m/\s+/ instead of m/ /; bug reported by Leon Ward |
155
|
8
|
|
|
|
|
75
|
my @values = split(m/\s+/, $rule, scalar @RULE_ELEMENTS); # no critic |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
# Support for 'Decoder and Preprocessor Rules' |
158
|
8
|
100
|
|
|
|
30
|
if ($values[1] eq '(') { |
159
|
1
|
|
|
|
|
3
|
$self->{preprocessed} = 1; |
160
|
1
|
|
|
|
|
7
|
$self->action($values[0]); |
161
|
1
|
|
|
|
|
23
|
shift @values; |
162
|
1
|
|
|
|
|
8
|
$self->opts(join(' ', @values)); |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
# Regular rules |
165
|
|
|
|
|
|
|
else { |
166
|
7
|
|
|
|
|
28
|
for my $i ( 0 .. $#values ) { |
167
|
56
|
|
|
|
|
575
|
my $meth = $RULE_ELEMENTS[$i]; |
168
|
56
|
|
|
|
|
178
|
$self->$meth( $values[$i] ); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
=head2 state |
174
|
|
|
|
|
|
|
|
175
|
|
|
|
|
|
|
The state of the rule: active (1) or commented (0) |
176
|
|
|
|
|
|
|
|
177
|
|
|
|
|
|
|
=cut |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub state { |
180
|
14
|
|
|
14
|
1
|
1272
|
my ($self, $state) = @_; |
181
|
|
|
|
|
|
|
|
182
|
14
|
100
|
|
|
|
42
|
if (defined $state) { |
183
|
9
|
|
|
|
|
61
|
$self->{state} = $state; |
184
|
|
|
|
|
|
|
} |
185
|
14
|
100
|
|
|
|
45
|
if (!defined $self->{state}) { |
186
|
2
|
|
|
|
|
37
|
return 1; |
187
|
|
|
|
|
|
|
} |
188
|
12
|
|
|
|
|
47
|
return $self->{state}; |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
=head2 METHODS FOR ACCESSING RULE ELEMENTS |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
You can access the core parts of a rule (action, protocol, source IP, etc) with the method of their name. These are read/write L accessors. If you want to read the value, don't pass an argument. If you want to set the value, pass in the new value. In either case it returns the current value, or undef if the value has not been set yet. |
194
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
=over 4 |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
=item action |
198
|
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
The rule action. Generally one of the following: C, C, C, C, or C. |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
=item proto |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
The protocol of the rule. Generally one of the following: C, C, C, or C. |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
=item src |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
The source IP address for the rule. Generally a dotted decimal IP address, Snort $HOME_NET variable, or CIDR block notation. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
=item src_port |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
The source port for the rule. Generally a static port, or a contigious range of ports. |
212
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
=item direction |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
The direction of the rule. One of the following: C<< -> >> C<< <> >> or C<< <- >>. |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
=item dst |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
The destination IP address for the rule. Same format as C |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
=item dst_port |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
The destination port for the rule. Same format as C |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
=item opts ( $opts_array_ref ) |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
=item opts ( $opts_string ) |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
The opts method can be used to read existing options of a parsed rule, or set them. The method takes two forms of arguments, either an Array of Arrays, or a rule string. |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
=over 4 |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=item $opts_array_ref |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
$opts_array_ref = [ |
236
|
|
|
|
|
|
|
[ 'msg' => '"perl 6 download detected\; may the world rejoice!"' ], |
237
|
|
|
|
|
|
|
[ 'depth' => 150 ], |
238
|
|
|
|
|
|
|
[ 'offset' => 0 ], |
239
|
|
|
|
|
|
|
[ 'content' => 'perl-6.0.0' ], |
240
|
|
|
|
|
|
|
[ 'nocase' ], |
241
|
|
|
|
|
|
|
] |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=item $opts_string |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
$opts_string='(msg:"perl 6 download detected\; may the world rejoice!";depth:150; offset:0; content:"perl-6.0.0"; nocase;)'; |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
The parenthesis surround the series of C pairs are optional. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=back |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
=cut |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
sub opts { |
254
|
27
|
|
|
27
|
1
|
16727
|
my ( $self, $args ) = @_; |
255
|
|
|
|
|
|
|
|
256
|
27
|
100
|
|
|
|
70
|
if ($args) { |
257
|
|
|
|
|
|
|
|
258
|
|
|
|
|
|
|
# setting |
259
|
17
|
100
|
|
|
|
84
|
if ( ref($args) eq "ARRAY" ) { |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
# list interface: |
262
|
|
|
|
|
|
|
# ([depth => 50], [offset => 0], [content => "perl6"], ["nocase"]) |
263
|
7
|
|
|
|
|
25
|
$self->set( 'opts', $args ); |
264
|
|
|
|
|
|
|
} else { |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# string interface |
267
|
|
|
|
|
|
|
# 'depth:50; offset:0; content:"perl\;6"; nocase;' |
268
|
10
|
100
|
|
|
|
52
|
if ( $args =~ m/^\(/ ) { |
269
|
|
|
|
|
|
|
# remove opts parens if they exist |
270
|
8
|
|
|
|
|
96
|
$args =~ s/^\((.+)\)$/$1/; |
271
|
|
|
|
|
|
|
} |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
# When I first wrote this regex I thought it was slick. |
274
|
|
|
|
|
|
|
# I still think that, but 2y after doing it the first time |
275
|
|
|
|
|
|
|
# it just hurt to look at. So, /x modifier we go! |
276
|
10
|
|
|
|
|
1939
|
my @set = map { [ split( m/\s*:\s*/, $_, 2 ) ] } $args =~ m/ |
|
174
|
|
|
|
|
646
|
|
277
|
|
|
|
|
|
|
\s* # ignore preceeding whitespace |
278
|
|
|
|
|
|
|
( # begin capturing |
279
|
|
|
|
|
|
|
(?: # grab characters we want |
280
|
|
|
|
|
|
|
\\. # skip over escapes |
281
|
|
|
|
|
|
|
| |
282
|
|
|
|
|
|
|
[^;] # or anything but a ; |
283
|
|
|
|
|
|
|
)+? # ? greedyness hack lets the \s* actually match |
284
|
|
|
|
|
|
|
) # end capturing |
285
|
|
|
|
|
|
|
\s* # ignore whitespace between value and ; or end of line |
286
|
|
|
|
|
|
|
(?: # stop anchor at ... |
287
|
|
|
|
|
|
|
; # semicolon |
288
|
|
|
|
|
|
|
| # or |
289
|
|
|
|
|
|
|
$ # end of line |
290
|
|
|
|
|
|
|
) |
291
|
|
|
|
|
|
|
\s*/gx; |
292
|
10
|
|
|
|
|
67
|
$self->set( 'opts', @set ); |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
} else { |
295
|
|
|
|
|
|
|
# getting |
296
|
10
|
|
|
|
|
29
|
return $self->get('opts'); |
297
|
|
|
|
|
|
|
} |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
sub _single_opt_accessor { |
301
|
72
|
|
|
72
|
|
129
|
my $opt = shift; |
302
|
|
|
|
|
|
|
return sub { |
303
|
47
|
|
|
47
|
|
19377
|
my ( $self, $val ) = @_; |
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
# find the (hopefully) pre-existing option in the opts AoA |
306
|
47
|
|
|
|
|
78
|
my $element; |
307
|
|
|
|
|
|
|
|
308
|
47
|
100
|
|
|
|
132
|
if ( defined $self->get('opts') ) { |
309
|
43
|
|
|
556
|
|
443
|
$element = first { $_->[0] eq $opt } @{ $self->get('opts') }; |
|
556
|
|
|
|
|
1081
|
|
|
43
|
|
|
|
|
113
|
|
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
47
|
100
|
|
|
|
219
|
if ( ref($element) ) { |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
# preexisting |
315
|
37
|
100
|
|
|
|
81
|
if ($val) { $element->[1] = $val; } |
|
10
|
|
|
|
|
48
|
|
316
|
27
|
|
|
|
|
126
|
else { return $element->[1]; } |
317
|
|
|
|
|
|
|
} else { |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
# doesn't exist |
320
|
10
|
100
|
|
|
|
22
|
if ($val) { |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
# setting |
323
|
8
|
100
|
|
|
|
19
|
if ( scalar $self->get('opts') ) { |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# other opts exist, tack it on the end |
326
|
|
|
|
|
|
|
$self->set( |
327
|
|
|
|
|
|
|
'opts', |
328
|
6
|
|
|
|
|
34
|
@{ $self->get('opts') }, |
|
6
|
|
|
|
|
11
|
|
329
|
|
|
|
|
|
|
[ $opt, $val ] |
330
|
|
|
|
|
|
|
); |
331
|
|
|
|
|
|
|
} else { |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
# blank slate, create the AoA |
334
|
2
|
|
|
|
|
22
|
$self->set( 'opts', [ [ $opt, $val ] ] ); |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
} else { |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
# getting |
339
|
2
|
|
|
|
|
12
|
return; |
340
|
|
|
|
|
|
|
} |
341
|
|
|
|
|
|
|
} |
342
|
|
|
|
|
|
|
} |
343
|
72
|
|
|
|
|
336
|
} |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# helper accessors that poke around inside rule options |
346
|
|
|
|
|
|
|
|
347
|
|
|
|
|
|
|
*sid = _single_opt_accessor('sid'); |
348
|
|
|
|
|
|
|
*rev = _single_opt_accessor('rev'); |
349
|
|
|
|
|
|
|
*msg = _single_opt_accessor('msg'); |
350
|
|
|
|
|
|
|
*classtype = _single_opt_accessor('classtype'); |
351
|
|
|
|
|
|
|
*gid = _single_opt_accessor('gid'); |
352
|
|
|
|
|
|
|
*metadata = _single_opt_accessor('metadata'); |
353
|
|
|
|
|
|
|
*priority = _single_opt_accessor('priority'); |
354
|
|
|
|
|
|
|
*flow = _single_opt_accessor('flow'); |
355
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
=back |
357
|
|
|
|
|
|
|
|
358
|
|
|
|
|
|
|
=head2 HELPER METHODS FOR VARIOUS OPTIONS |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=over 4 |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
=item sid |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
=item rev |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=item msg |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
=item classtype |
369
|
|
|
|
|
|
|
|
370
|
|
|
|
|
|
|
=item gid |
371
|
|
|
|
|
|
|
|
372
|
|
|
|
|
|
|
=item metadata |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=item flow |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=item priority |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
The these methods allow direct access to the rule option of the same name |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
my $sid = $rule_obj->sid(); # reads the sid of the rule |
381
|
|
|
|
|
|
|
$rule_obj->sid($sid); # sets the sid of the rule |
382
|
|
|
|
|
|
|
... etc ... |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=item references |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
The C method permits read-only access to the C options in the rule. This is in the form of an array of arrays, with each reference in the format |
387
|
|
|
|
|
|
|
|
388
|
|
|
|
|
|
|
[ 'reference_type' => 'reference_value' ] |
389
|
|
|
|
|
|
|
|
390
|
|
|
|
|
|
|
To modify references, use the C method to grab all the rule options, modify it to your needs, and use the C method to save your changes back to the rule object. |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
|
393
|
|
|
|
|
|
|
$references = $rule->references(); # just the references |
394
|
|
|
|
|
|
|
$no_references = grep { $_->[0] ne "reference" } @{ $rule->opts() }; # everything but the references |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=cut |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub references { |
399
|
2
|
|
|
2
|
1
|
7
|
my ($self) = shift; |
400
|
|
|
|
|
|
|
return [ |
401
|
26
|
|
|
|
|
86
|
map { [split(m/,/, $_->[1], 2)] } |
402
|
2
|
|
|
|
|
4
|
grep { $_->[0] eq "reference" } @{ $self->get('opts') } |
|
42
|
|
|
|
|
92
|
|
|
2
|
|
|
|
|
8
|
|
403
|
|
|
|
|
|
|
]; |
404
|
|
|
|
|
|
|
} |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
=item as_string |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
The C method returns a string that matches the normal Snort rule form of the object. This is what you want to use to write a rule to an output file that will be read by Snort. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
=cut |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
sub as_string { |
414
|
8
|
|
|
8
|
1
|
4547
|
my $self = shift; |
415
|
8
|
|
|
|
|
15
|
my $ret; |
416
|
|
|
|
|
|
|
my @missing; |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
# we may be incomplete |
419
|
8
|
100
|
|
|
|
20
|
@missing = grep { $_ } map { exists( $self->{$_} ) ? undef : $_ } @RULE_ELEMENTS_REQUIRED; |
|
56
|
|
|
|
|
83
|
|
|
56
|
|
|
|
|
114
|
|
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
# stitch together the required bits |
422
|
8
|
100
|
|
|
|
23
|
if (!scalar @missing) { |
423
|
|
|
|
|
|
|
$ret .= sprintf("%s %s %s %s %s %s %s", |
424
|
4
|
|
|
|
|
28
|
@$self{@RULE_ELEMENTS_REQUIRED}); |
425
|
|
|
|
|
|
|
} |
426
|
|
|
|
|
|
|
|
427
|
|
|
|
|
|
|
# tack on opts if they exist |
428
|
8
|
100
|
|
|
|
28
|
if (defined $self->get('opts')) { |
429
|
|
|
|
|
|
|
$ret .= sprintf( |
430
|
|
|
|
|
|
|
" (%s)", |
431
|
|
|
|
|
|
|
join(" ", |
432
|
90
|
100
|
|
|
|
318
|
map { defined($_->[1]) ? "$_->[0]:$_->[1];" : "$_->[0];" } |
433
|
6
|
|
|
|
|
44
|
@{ $self->get('opts') }) |
|
6
|
|
|
|
|
15
|
|
434
|
|
|
|
|
|
|
); |
435
|
|
|
|
|
|
|
} |
436
|
|
|
|
|
|
|
|
437
|
8
|
100
|
66
|
|
|
85
|
return undef if @missing && !$self->{preprocessed}; |
438
|
4
|
100
|
|
|
|
13
|
return $self->state ? $ret : "# $ret"; |
439
|
|
|
|
|
|
|
} |
440
|
|
|
|
|
|
|
|
441
|
|
|
|
|
|
|
=pod |
442
|
|
|
|
|
|
|
|
443
|
|
|
|
|
|
|
=item clone |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
Returns a clone of the current rule object. |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
=cut |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
# poor man's deep cloning. This will have to be maintained if the internal structure ever changes. |
450
|
|
|
|
|
|
|
sub clone { |
451
|
1
|
|
|
1
|
1
|
2
|
my $self = shift; |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# initial shallow copy |
454
|
1
|
|
|
|
|
8
|
my $copy = bless { %$self }, ref $self; |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
# deeper copy, for opts |
457
|
1
|
50
|
|
|
|
3
|
if ($self->opts()) { |
458
|
1
|
|
|
|
|
8
|
$copy->opts( [ map { [ @$_ ] } @{ $self->opts } ]); |
|
9
|
|
|
|
|
19
|
|
|
1
|
|
|
|
|
2
|
|
459
|
|
|
|
|
|
|
} |
460
|
1
|
|
|
|
|
7
|
return $copy; |
461
|
|
|
|
|
|
|
} |
462
|
|
|
|
|
|
|
|
463
|
|
|
|
|
|
|
=pod |
464
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
=item reset |
466
|
|
|
|
|
|
|
|
467
|
|
|
|
|
|
|
Resets the internal state (deletes it!) of the current rule object, and returns the rule object itself. Useful for parsing multiple rules, one after another. Just call C<< $rule->reset() >> after you're done with the current rule, and before you C<< $rule->parse() >> or set new values via the accessor methods. |
468
|
|
|
|
|
|
|
|
469
|
|
|
|
|
|
|
=back |
470
|
|
|
|
|
|
|
|
471
|
|
|
|
|
|
|
=cut |
472
|
|
|
|
|
|
|
|
473
|
|
|
|
|
|
|
sub reset { |
474
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
475
|
0
|
|
|
|
|
|
delete $self->{$_} for keys %$self; |
476
|
0
|
|
|
|
|
|
return $self; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
=head1 AUTHOR |
480
|
|
|
|
|
|
|
|
481
|
|
|
|
|
|
|
Richard G Harman Jr, C<< >> |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
=head1 BUGS |
484
|
|
|
|
|
|
|
|
485
|
|
|
|
|
|
|
Please report any bugs or feature requests to |
486
|
|
|
|
|
|
|
C, or through the web interface at |
487
|
|
|
|
|
|
|
L. |
488
|
|
|
|
|
|
|
I will be notified, and then you' ll automatically be notified of progress on your bug as I make changes. |
489
|
|
|
|
|
|
|
|
490
|
|
|
|
|
|
|
=head1 SUPPORT |
491
|
|
|
|
|
|
|
|
492
|
|
|
|
|
|
|
You can find documentation for this module with the perldoc command. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
perldoc Parse::Snort |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
You can also look for information at: |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
=over 4 |
499
|
|
|
|
|
|
|
|
500
|
|
|
|
|
|
|
=item * AnnoCPAN: Annotated CPAN documentation |
501
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
L |
503
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
=item * CPAN Ratings |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
L |
507
|
|
|
|
|
|
|
|
508
|
|
|
|
|
|
|
=item * RT: CPAN's request tracker |
509
|
|
|
|
|
|
|
|
510
|
|
|
|
|
|
|
L |
511
|
|
|
|
|
|
|
|
512
|
|
|
|
|
|
|
=item * Search CPAN |
513
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
L |
515
|
|
|
|
|
|
|
|
516
|
|
|
|
|
|
|
=back |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
=head1 DEPENDENCIES |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
L, L, L, L |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
=head1 ACKNOWLEDGEMENTS |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
MagNET #perl for putting up with me :) |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head1 COPYRIGHT & LICENSE |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Copyright 2007 Richard Harman, all rights reserved. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or modify it |
531
|
|
|
|
|
|
|
under the same terms as Perl itself. |
532
|
|
|
|
|
|
|
|
533
|
|
|
|
|
|
|
=cut |
534
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
!!'mtfnpy!!'; |