snort-rule 1.4.1 → 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -0
- data/lib/snort/rule.rb +28 -1
- data/lib/snort/rule/version.rb +1 -1
- data/lib/snort/ruleset.rb +165 -0
- data/test/helper.rb +1 -0
- data/test/test_snort-community-rules.rb +60 -10
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b475f0c004d1ff722c739e102fc44b8de4ae57b0
|
4
|
+
data.tar.gz: 16ceb5394563f2d3c6cbde7ed83c7e5a733cfbaf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3d74558797835687eb71aa1cb662a89f3d634e063c977bfc9829fc1a28e378e6060b13b281de97069decfe3632e427d2be215d65e3694899d103817fbe70cde
|
7
|
+
data.tar.gz: e9a47d83b028989f5cb91900acad0fb10f162984b9e44fd6c5f3139885719cf358b53e4a97b0a993937e6314944d790bd362576f01d963e794b776e7154cc322
|
data/README.md
CHANGED
@@ -47,6 +47,48 @@ Or install it yourself as:
|
|
47
47
|
rule = Snort::Rule.parse("pass udp 192.168.0.1 any <> any 53 ( sid:48; threshold:type limit,track by_src,count 1,seconds 3600; ref:ref1; ref:ref2;)")
|
48
48
|
rule.to_s # => "pass udp 192.168.0.1 any <> any 53 (sid:48; threshold:type limit,track by_src,count 1,seconds 3600; ref:ref1; ref:ref2;)"
|
49
49
|
|
50
|
+
## Snort::RuleSet Usage
|
51
|
+
|
52
|
+
require 'snort/ruleset'
|
53
|
+
ruleset = Snort::RuleSet::from_file("community.rules")
|
54
|
+
ruleset.length # => 3127
|
55
|
+
rules.length
|
56
|
+
rules.count{|r| ! r.enabled} # => 2522
|
57
|
+
rules.count{|r| r.enabled} # => 605
|
58
|
+
rules.disable_all
|
59
|
+
rules.count{|r| r.enabled} # => 0
|
60
|
+
rules.count{|r| ! r.enabled} # => 3127
|
61
|
+
rules.enable_all
|
62
|
+
rules.count{|r| r.enabled} # => 3127
|
63
|
+
rules.count{|r| ! r.enabled} # => 0
|
64
|
+
rules.disable do |r|
|
65
|
+
r.get_option_first("msg").match(/^"MALWARE\-CNC/)
|
66
|
+
end
|
67
|
+
rules.count{|r| ! r.enabled} # => 392
|
68
|
+
rules.count{|r| r.enabled} # => 2735
|
69
|
+
rules.delete do |r|
|
70
|
+
options = r.get_options("content")
|
71
|
+
if options
|
72
|
+
options.find { |o|
|
73
|
+
o.arguments.find { |a|
|
74
|
+
a.match(/"POST"/)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
else
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
rules.count{|r| ! r.enabled} # => 343
|
82
|
+
rules.count{|r| r.enabled} # 2726
|
83
|
+
rules.delete_all
|
84
|
+
rules.length # => 0
|
85
|
+
rules.count{|r| r.enabled} # => 0
|
86
|
+
rules.count{|r| ! r.enabled} # => 0
|
87
|
+
|
88
|
+
ruleset = Snort::RuleSet::from_file("community.rules")
|
89
|
+
ruleset.to_file("new_community.rules")
|
90
|
+
|
91
|
+
|
50
92
|
## Contributing
|
51
93
|
|
52
94
|
1. Fork it
|
data/lib/snort/rule.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require "snort/rule/version"
|
2
2
|
require "snort/rule/option"
|
3
|
-
require 'pp'
|
4
3
|
# Generates and parses snort rules
|
5
4
|
#
|
6
5
|
# Authors:: Chris Lee (mailto:rubygems@chrislee.dhs.org),
|
@@ -10,6 +9,22 @@ require 'pp'
|
|
10
9
|
# Copyright:: Copyright (c) 2011 Chris Lee
|
11
10
|
# License:: Distributes under the same terms as Ruby
|
12
11
|
module Snort
|
12
|
+
|
13
|
+
class Comment
|
14
|
+
def initialize(comment)
|
15
|
+
@comment = comment
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
@comment
|
20
|
+
end
|
21
|
+
|
22
|
+
def enable
|
23
|
+
end
|
24
|
+
|
25
|
+
def disable
|
26
|
+
end
|
27
|
+
end
|
13
28
|
|
14
29
|
# This class stores and generates the features of a snort rule
|
15
30
|
class Rule
|
@@ -64,6 +79,14 @@ module Snort
|
|
64
79
|
rule
|
65
80
|
end
|
66
81
|
|
82
|
+
def enable
|
83
|
+
@enabled = true
|
84
|
+
end
|
85
|
+
|
86
|
+
def disable
|
87
|
+
@enabled = false
|
88
|
+
end
|
89
|
+
|
67
90
|
def add_option(option)
|
68
91
|
if option.class == Array
|
69
92
|
option = Snort::RuleOption.new(option[0], option[1,100])
|
@@ -97,6 +120,10 @@ module Snort
|
|
97
120
|
end
|
98
121
|
nil
|
99
122
|
end
|
123
|
+
|
124
|
+
def get_options(option_name)
|
125
|
+
@options_hash[option_name]
|
126
|
+
end
|
100
127
|
|
101
128
|
def get_option_first(option_name)
|
102
129
|
if @options_hash[option_name]
|
data/lib/snort/rule/version.rb
CHANGED
@@ -0,0 +1,165 @@
|
|
1
|
+
require "snort/rule/version"
|
2
|
+
require "open-uri"
|
3
|
+
|
4
|
+
# Generates and parses snort rules
|
5
|
+
#
|
6
|
+
# Authors:: Chris Lee (mailto:rubygems@chrislee.dhs.org),
|
7
|
+
# Will Green (will[ at ]hotgazpacho[ dot ]org),
|
8
|
+
# Justin Knox (jknox[ at ]indexzero[ dot ]org)
|
9
|
+
# Ryan Barnett (rbarnett[ at ]modsecurity[ dot ]org)
|
10
|
+
# Copyright:: Copyright (c) 2011 Chris Lee
|
11
|
+
# License:: Distributes under the same terms as Ruby
|
12
|
+
module Snort
|
13
|
+
# This class stores a set of rules and allows actions against them
|
14
|
+
class RuleSet
|
15
|
+
|
16
|
+
def RuleSet::from_file(file)
|
17
|
+
if file.class == File
|
18
|
+
fh = file
|
19
|
+
else
|
20
|
+
fh = open(file.to_s, 'r')
|
21
|
+
end
|
22
|
+
RuleSet::from_filehandle(fh)
|
23
|
+
end
|
24
|
+
|
25
|
+
def RuleSet::from_url(url)
|
26
|
+
RuleSet::from_file(url)
|
27
|
+
end
|
28
|
+
|
29
|
+
def RuleSet::from_filehandle(fh)
|
30
|
+
rules = RuleSet.new
|
31
|
+
fh.each_line do |line|
|
32
|
+
if line =~ /(alert|drop|pass|reject|activate|dynamic|activate|sdrop)/
|
33
|
+
begin
|
34
|
+
rule = Snort::Rule.parse(line)
|
35
|
+
if rule
|
36
|
+
rules << rule
|
37
|
+
else
|
38
|
+
rules << Snort::Comment.new(line.strip)
|
39
|
+
end
|
40
|
+
rescue ArgumentError => e
|
41
|
+
rescue NoMethodError => e
|
42
|
+
end
|
43
|
+
else
|
44
|
+
rules << Snort::Comment.new(line.strip)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
rules
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_filehandle(fh)
|
51
|
+
@ruleset.each do |rule|
|
52
|
+
fh.puts rule.to_s
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_file(file)
|
57
|
+
i_opened_it = false
|
58
|
+
if file.class == File
|
59
|
+
fh = file
|
60
|
+
else
|
61
|
+
i_opened_it = true
|
62
|
+
fh = open(file.to_s, 'w')
|
63
|
+
end
|
64
|
+
to_filehandle(fh)
|
65
|
+
if i_opened_it
|
66
|
+
fh.close
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def initialize(ruleset=[])
|
71
|
+
@ruleset = ruleset
|
72
|
+
end
|
73
|
+
|
74
|
+
def <<(rule)
|
75
|
+
@ruleset << rule
|
76
|
+
end
|
77
|
+
|
78
|
+
def -(rule)
|
79
|
+
@ruleset -= rule
|
80
|
+
end
|
81
|
+
|
82
|
+
def length
|
83
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.length
|
84
|
+
end
|
85
|
+
|
86
|
+
def count(&block)
|
87
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.count(&block)
|
88
|
+
end
|
89
|
+
|
90
|
+
def enable(&block)
|
91
|
+
count = 0
|
92
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.each do |rule|
|
93
|
+
if block.call(rule)
|
94
|
+
rule.enable
|
95
|
+
count += 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
count
|
99
|
+
end
|
100
|
+
|
101
|
+
def disable(&block)
|
102
|
+
count = 0
|
103
|
+
@ruleset.find_all {|r| r.class == Snort::Rule}.each do |rule|
|
104
|
+
if block.call(rule)
|
105
|
+
rule.disable
|
106
|
+
count += 1
|
107
|
+
end
|
108
|
+
end
|
109
|
+
count
|
110
|
+
end
|
111
|
+
|
112
|
+
def delete(&block)
|
113
|
+
len = @ruleset.length
|
114
|
+
@ruleset.each do |rule|
|
115
|
+
next if rule.class == Snort::Comment
|
116
|
+
if block.call(rule)
|
117
|
+
@ruleset -= [rule]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
len - @ruleset.length
|
121
|
+
end
|
122
|
+
|
123
|
+
def enable_all
|
124
|
+
enable do |r|
|
125
|
+
true
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def disable_all
|
130
|
+
disable do |r|
|
131
|
+
true
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def delete_all
|
136
|
+
delete do |r|
|
137
|
+
true
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def enable_by_name(name)
|
142
|
+
enable do |r|
|
143
|
+
if r.name =~ name
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def disable_by_name(name)
|
150
|
+
disable do |r|
|
151
|
+
if r.name =~ name
|
152
|
+
true
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def delete_by_name(name)
|
158
|
+
delete do |r|
|
159
|
+
if r.name =~ name
|
160
|
+
true
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
data/test/helper.rb
CHANGED
@@ -7,6 +7,7 @@ unless Kernel.respond_to?(:require_relative)
|
|
7
7
|
end
|
8
8
|
|
9
9
|
require_relative 'helper'
|
10
|
+
require 'pp'
|
10
11
|
|
11
12
|
class TestSnortCommunityRules < Minitest::Test
|
12
13
|
def setup
|
@@ -45,20 +46,69 @@ class TestSnortCommunityRules < Minitest::Test
|
|
45
46
|
end
|
46
47
|
|
47
48
|
def test_complete_rules_file
|
48
|
-
rules =
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
49
|
+
rules = Snort::RuleSet::from_file("test/community-rules/community.rules")
|
50
|
+
assert_equal 3127, rules.length
|
51
|
+
assert_equal 2522, rules.count{|r| ! r.enabled}
|
52
|
+
assert_equal 605, rules.count{|r| r.enabled}
|
53
|
+
rules.disable_all
|
54
|
+
assert_equal 0, rules.count{|r| r.enabled}
|
55
|
+
assert_equal 3127, rules.count{|r| ! r.enabled}
|
56
|
+
rules.enable_all
|
57
|
+
assert_equal 3127, rules.count{|r| r.enabled}
|
58
|
+
assert_equal 0, rules.count{|r| ! r.enabled}
|
59
|
+
rules.disable do |r|
|
60
|
+
r.get_option_first("msg").match(/^"MALWARE\-CNC/)
|
61
|
+
end
|
62
|
+
assert_equal 392, rules.count{|r| ! r.enabled}
|
63
|
+
assert_equal 2735, rules.count{|r| r.enabled}
|
64
|
+
rules.delete do |r|
|
65
|
+
options = r.get_options("content")
|
66
|
+
if options
|
67
|
+
options.find { |o|
|
68
|
+
o.arguments.find { |a|
|
69
|
+
a.match(/"POST"/)
|
70
|
+
}
|
71
|
+
}
|
72
|
+
else
|
73
|
+
nil
|
58
74
|
end
|
59
75
|
end
|
76
|
+
assert_equal 343, rules.count{|r| ! r.enabled}
|
77
|
+
assert_equal 2726, rules.count{|r| r.enabled}
|
78
|
+
rules.delete_all
|
79
|
+
assert_equal 0, rules.length
|
80
|
+
assert_equal 0, rules.count{|r| r.enabled}
|
81
|
+
assert_equal 0, rules.count{|r| ! r.enabled}
|
82
|
+
end
|
83
|
+
|
84
|
+
# def test_ruleset_load_from_url
|
85
|
+
# rules = Snort::RuleSet::from_file("http://test.com/community.rules")
|
86
|
+
# assert_equal 3127, rules.length
|
87
|
+
# assert_equal 2522, rules.count{|r| ! r.enabled}
|
88
|
+
# assert_equal 605, rules.count{|r| r.enabled}
|
89
|
+
# end
|
90
|
+
|
91
|
+
def test_writing_file
|
92
|
+
rules = Snort::RuleSet::from_file("test/community-rules/community.rules")
|
60
93
|
assert_equal 3127, rules.length
|
61
94
|
assert_equal 2522, rules.count{|r| ! r.enabled}
|
62
95
|
assert_equal 605, rules.count{|r| r.enabled}
|
96
|
+
rules.to_file("test/community-rules/community.rules.test")
|
97
|
+
assert File.exist?("test/community-rules/community.rules.test")
|
98
|
+
total = enabled = disabled = 0
|
99
|
+
open("test/community-rules/community.rules.test", 'r').each_line do |l|
|
100
|
+
if l =~ /^(#)?\s*(alert|drop|pass|reject|activate|dynamic|activate|sdrop)/
|
101
|
+
total += 1
|
102
|
+
if l =~ /^#/
|
103
|
+
disabled += 1
|
104
|
+
else
|
105
|
+
enabled += 1
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
assert_equal 3127, total
|
110
|
+
assert_equal 2522, disabled
|
111
|
+
assert_equal 605, enabled
|
63
112
|
end
|
113
|
+
|
64
114
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: snort-rule
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- chrislee35
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -84,6 +84,7 @@ files:
|
|
84
84
|
- lib/snort/rule.rb
|
85
85
|
- lib/snort/rule/option.rb
|
86
86
|
- lib/snort/rule/version.rb
|
87
|
+
- lib/snort/ruleset.rb
|
87
88
|
- snort-rule.gemspec
|
88
89
|
- test/helper.rb
|
89
90
|
- test/test_snort-community-rules.rb
|